summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/.gitignore6
-rw-r--r--cmd/2048.c398
-rw-r--r--cmd/Kconfig3128
-rw-r--r--cmd/Makefile301
-rw-r--r--cmd/abootimg.c319
-rw-r--r--cmd/acpi.c216
-rw-r--r--cmd/adc.c166
-rw-r--r--cmd/addrmap.c34
-rw-r--r--cmd/adtimg.c244
-rw-r--r--cmd/aes.c344
-rw-r--r--cmd/arm/Makefile7
-rw-r--r--cmd/arm/exception.c59
-rw-r--r--cmd/arm/exception64.c87
-rw-r--r--cmd/armffa.c201
-rw-r--r--cmd/armflash.c310
-rw-r--r--cmd/avb.c485
-rw-r--r--cmd/axi.c355
-rw-r--r--cmd/bcb.c504
-rw-r--r--cmd/bdinfo.c216
-rw-r--r--cmd/bind.c260
-rw-r--r--cmd/binop.c154
-rw-r--r--cmd/blk_common.c131
-rw-r--r--cmd/blkcache.c75
-rw-r--r--cmd/blkmap.c239
-rw-r--r--cmd/blob.c124
-rw-r--r--cmd/bloblist.c37
-rw-r--r--cmd/bmp.c96
-rw-r--r--cmd/boot.c72
-rw-r--r--cmd/bootcount.c55
-rw-r--r--cmd/bootdev.c151
-rw-r--r--cmd/bootefi.c269
-rw-r--r--cmd/bootflow.c638
-rw-r--r--cmd/booti.c178
-rw-r--r--cmd/bootm.c585
-rw-r--r--cmd/bootmenu.c717
-rw-r--r--cmd/bootmeth.c134
-rw-r--r--cmd/bootstage.c102
-rw-r--r--cmd/bootstd.c65
-rw-r--r--cmd/bootz.c132
-rw-r--r--cmd/broadcom/Makefile6
-rw-r--r--cmd/broadcom/chimp_boot.c36
-rw-r--r--cmd/broadcom/chimp_handshake.c32
-rw-r--r--cmd/broadcom/nitro_image_load.c126
-rw-r--r--cmd/btrfs.c26
-rw-r--r--cmd/button.c85
-rw-r--r--cmd/c5_pl330_dma.c49
-rw-r--r--cmd/cache.c102
-rw-r--r--cmd/cat.c70
-rw-r--r--cmd/cbfs.c230
-rw-r--r--cmd/cedit.c321
-rw-r--r--cmd/cli.c128
-rw-r--r--cmd/clk.c153
-rw-r--r--cmd/clone.c128
-rw-r--r--cmd/cls.c21
-rw-r--r--cmd/config.c44
-rw-r--r--cmd/conitrace.c49
-rw-r--r--cmd/console.c56
-rw-r--r--cmd/cpu.c131
-rw-r--r--cmd/cramfs.c210
-rw-r--r--cmd/cros_ec.c568
-rw-r--r--cmd/cyclic.c84
-rw-r--r--cmd/date.c215
-rw-r--r--cmd/demo.c135
-rw-r--r--cmd/dfu.c112
-rw-r--r--cmd/diag.c59
-rw-r--r--cmd/disk.c131
-rw-r--r--cmd/dm.c131
-rw-r--r--cmd/echo.c43
-rw-r--r--cmd/eeprom.c577
-rw-r--r--cmd/efi.c330
-rw-r--r--cmd/efi_common.c23
-rw-r--r--cmd/eficonfig.c2507
-rw-r--r--cmd/eficonfig_sbkey.c543
-rw-r--r--cmd/efidebug.c1700
-rw-r--r--cmd/elf.c338
-rw-r--r--cmd/erofs.c42
-rw-r--r--cmd/ethsw.c1103
-rw-r--r--cmd/event.c24
-rw-r--r--cmd/exit.c26
-rw-r--r--cmd/ext2.c52
-rw-r--r--cmd/ext4.c90
-rw-r--r--cmd/extension_board.c202
-rw-r--r--cmd/fastboot.c187
-rw-r--r--cmd/fat.c135
-rw-r--r--cmd/fdt.c1159
-rw-r--r--cmd/flash.c700
-rw-r--r--cmd/font.c89
-rw-r--r--cmd/fpga.c452
-rw-r--r--cmd/fpgad.c101
-rw-r--r--cmd/fs.c154
-rw-r--r--cmd/fs_uuid.c24
-rw-r--r--cmd/fuse.c211
-rw-r--r--cmd/fwu_mdata.c89
-rw-r--r--cmd/gettime.c39
-rw-r--r--cmd/gpio.c317
-rw-r--r--cmd/gpt.c1240
-rw-r--r--cmd/hash.c57
-rw-r--r--cmd/help.c36
-rw-r--r--cmd/history.c22
-rw-r--r--cmd/host.c273
-rw-r--r--cmd/i2c.c1984
-rw-r--r--cmd/i3c.c271
-rw-r--r--cmd/ide.c78
-rw-r--r--cmd/ini.c251
-rw-r--r--cmd/io.c125
-rw-r--r--cmd/iotrace.c129
-rw-r--r--cmd/irq.c37
-rw-r--r--cmd/itest.c221
-rw-r--r--cmd/jffs2.c611
-rw-r--r--cmd/kaslrseed.c43
-rw-r--r--cmd/led.c133
-rw-r--r--cmd/legacy-mtd-utils.c102
-rw-r--r--cmd/legacy-mtd-utils.h12
-rw-r--r--cmd/legacy_led.c186
-rw-r--r--cmd/license.c45
-rw-r--r--cmd/load.c1193
-rw-r--r--cmd/log.c420
-rw-r--r--cmd/lsblk.c52
-rw-r--r--cmd/lwip/Makefile6
-rw-r--r--cmd/lwip/dhcp.c9
-rw-r--r--cmd/lwip/dns.c8
-rw-r--r--cmd/lwip/ping.c183
-rw-r--r--cmd/lwip/sntp.c133
-rw-r--r--cmd/lwip/tftp.c9
-rw-r--r--cmd/lwip/wget.c222
-rw-r--r--cmd/lzmadec.c55
-rw-r--r--cmd/mbr.c315
-rw-r--r--cmd/md5sum.c55
-rw-r--r--cmd/mdio.c333
-rw-r--r--cmd/mem.c1436
-rw-r--r--cmd/meminfo.c105
-rw-r--r--cmd/meson/Makefile5
-rw-r--r--cmd/meson/sm.c191
-rw-r--r--cmd/mii.c478
-rw-r--r--cmd/misc.c131
-rw-r--r--cmd/mmc.c1368
-rw-r--r--cmd/mp.c93
-rw-r--r--cmd/mtd.c831
-rw-r--r--cmd/mtdparts.c2126
-rw-r--r--cmd/mux.c183
-rw-r--r--cmd/mvebu/Kconfig82
-rw-r--r--cmd/mvebu/Makefile8
-rw-r--r--cmd/mvebu/bubt.c1265
-rw-r--r--cmd/mvebu/comphy_rx_training.c56
-rw-r--r--cmd/nand.c1253
-rw-r--r--cmd/net-common.c106
-rw-r--r--cmd/net.c677
-rw-r--r--cmd/nvedit.c1296
-rw-r--r--cmd/nvedit_efi.c533
-rw-r--r--cmd/nvme.c56
-rw-r--r--cmd/onenand.c599
-rw-r--r--cmd/optee.c71
-rw-r--r--cmd/optee_rpmb.c282
-rw-r--r--cmd/osd.c291
-rw-r--r--cmd/panic.c23
-rw-r--r--cmd/part.c318
-rw-r--r--cmd/pause.c32
-rw-r--r--cmd/pcap.c71
-rw-r--r--cmd/pci.c645
-rw-r--r--cmd/pci_mps.c161
-rw-r--r--cmd/pinmux.c180
-rw-r--r--cmd/pmc.c82
-rw-r--r--cmd/pmic.c230
-rw-r--r--cmd/printf.c647
-rw-r--r--cmd/printf.h8
-rw-r--r--cmd/pstore.c580
-rw-r--r--cmd/pvblock.c28
-rw-r--r--cmd/pwm.c115
-rw-r--r--cmd/pxe.c335
-rw-r--r--cmd/qfw.c123
-rw-r--r--cmd/read.c83
-rw-r--r--cmd/reginfo.c24
-rw-r--r--cmd/regulator.c468
-rw-r--r--cmd/remoteproc.c282
-rw-r--r--cmd/riscv/Makefile4
-rw-r--r--cmd/riscv/exception.c91
-rw-r--r--cmd/riscv/sbi.c136
-rw-r--r--cmd/rkmtd.c203
-rw-r--r--cmd/rng.c80
-rw-r--r--cmd/rockusb.c74
-rw-r--r--cmd/rtc.c167
-rw-r--r--cmd/sandbox/Makefile3
-rw-r--r--cmd/sandbox/exception.c44
-rw-r--r--cmd/sata.c126
-rw-r--r--cmd/sb.c58
-rw-r--r--cmd/scmi.c384
-rw-r--r--cmd/scp03.c51
-rw-r--r--cmd/scsi.c71
-rw-r--r--cmd/seama.c158
-rw-r--r--cmd/setexpr.c569
-rw-r--r--cmd/sf.c653
-rw-r--r--cmd/sha1sum.c53
-rw-r--r--cmd/sleep.c59
-rw-r--r--cmd/smbios.c500
-rw-r--r--cmd/smccc.c72
-rw-r--r--cmd/sound.c104
-rw-r--r--cmd/source.c72
-rw-r--r--cmd/spawn.c188
-rw-r--r--cmd/spi.c176
-rw-r--r--cmd/spl.c167
-rw-r--r--cmd/sqfs.c42
-rw-r--r--cmd/stackprot_test.c27
-rw-r--r--cmd/strings.c47
-rw-r--r--cmd/sysboot.c134
-rw-r--r--cmd/tcpm.c136
-rw-r--r--cmd/temperature.c84
-rw-r--r--cmd/terminal.c75
-rw-r--r--cmd/test.c237
-rw-r--r--cmd/thordown.c84
-rw-r--r--cmd/ti/Kconfig19
-rw-r--r--cmd/ti/Makefile5
-rw-r--r--cmd/ti/ddr3.c342
-rw-r--r--cmd/ti/pd.c184
-rw-r--r--cmd/time.c44
-rw-r--r--cmd/timer.c35
-rw-r--r--cmd/tlv_eeprom.c1111
-rw-r--r--cmd/tpm-common.c422
-rw-r--r--cmd/tpm-user-utils.h29
-rw-r--r--cmd/tpm-v1.c839
-rw-r--r--cmd/tpm-v2.c598
-rw-r--r--cmd/tpm_test.c573
-rw-r--r--cmd/trace.c124
-rw-r--r--cmd/ubi.c921
-rw-r--r--cmd/ubifs.c171
-rw-r--r--cmd/ufetch.c234
-rw-r--r--cmd/ufs.c38
-rw-r--r--cmd/unlz4.c48
-rw-r--r--cmd/unzip.c93
-rw-r--r--cmd/upl.c119
-rw-r--r--cmd/usb.c725
-rw-r--r--cmd/usb_gadget_sdp.c58
-rw-r--r--cmd/usb_mass_storage.c259
-rw-r--r--cmd/vbe.c116
-rw-r--r--cmd/version.c38
-rw-r--r--cmd/video.c60
-rw-r--r--cmd/virtio.c53
-rw-r--r--cmd/w1.c126
-rw-r--r--cmd/wdt.c173
-rw-r--r--cmd/wol.c33
-rw-r--r--cmd/x86/Makefile9
-rw-r--r--cmd/x86/cbcmos.c139
-rw-r--r--cmd/x86/cbsysinfo.c474
-rw-r--r--cmd/x86/cpuid.c37
-rw-r--r--cmd/x86/exception.c27
-rw-r--r--cmd/x86/fsp.c109
-rw-r--r--cmd/x86/hob.c163
-rw-r--r--cmd/x86/msr.c52
-rw-r--r--cmd/x86/mtrr.c140
-rw-r--r--cmd/x86/zboot.c192
-rw-r--r--cmd/ximg.c297
-rw-r--r--cmd/xxd.c82
-rw-r--r--cmd/zfs.c167
-rw-r--r--cmd/zip.c43
253 files changed, 67745 insertions, 0 deletions
diff --git a/cmd/.gitignore b/cmd/.gitignore
new file mode 100644
index 00000000000..bab889fd3d7
--- /dev/null
+++ b/cmd/.gitignore
@@ -0,0 +1,6 @@
+config_data.gz
+config_data_gz.h
+config_data_size.h
+license_data.gz
+license_data_gz.h
+license_data_size.h
diff --git a/cmd/2048.c b/cmd/2048.c
new file mode 100644
index 00000000000..aa0f82721dc
--- /dev/null
+++ b/cmd/2048.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: MIT
+// SPDX-FileCopyrightText: © 2014 Maurits van der Schee
+
+/* Console version of the game "2048" for GNU/Linux */
+
+#include <cli.h>
+#include <command.h>
+#include <rand.h>
+#include <vsprintf.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+
+#define SIZE 4
+static uint score;
+
+static void getColor(uint value, char *color, size_t length)
+{
+ u8 original[] = {
+ 8, 255, 1, 255, 2, 255, 3, 255,
+ 4, 255, 5, 255, 6, 255, 7, 255,
+ 9, 0, 10, 0, 11, 0, 12, 0, 13,
+ 0, 14, 0, 255, 0, 255, 0};
+ u8 *scheme = original;
+ u8 *background = scheme + 0;
+ u8 *foreground = scheme + 1;
+
+ if (value > 0) {
+ while (value >>= 1) {
+ if (background + 2 < scheme + sizeof(original)) {
+ background += 2;
+ foreground += 2;
+ }
+ }
+ }
+ snprintf(color, length, "\033[38;5;%d;48;5;%dm", *foreground,
+ *background);
+}
+
+static void drawBoard(u16 board[SIZE][SIZE])
+{
+ int x, y;
+ char color[40], reset[] = "\033[0m";
+
+ printf("\033[H");
+ printf("2048.c %17d pts\n\n", score);
+
+ for (y = 0; y < SIZE; y++) {
+ for (x = 0; x < SIZE; x++) {
+ getColor(board[x][y], color, 40);
+ printf("%s", color);
+ printf(" ");
+ printf("%s", reset);
+ }
+ printf("\n");
+ for (x = 0; x < SIZE; x++) {
+ getColor(board[x][y], color, 40);
+ printf("%s", color);
+ if (board[x][y] != 0) {
+ char s[8];
+ s8 t;
+
+ snprintf(s, 8, "%u", board[x][y]);
+ t = 7 - strlen(s);
+ printf("%*s%s%*s", t - t / 2, "", s, t / 2, "");
+ } else {
+ printf(" · ");
+ }
+ printf("%s", reset);
+ }
+ printf("\n");
+ for (x = 0; x < SIZE; x++) {
+ getColor(board[x][y], color, 40);
+ printf("%s", color);
+ printf(" ");
+ printf("%s", reset);
+ }
+ printf("\n");
+ }
+ printf("\n");
+ printf(" ←, ↑, →, ↓ or q \n");
+ printf("\033[A");
+}
+
+static int8_t findTarget(u16 array[SIZE], int x, int stop)
+{
+ int t;
+
+ /* if the position is already on the first, don't evaluate */
+ if (x == 0)
+ return x;
+ for (t = x - 1; t >= 0; t--) {
+ if (array[t]) {
+ if (array[t] != array[x]) {
+ /* merge is not possible, take next position */
+ return t + 1;
+ }
+ return t;
+ }
+
+ /* we should not slide further, return this one */
+ if (t == stop)
+ return t;
+ }
+ /* we did not find a */
+ return x;
+}
+
+static bool slideArray(u16 array[SIZE])
+{
+ bool success = false;
+ int x, t, stop = 0;
+
+ for (x = 0; x < SIZE; x++) {
+ if (array[x] != 0) {
+ t = findTarget(array, x, stop);
+ /*
+ * if target is not original position, then move or
+ * merge
+ */
+ if (t != x) {
+ /*
+ * if target is not zero, set stop to avoid
+ * double merge
+ */
+ if (array[t]) {
+ score += array[t] + array[x];
+ stop = t + 1;
+ }
+ array[t] += array[x];
+ array[x] = 0;
+ success = true;
+ }
+ }
+ }
+ return success;
+}
+
+static void rotateBoard(u16 board[SIZE][SIZE])
+{
+ s8 i, j, n = SIZE;
+ int tmp;
+
+ for (i = 0; i < n / 2; i++) {
+ for (j = i; j < n - i - 1; j++) {
+ tmp = board[i][j];
+ board[i][j] = board[j][n - i - 1];
+ board[j][n - i - 1] = board[n - i - 1][n - j - 1];
+ board[n - i - 1][n - j - 1] = board[n - j - 1][i];
+ board[n - j - 1][i] = tmp;
+ }
+ }
+}
+
+static bool moveUp(u16 board[SIZE][SIZE])
+{
+ bool success = false;
+ int x;
+
+ for (x = 0; x < SIZE; x++)
+ success |= slideArray(board[x]);
+
+ return success;
+}
+
+static bool moveLeft(u16 board[SIZE][SIZE])
+{
+ bool success;
+
+ rotateBoard(board);
+ success = moveUp(board);
+ rotateBoard(board);
+ rotateBoard(board);
+ rotateBoard(board);
+ return success;
+}
+
+static bool moveDown(u16 board[SIZE][SIZE])
+{
+ bool success;
+
+ rotateBoard(board);
+ rotateBoard(board);
+ success = moveUp(board);
+ rotateBoard(board);
+ rotateBoard(board);
+ return success;
+}
+
+static bool moveRight(u16 board[SIZE][SIZE])
+{
+ bool success;
+
+ rotateBoard(board);
+ rotateBoard(board);
+ rotateBoard(board);
+ success = moveUp(board);
+ rotateBoard(board);
+ return success;
+}
+
+static bool findPairDown(u16 board[SIZE][SIZE])
+{
+ bool success = false;
+ int x, y;
+
+ for (x = 0; x < SIZE; x++) {
+ for (y = 0; y < SIZE - 1; y++) {
+ if (board[x][y] == board[x][y + 1])
+ return true;
+ }
+ }
+
+ return success;
+}
+
+static int16_t countEmpty(u16 board[SIZE][SIZE])
+{
+ int x, y;
+ int count = 0;
+
+ for (x = 0; x < SIZE; x++) {
+ for (y = 0; y < SIZE; y++) {
+ if (board[x][y] == 0)
+ count++;
+ }
+ }
+ return count;
+}
+
+static bool gameEnded(u16 board[SIZE][SIZE])
+{
+ bool ended = true;
+
+ if (countEmpty(board) > 0)
+ return false;
+ if (findPairDown(board))
+ return false;
+ rotateBoard(board);
+ if (findPairDown(board))
+ ended = false;
+ rotateBoard(board);
+ rotateBoard(board);
+ rotateBoard(board);
+
+ return ended;
+}
+
+static void addRandom(u16 board[SIZE][SIZE])
+{
+ int x, y;
+ int r, len = 0;
+ u16 n, list[SIZE * SIZE][2];
+
+ for (x = 0; x < SIZE; x++) {
+ for (y = 0; y < SIZE; y++) {
+ if (board[x][y] == 0) {
+ list[len][0] = x;
+ list[len][1] = y;
+ len++;
+ }
+ }
+ }
+
+ if (len > 0) {
+ r = rand() % len;
+ x = list[r][0];
+ y = list[r][1];
+ n = ((rand() % 10) / 9 + 1) * 2;
+ board[x][y] = n;
+ }
+}
+
+static int test(void)
+{
+ u16 array[SIZE];
+ u16 data[] = {
+ 0, 0, 0, 2, 2, 0, 0, 0,
+ 0, 0, 2, 2, 4, 0, 0, 0,
+ 0, 2, 0, 2, 4, 0, 0, 0,
+ 2, 0, 0, 2, 4, 0, 0, 0,
+ 2, 0, 2, 0, 4, 0, 0, 0,
+ 2, 2, 2, 0, 4, 2, 0, 0,
+ 2, 0, 2, 2, 4, 2, 0, 0,
+ 2, 2, 0, 2, 4, 2, 0, 0,
+ 2, 2, 2, 2, 4, 4, 0, 0,
+ 4, 4, 2, 2, 8, 4, 0, 0,
+ 2, 2, 4, 4, 4, 8, 0, 0,
+ 8, 0, 2, 2, 8, 4, 0, 0,
+ 4, 0, 2, 2, 4, 4, 0, 0
+ };
+ u16 *in, *out;
+ u16 t, tests;
+ int i;
+ bool success = true;
+
+ tests = (sizeof(data) / sizeof(data[0])) / (2 * SIZE);
+ for (t = 0; t < tests; t++) {
+ in = data + t * 2 * SIZE;
+ out = in + SIZE;
+ for (i = 0; i < SIZE; i++)
+ array[i] = in[i];
+ slideArray(array);
+ for (i = 0; i < SIZE; i++) {
+ if (array[i] != out[i])
+ success = false;
+ }
+ if (!success) {
+ for (i = 0; i < SIZE; i++)
+ printf("%d ", in[i]);
+ printf(" = > ");
+ for (i = 0; i < SIZE; i++)
+ printf("%d ", array[i]);
+ printf("expected ");
+ for (i = 0; i < SIZE; i++)
+ printf("%d ", in[i]);
+ printf(" = > ");
+ for (i = 0; i < SIZE; i++)
+ printf("%d ", out[i]);
+ printf("\n");
+ break;
+ }
+ }
+ if (success)
+ printf("All %u tests executed successfully\n", tests);
+
+ return !success;
+}
+
+static int do_2048(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cli_ch_state cch_s, *cch = &cch_s;
+ u16 board[SIZE][SIZE];
+ bool success;
+
+ if (argc == 2 && strcmp(argv[1], "test") == 0)
+ return test();
+
+ score = 0;
+
+ printf("\033[?25l\033[2J\033[H");
+
+ memset(board, 0, sizeof(board));
+ addRandom(board);
+ addRandom(board);
+ drawBoard(board);
+ cli_ch_init(cch);
+ while (true) {
+ int c;
+
+ c = cli_ch_process(cch, 0);
+ if (!c) {
+ c = getchar();
+ c = cli_ch_process(cch, c);
+ }
+ switch (c) {
+ case CTL_CH('b'): /* left arrow */
+ success = moveLeft(board);
+ break;
+ case CTL_CH('f'): /* right arrow */
+ success = moveRight(board);
+ break;
+ case CTL_CH('p'):/* up arrow */
+ success = moveUp(board);
+ break;
+ case CTL_CH('n'): /* down arrow */
+ success = moveDown(board);
+ break;
+ default:
+ success = false;
+ }
+ if (success) {
+ drawBoard(board);
+ mdelay(150);
+ addRandom(board);
+ drawBoard(board);
+ if (gameEnded(board)) {
+ printf(" GAME OVER \n");
+ break;
+ }
+ }
+ if (c == 'q') {
+ printf(" QUIT \n");
+ break;
+ }
+ }
+
+ printf("\033[?25h");
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ 2048, 2, 1, do_2048,
+ "The 2048 game",
+ "Use your arrow keys to move the tiles. When two tiles with "
+ "the same number touch, they merge into one!"
+);
diff --git a/cmd/Kconfig b/cmd/Kconfig
new file mode 100644
index 00000000000..eb615552a00
--- /dev/null
+++ b/cmd/Kconfig
@@ -0,0 +1,3128 @@
+menuconfig CMDLINE
+ bool "Command line interface"
+ default y
+ help
+ Enable U-Boot's command-line functions. This provides a means
+ to enter commands into U-Boot for a wide variety of purposes. It
+ also allows scripts (containing commands) to be executed.
+ Various commands and command categories can be individually enabled.
+ Depending on the number of commands enabled, this can add
+ substantially to the size of U-Boot.
+
+if CMDLINE
+
+config HUSH_PARSER
+ bool "Use hush shell"
+ help
+ This option enables the "hush" shell (from Busybox) as command line
+ interpreter, thus enabling powerful command line syntax like
+ if...then...else...fi conditionals or `&&' and '||'
+ constructs ("shell scripts").
+
+ If disabled, you get the old, much simpler behaviour with a somewhat
+ smaller memory footprint.
+
+menu "Hush flavor to use"
+depends on HUSH_PARSER
+
+config HUSH_OLD_PARSER
+ bool "Use hush old parser"
+ default y
+ help
+ This option enables the old flavor of hush based on hush Busybox from
+ 2005.
+
+ It is actually the default U-Boot shell when decided to use hush as shell.
+
+config HUSH_MODERN_PARSER
+ bool "Use hush modern parser"
+ help
+ This option enables the new flavor of hush based on hush upstream
+ Busybox.
+
+ This parser is experimental and not well tested.
+
+config HUSH_SELECTABLE
+ bool
+ default y if HUSH_OLD_PARSER && HUSH_MODERN_PARSER
+endmenu
+
+config CMDLINE_EDITING
+ bool "Enable command line editing"
+ default y
+ help
+ Enable editing and History functions for interactive command line
+ input operations
+
+config CMDLINE_PS_SUPPORT
+ bool "Enable support for changing the command prompt string at run-time"
+ depends on HUSH_PARSER
+ help
+ Only static string in the prompt is supported so far. The string is
+ obtained from environment variables PS1 and PS2.
+
+config AUTO_COMPLETE
+ bool "Enable auto complete using TAB"
+ default y
+ help
+ Enable auto completion of commands using TAB.
+
+config SYS_LONGHELP
+ bool "Enable long help messages"
+ default y
+ help
+ Defined when you want long help messages included
+ Do not set this option when short of memory.
+
+config SYS_PROMPT
+ string "Shell prompt"
+ default "Zynq> " if ARCH_ZYNQ
+ default "ZynqMP> " if ARCH_ZYNQMP
+ default "=> "
+ help
+ This string is displayed in the command line to the left of the
+ cursor.
+
+config SYS_PROMPT_HUSH_PS2
+ string "Hush shell secondary prompt"
+ depends on HUSH_PARSER
+ default "> "
+ help
+ This defines the secondary prompt string, which is
+ printed when the command interpreter needs more input
+ to complete a command. Usually "> ".
+
+config SYS_MAXARGS
+ int "Maximum number arguments accepted by commands"
+ default 64
+
+config SYS_XTRACE
+ bool "Command execution tracer"
+ default y
+ help
+ This option enables the possiblity to print all commands before
+ executing them and after all variables are evaluated (similar
+ to Bash's xtrace/'set -x' feature).
+ To enable the tracer a variable "xtrace" needs to be defined in
+ the environment.
+
+config BUILD_BIN2C
+ bool
+
+comment "Commands"
+
+menu "Info commands"
+
+config CMD_ACPI
+ bool "acpi"
+ depends on ACPI
+ default y
+ help
+ List and dump ACPI tables. ACPI (Advanced Configuration and Power
+ Interface) is used mostly on x86 for providing information to the
+ Operating System about devices in the system. The tables are set up
+ by the firmware, typically U-Boot but possibly an earlier firmware
+ module, if U-Boot is chain-loaded from something else. ACPI tables
+ can also include code, to perform hardware-specific tasks required
+ by the Operating Systems. This allows some amount of separation
+ between the firmware and OS, and is particularly useful when you
+ want to make hardware changes without the OS needing to be adjusted.
+
+config CMD_ADDRMAP
+ bool "addrmap"
+ depends on ADDR_MAP
+ default y
+ help
+ List non-identity virtual-physical memory mappings for 32-bit CPUs.
+
+config CMD_BDI
+ bool "bdinfo"
+ default y
+ help
+ Print board info
+
+config CMD_BDINFO_EXTRA
+ bool "bdinfo extra features"
+ default y if SANDBOX || X86
+ help
+ Show additional information about the board. This uses a little more
+ code space but provides more options, particularly those useful for
+ bringup, development and debugging.
+
+config CMD_CONFIG
+ bool "config"
+ default SANDBOX
+ select BUILD_BIN2C
+ select GZIP
+ help
+ Print ".config" contents.
+
+ If this option is enabled, the ".config" file contents are embedded
+ in the U-Boot image and can be printed on the console by the "config"
+ command. This provides information of which options are enabled on
+ the running U-Boot.
+
+config CMD_CONSOLE
+ bool "coninfo"
+ default y
+ help
+ Print console devices and information.
+
+config CMD_CPU
+ bool "cpu"
+ depends on CPU
+ help
+ Print information about available CPUs. This normally shows the
+ 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"
+ 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
+ imply HEXDUMP if FWU_MDATA_V2
+ help
+ Command to read the metadata and dump it's contents
+
+config CMD_HELP
+ bool "help"
+ default y
+ help
+ Command to show help information about other commands.
+
+config CMD_HISTORY
+ bool "history"
+ depends on CMDLINE_EDITING
+ help
+ Show the command-line history, i.e. a list of commands that are in
+ the history buffer.
+
+config CMD_HISTORY_USE_CALLOC
+ bool "dynamically allocate memory"
+ default y
+ depends on CMD_HISTORY
+ help
+ Saying Y to this will use calloc to get the space for history
+ storing. Otherwise the history buffer will be an uninitialized
+ static array directly, without the memory allocation, and it is
+ writable after relocation to RAM. If u-boot is running from ROM
+ all the time or unsure, say Y to this.
+
+config CMD_LICENSE
+ bool "license"
+ select BUILD_BIN2C
+ depends on GZIP
+ help
+ Print GPL license text
+
+config CMD_PMC
+ bool "pmc"
+ help
+ Provides access to the Intel Power-Management Controller (PMC) so
+ that its state can be examined. This does not currently support
+ changing the state but it is still useful for debugging and seeing
+ what is going on.
+
+config CMD_REGINFO
+ bool "reginfo"
+ depends on PPC
+ help
+ Register dump
+
+config CMD_TCPM
+ bool "tcpm"
+ depends on TYPEC_TCPM
+ help
+ Show voltage and current negotiated via USB PD as well as the
+ current state of the Type C Port Manager (TCPM) state machine.
+
+config CMD_TLV_EEPROM
+ bool "tlv_eeprom"
+ depends on I2C_EEPROM
+ select CRC32
+ help
+ Display and program the system EEPROM data block in ONIE Tlvinfo
+ format. TLV stands for Type-Length-Value.
+
+config SPL_CMD_TLV_EEPROM
+ bool "tlv_eeprom for SPL"
+ depends on SPL_I2C_EEPROM
+ select SPL_DRIVERS_MISC
+ select SPL_CRC32
+ help
+ Read system EEPROM data block in ONIE Tlvinfo format from SPL.
+
+config CMD_SBI
+ bool "sbi"
+ depends on RISCV_SMODE && SBI_V02
+ help
+ Display information about the SBI implementation.
+
+config CMD_SMBIOS
+ bool "smbios"
+ depends on SMBIOS
+ help
+ Display the SMBIOS information.
+
+endmenu
+
+menu "Boot commands"
+
+config CMD_BOOTD
+ bool "bootd"
+ default y
+ help
+ Run the command stored in the environment "bootcmd", i.e.
+ "bootd" does the same thing as "run bootcmd".
+
+config CMD_BOOTM
+ bool "bootm"
+ default y
+ help
+ Boot an application image from the memory.
+
+config CMD_BOOTM_PRE_LOAD
+ bool "enable pre-load on bootm"
+ depends on CMD_BOOTM
+ depends on IMAGE_PRE_LOAD
+ help
+ Enable support of stage pre-load for the bootm command.
+ This stage allow to check or modify the image provided
+ to the bootm command.
+
+config CMD_BOOTDEV
+ bool "bootdev"
+ depends on BOOTSTD
+ default y if BOOTSTD_FULL
+ help
+ Support listing available bootdevs (boot devices) which can provide an
+ OS to boot, as well as showing information about a particular one.
+
+ This command is not necessary for bootstd to work.
+
+config CMD_BOOTFLOW
+ bool "bootflow"
+ depends on BOOTSTD
+ default y
+ help
+ Support scanning for bootflows available with the bootdevs. The
+ bootflows can optionally be booted.
+
+config CMD_BOOTFLOW_FULL
+ bool "bootflow - extract subcommands"
+ depends on BOOTSTD_FULL
+ default y
+ help
+ Add the ability to list the available bootflows, select one and obtain
+ information about it.
+
+ This command is not necessary for bootstd to work.
+
+config CMD_BOOTMETH
+ bool "bootmeth"
+ depends on BOOTSTD
+ default y if BOOTSTD_FULL
+ help
+ Support listing available bootmeths (methods used to boot an
+ Operating System), as well as selecting the order that the bootmeths
+ are used.
+
+ This command is not necessary for bootstd to work.
+
+config CMD_BOOTSTD
+ bool "bootstd"
+ depends on BOOTSTD
+ default y if BOOTSTD_FULL
+ help
+ Provide general information and control for bootstd.
+
+ This command is not necessary for bootstd to work.
+
+config BOOTM_EFI
+ bool "Support booting UEFI FIT images"
+ depends on EFI_BINARY_EXEC && CMD_BOOTM && FIT
+ default y
+ help
+ Support booting UEFI FIT images via the bootm command.
+
+config BOOTM_ELF
+ bool "Support booting ELF images"
+ depends on CMD_BOOTM && LIB_ELF
+ default y if LIB_ELF
+ help
+ Support booting ELF images via the bootm command.
+
+config CMD_BOOTZ
+ bool "bootz"
+ help
+ Boot the Linux zImage
+
+config CMD_BOOTI
+ bool "booti"
+ depends on ARM64 || RISCV || SANDBOX
+ default y
+ help
+ Boot an AArch64 Linux Kernel image from memory.
+
+config BOOTM_LINUX
+ bool "Support booting Linux OS images"
+ depends on CMD_BOOTM || CMD_BOOTZ || CMD_BOOTI
+ default y
+ help
+ Support booting the Linux kernel directly via a command such as bootm
+ or booti or bootz.
+
+config BOOTM_NETBSD
+ bool "Support booting NetBSD (non-EFI) loader images"
+ depends on CMD_BOOTM
+ default y
+ help
+ Support booting NetBSD via the bootm command.
+
+config BOOTM_OPENRTOS
+ bool "Support booting OPENRTOS / FreeRTOS images"
+ depends on CMD_BOOTM
+ help
+ Support booting OPENRTOS / FreeRTOS via the bootm command.
+
+config BOOTM_OSE
+ bool "Support booting Enea OSE images"
+ depends on (ARM && (ARM64 || CPU_V7A || CPU_V7R) || SANDBOX || PPC || X86)
+ depends on CMD_BOOTM
+ help
+ Support booting Enea OSE images via the bootm command.
+
+config BOOTM_PLAN9
+ bool "Support booting Plan9 OS images"
+ depends on CMD_BOOTM
+ default y
+ help
+ Support booting Plan9 images via the bootm command.
+
+config BOOTM_RTEMS
+ bool "Support booting RTEMS OS images"
+ depends on CMD_BOOTM
+ default y
+ help
+ Support booting RTEMS images via the bootm command.
+
+config CMD_SEAMA
+ bool "Support read SEAMA NAND images"
+ depends on (TARGET_BCMNS || TARGET_BCMNS3) && MTD_RAW_NAND
+ help
+ Support reading NAND Seattle Image (SEAMA) images.
+
+config CMD_UPL
+ bool "upl - Universal Payload Specification"
+ help
+ Provides commands to deal with UPL payloads and handoff information.
+ U-Boot supports generating and accepting handoff information. The
+ mkimage tool will eventually support creating payloads.
+
+config CMD_VBE
+ bool "vbe - Verified Boot for Embedded"
+ depends on BOOTMETH_VBE
+ default y if BOOTSTD_FULL
+ help
+ Provides various subcommands related to VBE, such as listing the
+ available methods, looking at the state and changing which method
+ is used to boot. Updating the parameters is not currently
+ supported.
+
+config BOOTM_VXWORKS
+ bool "Support booting VxWorks OS images"
+ depends on CMD_BOOTM
+ default y
+ help
+ Support booting VxWorks images via the bootm command.
+
+config CMD_BOOTEFI
+ bool "bootefi"
+ depends on EFI_LOADER
+ default y
+ help
+ Boot an EFI image from memory.
+
+if CMD_BOOTEFI
+config CMD_BOOTEFI_BINARY
+ bool "Allow booting an EFI binary directly"
+ depends on EFI_BINARY_EXEC
+ default y
+ help
+ Select this option to enable direct execution of binary at 'bootefi'.
+ This subcommand will allow you to load the UEFI binary using
+ other U-Boot commands or external methods and then run it.
+
+config CMD_BOOTEFI_BOOTMGR
+ bool "UEFI Boot Manager command"
+ depends on EFI_BOOTMGR
+ default y
+ help
+ Select this option to enable the 'bootmgr' subcommand of 'bootefi'.
+ This subcommand will allow you to select the UEFI binary to be booted
+ via UEFI variables Boot####, BootOrder, and BootNext.
+
+config CMD_BOOTEFI_HELLO
+ bool "Allow booting a standard EFI hello world for testing"
+ depends on CMD_BOOTEFI_BINARY && BOOTEFI_HELLO_COMPILE
+ default y if CMD_BOOTEFI_SELFTEST
+ help
+ This adds a standard EFI hello world application to U-Boot so that
+ it can be used with the 'bootefi hello' command. This is useful
+ for testing that EFI is working at a basic level, and for bringing
+ up EFI support on a new architecture.
+
+source "lib/efi_selftest/Kconfig"
+endif
+
+config CMD_BOOTMENU
+ bool "bootmenu"
+ select MENU
+ select CHARSET
+ help
+ Add an ANSI terminal boot menu command.
+
+config CMD_ADTIMG
+ bool "adtimg"
+ help
+ Android DTB/DTBO image manipulation commands. Read dtb/dtbo files from
+ image into RAM, dump image structure information, etc. Those dtb/dtbo
+ files should be merged in one dtb further, which needs to be passed to
+ the kernel, as part of a boot process.
+
+config CMD_ABOOTIMG
+ bool "abootimg"
+ depends on ANDROID_BOOT_IMAGE
+ help
+ Android Boot Image manipulation commands. Allows one to extract
+ images contained in boot.img, like kernel, ramdisk, dtb, etc, and
+ obtain corresponding meta-information from boot.img.
+
+ See doc/android/boot-image.rst for details.
+
+config CMD_CEDIT
+ bool "cedit - Configuration editor"
+ depends on CEDIT
+ default y
+ help
+ Provides a command to allow editing of board configuration and
+ providing a UI for the user to adjust settings. Subcommands allow
+ loading and saving of configuration as well as showing an editor.
+
+config CMD_ELF
+ bool "bootelf"
+ default y
+ select LIB_ELF
+ help
+ Boot an ELF image from memory.
+
+config CMD_ELF_BOOTVX
+ bool "bootvx"
+ default y
+ depends on CMD_ELF
+ help
+ Boot a vxWorks image from memory
+
+config CMD_ELF_FDT_SETUP
+ bool "Flattened Device Tree setup in bootelf cmd"
+ depends on CMD_ELF
+ select OF_LIBFDT
+ help
+ Do FDT setup in bootelf command optionally by param -d, which
+ allows to bring additional system info (e.g. /memory node) to
+ the Operating System or application.
+
+config CMD_FDT
+ bool "Flattened Device Tree utility commands"
+ default y
+ depends on OF_LIBFDT
+ help
+ Do FDT related setup before booting into the Operating System.
+
+config SUPPORT_EXTENSION_SCAN
+ bool
+
+config CMD_EXTENSION
+ bool "Extension board management command"
+ select CMD_FDT
+ depends on SUPPORT_EXTENSION_SCAN
+ help
+ Enables the "extension" command, which allows to detect
+ extension boards connected to the system, and apply
+ corresponding Device Tree overlays.
+
+config CMD_GO
+ bool "go"
+ default y
+ help
+ Start an application at a given address.
+
+config CMD_RUN
+ bool "run"
+ default y
+ help
+ Run the command in the given environment variable.
+
+config CMD_IMI
+ bool "iminfo"
+ default y
+ help
+ Print header information for application image.
+
+config CMD_IMLS
+ bool "imls"
+ depends on MTD_NOR_FLASH || FLASH_CFI_DRIVER
+ help
+ List all images found in flash
+
+config CMD_XIMG
+ bool "imxtract"
+ default y
+ help
+ Extract a part of a multi-image.
+
+config SYS_XIMG_LEN
+ hex "imxtract max gunzip size"
+ default 0x800000
+ depends on CMD_XIMG && GZIP
+ help
+ This provides the size of the commad-line argument area
+ used by imxtract for extracting pieces of FIT image.
+ It should be large enough to fit uncompressed size of
+ FIT piece we are extracting.
+
+config CMD_SPL
+ bool "spl export - Export boot information for Falcon boot"
+ depends on SPL
+ help
+ Falcon mode allows booting directly from SPL into an Operating
+ System such as Linux, thus skipping U-Boot proper. See
+ doc/README.falcon for full information about how to use this
+ command.
+
+config CMD_SPL_NAND_OFS
+ hex "Offset of OS args or dtb for Falcon-mode NAND boot"
+ depends on CMD_SPL && (TPL_NAND_SUPPORT || SPL_NAND_SUPPORT)
+ default 0x0
+ help
+ This provides the offset of the command line arguments for Linux
+ when booting from NAND in Falcon mode. See doc/README.falcon
+ for full information about how to use this option (and also see
+ board/gateworks/gw_ventana/README for an example).
+
+config CMD_SPL_NOR_OFS
+ hex "Offset of OS args or dtb for Falcon-mode NOR boot"
+ depends on CMD_SPL && SPL_NOR_SUPPORT
+ default 0x0
+ help
+ This provides the offset of the command line arguments or dtb for
+ Linux when booting from NOR in Falcon mode.
+
+config CMD_SPL_WRITE_SIZE
+ hex "Size of argument area"
+ depends on CMD_SPL
+ default 0x2000
+ help
+ This provides the size of the command-line argument area in NAND
+ flash used by Falcon-mode boot. See the documentation until CMD_SPL
+ for detail.
+
+config CMD_THOR_DOWNLOAD
+ bool "thor - TIZEN 'thor' download"
+ select DFU
+ select USB_FUNCTION_THOR
+ depends on USB_GADGET_DOWNLOAD
+ help
+ Implements the 'thor' download protocol. This is a way of
+ downloading a software update over USB from an attached host.
+ There is no documentation about this within the U-Boot source code
+ but you should be able to find something on the interwebs.
+
+config THOR_RESET_OFF
+ bool "thor: Disable reset on completion"
+ depends on CMD_THOR_DOWNLOAD
+
+config CMD_ZBOOT
+ bool "zboot - x86 boot command"
+ depends on ZBOOT
+ default y
+ help
+ With x86 machines it is common to boot a bzImage file which
+ contains both a kernel and a setup.bin file. The latter includes
+ configuration information from the dark ages which x86 boards still
+ need to pick things out of.
+
+ Consider using FIT in preference to this since it supports directly
+ booting both 32- and 64-bit kernels, as well as secure boot.
+ Documentation is available in doc/usage/fit/x86-fit-boot.rst.
+
+endmenu
+
+menu "Environment commands"
+
+config CMD_ASKENV
+ bool "ask for env variable"
+ help
+ Ask for environment variable
+
+config CMD_EXPORTENV
+ bool "env export"
+ default y
+ help
+ Export environments.
+
+config CMD_IMPORTENV
+ bool "env import"
+ default y
+ help
+ Import environments.
+
+config CMD_EDITENV
+ bool "editenv"
+ default y
+ help
+ Edit environment variable.
+
+config CMD_GREPENV
+ bool "search env"
+ help
+ Allow for searching environment variables
+
+config CMD_SAVEENV
+ bool "saveenv"
+ default y
+ help
+ Save all environment variables into the compiled-in persistent
+ storage.
+
+config CMD_ERASEENV
+ bool "eraseenv"
+ depends on CMD_SAVEENV
+ help
+ Erase environment variables from the compiled-in persistent
+ storage.
+
+config CMD_ENV_EXISTS
+ bool "env exists"
+ default y
+ help
+ Check if a variable is defined in the environment for use in
+ shell scripting.
+
+config CMD_ENV_CALLBACK
+ bool "env callbacks - print callbacks and their associated variables"
+ help
+ Some environment variable have callbacks defined by
+ U_BOOT_ENV_CALLBACK. These are called when the variable changes.
+ For example changing "baudrate" adjust the serial baud rate. This
+ command lists the currently defined callbacks.
+
+config CMD_ENV_FLAGS
+ bool "env flags -print variables that have non-default flags"
+ help
+ Some environment variables have special flags that control their
+ behaviour. For example, serial# can only be written once and cannot
+ be deleted. This command shows the variables that have special
+ flags.
+
+config CMD_NVEDIT_EFI
+ bool "env [set|print] -e - set/print UEFI variables"
+ depends on EFI_LOADER
+ imply HEXDUMP
+ help
+ UEFI variables are encoded as some form of U-Boot variables.
+ If enabled, we are allowed to set/print UEFI variables using
+ "env" command with "-e" option without knowing details.
+
+config CMD_NVEDIT_INDIRECT
+ bool "env indirect - Sets environment value from another"
+
+config CMD_NVEDIT_INFO
+ bool "env info - print or evaluate environment information"
+ help
+ Print environment information:
+ - env_valid : is environment valid
+ - env_ready : is environment imported into hash table
+ - env_use_default : is default environment used
+
+ This command can be optionally used for evaluation in scripts:
+ [-d] : evaluate whether default environment is used
+ [-p] : evaluate whether environment can be persisted
+ [-q] : quiet output
+ The result of multiple evaluations will be combined with AND.
+
+config CMD_NVEDIT_LOAD
+ bool "env load"
+ help
+ Load all environment variables from the compiled-in persistent
+ storage.
+
+config CMD_NVEDIT_SELECT
+ bool "env select"
+ help
+ Select the compiled-in persistent storage of environment variables.
+
+endmenu
+
+menu "Memory commands"
+
+config CMD_BINOP
+ bool "binop"
+ help
+ Compute binary operations (xor, or, and) of byte arrays of arbitrary
+ size from memory and store the result in memory or the environment.
+
+config CMD_BLOBLIST
+ bool "bloblist"
+ depends on BLOBLIST
+ default y
+ help
+ Show information about the bloblist, a collection of binary blobs
+ held in memory that persist between SPL and U-Boot. In the case of
+ x86 devices the bloblist can be used to hold ACPI tables so that they
+ remain available in memory.
+
+config CMD_CRC32
+ bool "crc32"
+ default y
+ select HASH
+ help
+ Compute CRC32.
+
+config CRC32_VERIFY
+ bool "crc32 -v"
+ depends on CMD_CRC32
+ help
+ Add -v option to verify data against a crc32 checksum.
+
+config CMD_EEPROM
+ bool "eeprom - EEPROM subsystem"
+ depends on DM_I2C || SYS_I2C_LEGACY
+ help
+ (deprecated, needs conversion to driver model)
+ Provides commands to read and write EEPROM (Electrically Erasable
+ Programmable Read Only Memory) chips that are connected over an
+ I2C bus.
+
+config CMD_EEPROM_LAYOUT
+ bool "Enable layout-aware eeprom commands"
+ depends on CMD_EEPROM
+ help
+ (deprecated, needs conversion to driver model)
+ When enabled, additional eeprom sub-commands become available.
+
+ eeprom print - prints the contents of the eeprom in a human-readable
+ way (eeprom layout fields, and data formatted to be fit for human
+ consumption).
+
+ eeprom update - allows user to update eeprom fields by specifying
+ the field name, and providing the new data in a human readable format
+ (same format as displayed by the eeprom print command).
+
+ Both commands can either auto detect the layout, or be told which
+ layout to use.
+
+ Feature API:
+ __weak int parse_layout_version(char *str)
+ - override to provide your own layout name parsing
+ __weak void __eeprom_layout_assign(struct eeprom_layout *layout,
+ int layout_version);
+ - override to setup the layout metadata based on the version
+ __weak int eeprom_layout_detect(unsigned char *data)
+ - override to provide your own algorithm for detecting layout
+ version
+ eeprom_field.c
+ - contains various printing and updating functions for common
+ types of eeprom fields. Can be used for defining
+ custom layouts.
+
+config EEPROM_LAYOUT_VERSIONS
+ bool "Support specifying eeprom layout version"
+ depends on CMD_EEPROM_LAYOUT
+ help
+ Support specifying eeprom layout version in the 'eeprom' command
+ via the -l option.
+
+config EEPROM_LAYOUT_HELP_STRING
+ string "Tells user what layout names are supported"
+ depends on EEPROM_LAYOUT_VERSIONS
+ default "<not defined>"
+ help
+ Help printed with the LAYOUT VERSIONS part of the 'eeprom'
+ command's help.
+
+config SYS_I2C_EEPROM_BUS
+ int "I2C bus of the EEPROM device."
+ depends on CMD_EEPROM
+ default 0
+
+config SYS_I2C_EEPROM_ADDR_LEN
+ int "Length in bytes of the EEPROM memory array address"
+ depends on CMD_EEPROM || ID_EEPROM
+ default 1
+ range 1 2
+ help
+ Note: This is NOT the chip address length!
+
+config SYS_EEPROM_SIZE
+ depends on CMD_EEPROM
+ int "Size in bytes of the EEPROM device"
+ default 256
+
+config SYS_EEPROM_PAGE_WRITE_BITS
+ int "Number of bits used to address bytes in a single page"
+ depends on CMD_EEPROM || ENV_IS_IN_EEPROM
+ default 8
+ help
+ The EEPROM page size is 2^SYS_EEPROM_PAGE_WRITE_BITS.
+ A 64 byte page, for example would require six bits.
+
+config SYS_EEPROM_PAGE_WRITE_DELAY_MS
+ int "Number of milliseconds to delay between page writes"
+ depends on CMD_EEPROM || CMD_I2C
+ default 0
+
+config LOOPW
+ bool "loopw"
+ help
+ Infinite write loop on address range
+
+config CMD_MD5SUM
+ bool "md5sum"
+ select MD5
+ select HASH
+ help
+ Compute MD5 checksum.
+
+config MD5SUM_VERIFY
+ bool "md5sum -v"
+ depends on CMD_MD5SUM
+ help
+ Add -v option to verify data against an MD5 checksum.
+
+config CMD_MEMINFO
+ bool "meminfo"
+ default y if SANDBOX || X86
+ help
+ Display memory information.
+
+config CMD_MEMINFO_MAP
+ bool "- with memory map"
+ depends on CMD_MEMINFO
+ default y if SANDBOX || X86
+ help
+ Shows a memory map, in addition to just the DRAM size. This allows
+ seeing where U-Boot's memory area is, at the top of DRAM, as well as
+ detail about each piece of it.
+
+ See doc/usage/cmd/meminfo.rst for more information.
+
+config CMD_MEMORY
+ bool "md, mm, nm, mw, cp, cmp, base, loop"
+ default y
+ help
+ Memory commands.
+ md - memory display
+ mm - memory modify (auto-incrementing address)
+ nm - memory modify (constant address)
+ mw - memory write (fill)
+ cp - memory copy
+ cmp - memory compare
+ base - print or set address offset
+ loop - initialize loop on address range
+
+config CMD_MEM_SEARCH
+ bool "ms - Memory search"
+ help
+ Memory-search command
+
+ This allows searching through a region of memory looking for hex
+ data (byte, 16-bit word, 32-bit long, also 64-bit on machines that
+ support it). It is also possible to search for a string. The
+ command accepts a memory range and a list of values to search for.
+ The values need to appear in memory in the same order they are given
+ in the command. At most 10 matches can be returned at a time, but
+ pressing return will show the next 10 matches. Environment variables
+ are set for use with scripting (memmatches, memaddr, mempos).
+
+config CMD_MX_CYCLIC
+ bool "Enable cyclic md/mw commands"
+ depends on CMD_MEMORY
+ help
+ Add the "mdc" and "mwc" memory commands. These are cyclic
+ "md/mw" commands.
+ Examples:
+
+ => mdc.b 10 4 500
+ This command will print 4 bytes (10,11,12,13) each 500 ms.
+
+ => mwc.l 100 12345678 10
+ This command will write 12345678 to address 100 all 10 ms.
+
+config CMD_RANDOM
+ bool "random"
+ default y
+ depends on CMD_MEMORY && (LIB_RAND || LIB_HW_RAND)
+ help
+ random - fill memory with random data
+
+config CMD_MEMTEST
+ bool "memtest"
+ help
+ Simple RAM read/write test.
+
+if CMD_MEMTEST
+
+config SYS_ALT_MEMTEST
+ bool "Alternative test"
+ help
+ Use a more complete alternative memory test.
+
+if SYS_ALT_MEMTEST
+
+config SYS_ALT_MEMTEST_BITFLIP
+ bool "Bitflip test"
+ default y
+ help
+ The alternative memory test includes bitflip test since 2020.07.
+ The bitflip test significantly increases the overall test time.
+ Bitflip test can optionally be disabled here.
+
+endif
+
+config SYS_MEMTEST_START
+ hex "default start address for mtest"
+ default 0x0
+ help
+ This is the default start address for mtest for simple read/write
+ test. If no arguments are given to mtest, default address is used
+ as start address.
+
+config SYS_MEMTEST_END
+ hex "default end address for mtest"
+ default 0x1000
+ help
+ This is the default end address for mtest for simple read/write
+ test. If no arguments are given to mtest, default address is used
+ as end address.
+
+endif
+
+config CMD_SHA1SUM
+ bool "sha1sum"
+ select SHA1
+ help
+ Compute SHA1 checksum.
+
+config SHA1SUM_VERIFY
+ bool "sha1sum -v"
+ depends on CMD_SHA1SUM
+ help
+ Add -v option to verify data against a SHA1 checksum.
+
+config CMD_STRINGS
+ bool "strings - display strings in memory"
+ help
+ This works similarly to the Unix 'strings' command except that it
+ works with a memory range. String of printable characters found
+ within the range are displayed. The minimum number of characters
+ for a sequence to be considered a string can be provided.
+
+endmenu
+
+menu "Compression commands"
+
+config CMD_LZMADEC
+ bool "lzmadec"
+ default y if CMD_BOOTI
+ select LZMA
+ help
+ Support decompressing an LZMA (Lempel-Ziv-Markov chain algorithm)
+ image from memory.
+
+config CMD_UNLZ4
+ bool "unlz4"
+ default y if CMD_BOOTI
+ select LZ4
+ help
+ Support decompressing an LZ4 image from memory region.
+
+config CMD_UNZIP
+ bool "unzip"
+ default y if CMD_BOOTI
+ select GZIP
+ help
+ Uncompress a zip-compressed memory region.
+
+config CMD_ZIP
+ bool "zip"
+ select GZIP_COMPRESSED
+ help
+ Compress a memory region with zlib deflate method.
+
+endmenu
+
+menu "Device access commands"
+
+config CMD_ARMFFA
+ bool "Arm FF-A test command"
+ depends on ARM_FFA_TRANSPORT
+ help
+ Provides a test command for the FF-A support
+ supported options:
+ - Listing the partition(s) info
+ - Sending a data pattern to the specified partition
+ - Displaying the arm_ffa device info
+
+config CMD_ARMFLASH
+ bool "armflash"
+ depends on FLASH_CFI_DRIVER
+ help
+ ARM Ltd reference designs flash partition access
+
+config CMD_ADC
+ bool "adc - Access Analog to Digital Converters info and data"
+ select ADC
+ depends on DM_REGULATOR
+ help
+ Shows ADC device info and permit printing one-shot analog converted
+ data from a named Analog to Digital Converter.
+
+config CMD_BCB
+ bool "bcb"
+ depends on PARTITIONS
+ help
+ Read/modify/write the fields of Bootloader Control Block, usually
+ stored on the flash "misc" partition with its structure defined in:
+ https://android.googlesource.com/platform/bootable/recovery/+/master/
+ bootloader_message/include/bootloader_message/bootloader_message.h
+
+ Some real-life use-cases include (but are not limited to):
+ - Determine the "boot reason" (and act accordingly):
+ https://source.android.com/devices/bootloader/boot-reason
+ - Get/pass a list of commands from/to recovery:
+ https://android.googlesource.com/platform/bootable/recovery
+ - Inspect/dump the contents of the BCB fields
+
+config CMD_BIND
+ bool "bind/unbind - Bind or unbind a device to/from a driver"
+ depends on DM
+ imply CMD_DM
+ help
+ Bind or unbind a device to/from a driver from the command line.
+ This is useful in situations where a device may be handled by several
+ drivers. For example, this can be used to bind a UDC to the usb ether
+ gadget driver from the command line.
+
+config CMD_CLK
+ bool "clk - Show and set clock frequencies"
+ depends on CLK
+ help
+ Show and set clock frequencies managed by CLK uclass drivers.
+
+config CMD_DEMO
+ bool "demo - Demonstration commands for driver model"
+ depends on DM
+ help
+ Provides a 'demo' command which can be used to play around with
+ driver model. To use this properly you will need to enable one or
+ both of the demo devices (DM_DEMO_SHAPE and DM_DEMO_SIMPLE).
+ Otherwise you will always get an empty list of devices. The demo
+ devices are defined in the sandbox device tree, so the easiest
+ option is to use sandbox and pass the -d point to sandbox's
+ u-boot.dtb file.
+
+config CMD_DFU
+ bool "dfu"
+ select DFU
+ help
+ Enables the command "dfu" which is used to have U-Boot create a DFU
+ class device via USB. This command requires that the "dfu_alt_info"
+ environment variable be set and define the alt settings to expose to
+ the host.
+
+config CMD_DM
+ bool "dm - Access to driver model information"
+ depends on DM
+ help
+ Provides access to driver model data structures and information,
+ such as a list of devices, list of uclasses and the state of each
+ device (e.g. activated). This is not required for operation, but
+ can be useful to see the state of driver model for debugging or
+ interest.
+
+config CMD_FASTBOOT
+ bool "fastboot - Android fastboot support"
+ depends on FASTBOOT
+ help
+ This enables the command "fastboot" which enables the Android
+ fastboot mode for the platform. Fastboot is a protocol for
+ downloading images, flashing and device control used on
+ Android devices. Fastboot requires either the network stack
+ enabled or support for acting as a USB device.
+
+ See doc/android/fastboot.rst for more information.
+
+config CMD_FLASH
+ bool "flinfo, erase, protect"
+ default y
+ depends on FLASH_CFI_DRIVER || MTD_NOR_FLASH
+ help
+ NOR flash support.
+ flinfo - print FLASH memory information
+ erase - FLASH memory
+ protect - enable or disable FLASH write protection
+
+config CMD_FPGA
+ bool "fpga"
+ depends on FPGA
+ default y
+ help
+ FPGA support.
+
+config CMD_FPGA_LOADB
+ bool "fpga loadb - load bitstream file"
+ default y
+ depends on CMD_FPGA && FPGA_XILINX
+ help
+ Supports loading an FPGA device from a bitstream file (.BIT)
+
+config CMD_FPGA_LOADP
+ bool "fpga loadp - load partial bitstream"
+ depends on CMD_FPGA
+ help
+ Supports loading an FPGA device from a bitstream buffer (.BIN)
+ containing a partial bitstream.
+
+config CMD_FPGA_LOADBP
+ bool "fpga loadbp - load partial bitstream file"
+ depends on CMD_FPGA && FPGA_XILINX
+ help
+ Supports loading an FPGA device from a bitstream file (.BIT)
+ containing a partial bitstream.
+
+config CMD_FPGA_LOADFS
+ bool "fpga loadfs - load bitstream from FAT filesystem"
+ depends on CMD_FPGA && FPGA_XILINX
+ help
+ Supports loading an FPGA device from a FAT filesystem.
+
+config CMD_FPGA_LOADMK
+ bool "fpga loadmk - load bitstream from image"
+ depends on CMD_FPGA
+ help
+ Supports loading an FPGA device from a image generated by mkimage.
+
+config CMD_FPGA_LOAD_SECURE
+ bool "fpga loads - loads secure bitstreams"
+ depends on CMD_FPGA && FPGA_XILINX
+ select FPGA_LOAD_SECURE
+ help
+ Enables the fpga loads command which is used to load secure
+ (authenticated or encrypted or both) bitstreams on to FPGA.
+
+config CMD_FPGAD
+ bool "fpgad - dump FPGA registers"
+ depends on GDSYS_LEGACY_DRIVERS
+ help
+ (legacy, needs conversion to driver model)
+ Provides a way to dump FPGA registers by calling the board-specific
+ fpga_get_reg() function. This functions similarly to the 'md'
+ command.
+
+config CMD_FUSE
+ bool "fuse - support for the fuse subssystem"
+ depends on !COMPILE_TEST
+ help
+ (deprecated - needs conversion to driver model)
+ This allows reading, sensing, programming or overriding fuses
+ which control the behaviour of the device. The command uses the
+ fuse_...() API.
+
+config CMD_FUSE_WRITEBUFF
+ bool "Support for the fuse writebuff"
+ depends on CMD_FUSE
+ help
+ This allows programming fuses, which control the behaviour of
+ the device, using a structured buffer in memory. The command
+ uses the fuse_writebuff() API.
+
+config CMD_GPIO
+ bool "gpio"
+ help
+ GPIO support.
+
+config CMD_GPIO_READ
+ bool "gpio read - save GPIO value to variable"
+ depends on CMD_GPIO
+ help
+ Enables the 'gpio read' command that saves the value
+ of a GPIO pin to a variable.
+
+config CMD_PWM
+ bool "pwm"
+ depends on DM_PWM
+ help
+ Control PWM channels, this allows invert/config/enable/disable PWM channels.
+
+config CMD_GPT
+ bool "GPT (GUID Partition Table) command"
+ select EFI_PARTITION
+ select PARTITION_UUIDS
+ imply RANDOM_UUID
+ help
+ Enable the 'gpt' command to ready and write GPT style partition
+ tables.
+
+config CMD_GPT_RENAME
+ bool "GPT partition renaming commands"
+ depends on CMD_GPT
+ help
+ Enables the 'gpt' command to interchange names on two GPT
+ partitions via the 'gpt swap' command or to rename single
+ partitions via the 'rename' command.
+
+config CMD_IDE
+ bool "ide - Support for IDE drivers"
+ depends on !COMPILE_TEST
+ select IDE
+ help
+ Provides an 'ide' command which allows accessing the IDE drive,
+ resetting the IDE interface, printing the partition table and
+ geting device info. It also enables the 'diskboot' command which
+ permits booting from an IDE drive.
+
+config CMD_IO
+ bool "io - Support for performing I/O accesses"
+ help
+ Provides an 'iod' command to display I/O space and an 'iow' command
+ to write values to the I/O space. This can be useful for manually
+ checking the state of devices during boot when debugging device
+ drivers, etc.
+
+config CMD_IOTRACE
+ bool "iotrace - Support for tracing I/O activity"
+ help
+ Provides an 'iotrace' command which supports recording I/O reads and
+ writes in a trace buffer in memory . It also maintains a checksum
+ of the trace records (even if space is exhausted) so that the
+ sequence of I/O accesses can be verified.
+
+ When debugging drivers it is useful to see what I/O accesses were
+ done and in what order.
+
+ Even if the individual accesses are of little interest it can be
+ useful to verify that the access pattern is consistent each time
+ an operation is performed. In this case a checksum can be used to
+ characterise the operation of a driver. The checksum can be compared
+ across different runs of the operation to verify that the driver is
+ working properly.
+
+ In particular, when performing major refactoring of the driver, where
+ the access pattern should not change, the checksum provides assurance
+ that the refactoring work has not broken the driver.
+
+ This works by sneaking into the io.h heder for an architecture and
+ redirecting I/O accesses through iotrace's tracing mechanism.
+
+ For now no commands are provided to examine the trace buffer. The
+ format is fairly simple, so 'md' is a reasonable substitute.
+
+ Note: The checksum feature is only useful for I/O regions where the
+ contents do not change outside of software control. Where this is not
+ suitable you can fall back to manually comparing the addresses. It
+ might be useful to enhance tracing to only checksum the accesses and
+ not the data read/written.
+
+config CMD_I2C
+ bool "i2c"
+ help
+ I2C support.
+
+config CMD_I3C
+ bool "i3c"
+ depends on I3C
+ help
+ Enable command to list i3c devices connected to the i3c controller
+ and perform read and write on the connected i3c devices.
+
+config CMD_W1
+ depends on W1
+ default y if W1
+ bool "w1 - Support for Dallas 1-Wire protocol"
+ help
+ Dallas 1-wire protocol support
+
+config CMD_LOADB
+ bool "loadb"
+ default y
+ help
+ Load a binary file over serial line.
+
+config CMD_LOADM
+ bool "loadm"
+ help
+ Load a binary over memory mapped.
+
+config CMD_LOADS
+ bool "loads - Load a file over serial in S-Record format"
+ default y
+ help
+ Load an S-Record file over serial line
+
+config LOADS_ECHO
+ bool "Echo all characters received during a loads back to console"
+ depends on CMD_LOADS
+ help
+ If enabled, all characters received during a serial download (using
+ the "loads" command) are echoed back. This might be needed by some
+ terminal emulations (like "cu"), but may as well just take time on
+ others. This sets the initial value of the "loads_echo" environment
+ variable to 1.
+
+config CMD_SAVES
+ bool "saves - Save a file over serial in S-Record format"
+ depends on CMD_LOADS
+ help
+ Provides a way to save a binary file using the Motorola S-Record
+ format over the serial line.
+
+config SYS_LOADS_BAUD_CHANGE
+ bool "Enable a temporary baudrate change during loads/saves command"
+ depends on CMD_LOADS || CMD_SAVES
+
+config CMD_LOADXY_TIMEOUT
+ int "loadxy_timeout"
+ range 0 2000
+ default 90
+ help
+ Initial timeout for loadx and loady commands. Zero means infinity.
+
+config CMD_LSBLK
+ depends on BLK
+ bool "lsblk - list block drivers and devices"
+ help
+ Print list of available block device drivers, and for each, the list
+ of known block devices.
+
+config CMD_MBR
+ bool "MBR (Master Boot Record) command"
+ select DOS_PARTITION
+ help
+ Enable the 'mbr' command to ready and write MBR (Master Boot Record)
+ style partition tables.
+
+config CMD_MISC
+ bool "misc"
+ depends on MISC
+ help
+ Enable the command "misc" for accessing miscellaneous devices with
+ a MISC uclass driver. The command provides listing all MISC devices
+ as well as read and write functionalities via their drivers.
+
+config CMD_MMC
+ bool "mmc"
+ depends on MMC
+ help
+ MMC memory mapped support.
+
+if CMD_MMC
+
+config CMD_BKOPS_ENABLE
+ bool "mmc bkops enable"
+ depends on CMD_MMC
+ help
+ Enable command for setting manual background operations handshake
+ on a eMMC device. The feature is optionally available on eMMC devices
+ conforming to standard >= 4.41.
+
+config CMD_MMC_REG
+ bool "Enable support for reading card registers in the mmc command"
+ depends on CMD_MMC
+ help
+ Enable the commands for reading card registers. This is useful
+ mostly for debugging or extracting details from the card.
+
+config CMD_MMC_RPMB
+ bool "Enable support for RPMB in the mmc command"
+ depends on SUPPORT_EMMC_RPMB
+ help
+ Enable the commands for reading, writing and programming the
+ key for the Replay Protection Memory Block partition in eMMC.
+
+config CMD_MMC_SWRITE
+ bool "mmc swrite"
+ depends on MMC_WRITE
+ select IMAGE_SPARSE
+ help
+ Enable support for the "mmc swrite" command to write Android sparse
+ images to eMMC.
+
+config MMC_SPEED_MODE_SET
+ bool "set speed mode using mmc command"
+ help
+ Enable setting speed mode using mmc rescan and mmc dev commands.
+ The speed mode is provided as the last argument in these commands
+ and is indicated using the index from enum bus_mode in
+ include/mmc.h. A speed mode can be set only if it has already
+ been enabled in the device tree.
+
+endif
+
+config CMD_CLONE
+ bool "clone"
+ depends on BLK
+ help
+ Enable storage cloning over block devices, useful for
+ initial flashing by external block device without network
+ or usb support.
+
+config CMD_OPTEE_RPMB
+ bool "Enable read/write support on RPMB via OPTEE"
+ depends on (SUPPORT_EMMC_RPMB && OPTEE) || SANDBOX_TEE
+ default y if SANDBOX_TEE
+ select OPTEE_TA_AVB if SANDBOX_TEE
+ help
+ Enable the commands for reading, writing persistent named values
+ in the Replay Protection Memory Block partition in eMMC by
+ using Persistent Objects in OPTEE
+
+config CMD_OPTEE
+ bool "Enable OP-TEE commands"
+ depends on OPTEE
+ help
+ OP-TEE commands support.
+
+config CMD_MTD
+ bool "mtd"
+ depends on MTD
+ select MTD_PARTITIONS
+ help
+ MTD commands support.
+
+config CMD_MTD_OTP
+ bool "mtd otp"
+ depends on CMD_MTD
+ select HEXDUMP
+ help
+ MTD commands for OTP access.
+
+config CMD_MUX
+ bool "mux"
+ depends on MULTIPLEXER
+ help
+ List, select, and deselect mux controllers on the fly.
+
+config CMD_NAND
+ bool "nand"
+ default y if NAND_SUNXI
+ depends on MTD_RAW_NAND
+ help
+ NAND support.
+
+if CMD_NAND
+config CMD_NAND_TRIMFFS
+ bool "nand write.trimffs"
+ default y if ARCH_SUNXI
+ help
+ Allows one to skip empty pages when flashing something on a NAND.
+
+config CMD_NAND_LOCK_UNLOCK
+ bool "nand lock/unlock"
+ help
+ NAND locking support.
+
+config CMD_NAND_TORTURE
+ bool "nand torture"
+ help
+ NAND torture support.
+
+config CMD_NAND_WATCH
+ bool "nand watch"
+ help
+ NAND watch bitflip support.
+
+endif # CMD_NAND
+
+config CMD_NVME
+ bool "nvme"
+ depends on NVME
+ default y if NVME
+ help
+ NVM Express device support
+
+config CMD_ONENAND
+ bool "onenand - access to onenand device"
+ depends on MTD
+ depends on !COMPILE_TEST
+ help
+ OneNAND is a brand of NAND ('Not AND' gate) flash which provides
+ various useful features. This command allows reading, writing,
+ and erasing blocks. It allso provides a way to show and change
+ bad blocks, and test the device.
+
+config USE_ONENAND_BOARD_INIT
+ bool "Call onenand_board_init() in the onenand command"
+ depends on CMD_ONENAND
+ depends on !COMPILE_TEST
+
+config CMD_OSD
+ bool "osd"
+ depends on OSD
+ help
+ Enable the 'osd' command which allows to query information from and
+ write text data to a on-screen display (OSD) device; a virtual device
+ associated with a display capable of displaying a text overlay on the
+ display it's associated with..
+
+config CMD_PART
+ bool "part"
+ depends on PARTITIONS
+ select PARTITION_UUIDS
+ help
+ Read and display information about the partition table on
+ various media.
+
+config CMD_PCI
+ bool "pci - Access PCI devices"
+ help
+ Provide access to PCI (Peripheral Interconnect Bus), a type of bus
+ used on some devices to allow the CPU to communicate with its
+ peripherals. Sub-commands allow bus enumeration, displaying and
+ changing configuration space and a few other features.
+
+config CMD_PCI_MPS
+ bool "pci_mps - Configure PCI device MPS"
+ depends on PCI
+ help
+ Enables PCI Express Maximum Packet Size (MPS) tuning. This
+ command configures the PCI Express MPS of each endpoint to the
+ largest value supported by all devices below the root complex.
+ The Maximum Read Request Size will not be altered. This method is
+ the same algorithm as used by Linux pci=pcie_bus_safe.
+
+config CMD_PINMUX
+ bool "pinmux - show pins muxing"
+ depends on PINCTRL
+ default y if PINCTRL
+ help
+ Parse all available pin-controllers and show pins muxing. This
+ is useful for debug purpoer to check the pin muxing and to know if
+ a pin is configured as a GPIO or as an alternate function.
+
+config CMD_POWEROFF
+ bool "poweroff"
+ help
+ Poweroff/Shutdown the system
+
+config CMD_READ
+ bool "read - Read binary data from a partition"
+ help
+ Provides low-level access to the data in a partition.
+
+config CMD_REMOTEPROC
+ bool "remoteproc"
+ depends on REMOTEPROC
+ default y if ARCH_K3
+ help
+ Support for Remote Processor control
+
+config CMD_SATA
+ bool "sata - Access SATA subsystem"
+ select SATA
+ help
+ SATA (Serial Advanced Technology Attachment) is a serial bus
+ standard for connecting to hard drives and other storage devices.
+ This command provides information about attached devices and allows
+ reading, writing and other operations.
+
+ SATA replaces PATA (originally just ATA), which stands for Parallel AT
+ Attachment, where AT refers to an IBM AT (Advanced Technology)
+ computer released in 1984.
+
+config CMD_SCSI
+ bool "scsi - Access to SCSI devices"
+ depends on SCSI
+ default y
+ help
+ This provides a 'scsi' command which provides access to SCSI (Small
+ Computer System Interface) devices. The command provides a way to
+ scan the bus, reset the bus, read and write data and get information
+ about devices.
+
+config CMD_SDRAM
+ bool "sdram - Print SDRAM configuration information"
+ help
+ Provides information about attached SDRAM. This assumed that the
+ SDRAM has an EEPROM with information that can be read using the
+ I2C bus. This is only available on some boards.
+
+config CMD_SF
+ bool "sf"
+ depends on DM_SPI_FLASH || SPI_FLASH
+ default y if DM_SPI_FLASH
+ help
+ SPI Flash support
+
+config CMD_SF_TEST
+ bool "sf test - Allow testing of SPI flash"
+ depends on CMD_SF
+ help
+ Provides a way to test that SPI flash is working correctly. The
+ test is destructive, in that an area of SPI flash must be provided
+ for the test to use. Performance information is also provided,
+ measuring the performance of reading, writing and erasing in
+ Mbps (Million Bits Per Second). This value should approximately
+ equal the SPI bus speed for a single-bit-wide SPI bus, assuming
+ everything is working properly.
+
+config CMD_SPI
+ bool "sspi - Command to access spi device"
+ depends on SPI
+ help
+ SPI utility command.
+
+config DEFAULT_SPI_BUS
+ int "default spi bus used by sspi command"
+ depends on CMD_SPI
+ default 0
+
+config DEFAULT_SPI_MODE
+ hex "default spi mode used by sspi command (see include/spi.h)"
+ depends on CMD_SPI
+ default 0x0
+
+config CMD_TEMPERATURE
+ bool "temperature - display the temperature from thermal sensors"
+ depends on DM_THERMAL
+ help
+ Provides a way to list thermal sensors and to get their readings.
+
+config CMD_UFS
+ bool "ufs - Universal Flash Storage commands"
+ depends on UFS
+ help
+ "This provides commands to initialise and configure universal flash
+ subsystem devices"
+
+config CMD_USB
+ bool "usb"
+ depends on USB_HOST
+ help
+ USB support.
+
+config CMD_USB_SDP
+ bool "sdp"
+ depends on USB_GADGET_DOWNLOAD
+ select USB_FUNCTION_SDP
+ help
+ Enables the command "sdp" which is used to have U-Boot emulating the
+ Serial Download Protocol (SDP) via USB.
+
+config CMD_RKMTD
+ bool "rkmtd"
+ select RKMTD
+ help
+ Enable the command "rkmtd" to create a virtual block device to transfer
+ Rockchip boot block data to and from NAND with block orientated tools
+ like "ums" and "rockusb".
+
+config CMD_ROCKUSB
+ bool "rockusb"
+ depends on USB_FUNCTION_ROCKUSB
+ help
+ Rockusb protocol is widely used by Rockchip SoC based devices. It can
+ read/write info, image to/from devices. This enable rockusb command
+ support to communication with rockusb device. for more detail about
+ this command, please read doc/README.rockusb.
+
+config CMD_USB_MASS_STORAGE
+ bool "UMS usb mass storage"
+ depends on BLK && USB_GADGET_DOWNLOAD
+ select USB_FUNCTION_MASS_STORAGE
+ help
+ Enables the command "ums" and the USB mass storage support to the
+ export a block device: U-Boot, the USB device, acts as a simple
+ external hard drive plugged on the host USB port.
+
+config CMD_UMS_ABORT_KEYED
+ bool "UMS abort with any key"
+ depends on CMD_USB_MASS_STORAGE
+ help
+ Allow interruption of usb mass storage run with any key pressed.
+
+config CMD_PVBLOCK
+ bool "Xen para-virtualized block device"
+ depends on XEN
+ select PVBLOCK
+ help
+ Xen para-virtualized block device support
+
+config CMD_VIRTIO
+ bool "virtio"
+ depends on VIRTIO
+ default y if VIRTIO
+ help
+ VirtIO block device support
+
+config CMD_WDT
+ bool "wdt"
+ depends on WDT
+ help
+ This provides commands to control the watchdog timer devices.
+
+config CMD_WRITE
+ bool "write - Write binary data to a partition"
+ help
+ Provides low-level write access to a partition.
+
+config CMD_AXI
+ bool "axi"
+ depends on AXI
+ help
+ Enable the command "axi" for accessing AXI (Advanced eXtensible
+ Interface) busses, a on-chip interconnect specification for managing
+ functional blocks in SoC designs, which is also often used in designs
+ involving FPGAs (e.g. communication with IP cores in Xilinx FPGAs).
+endmenu
+
+
+menu "Shell scripting commands"
+
+
+config CMD_C5_PL330_DMA
+ bool "Release Reset DMA Channels for PL330 Handshake"
+ depends on TARGET_SOCFPGA_CYCLONE5_SOCDK
+ help
+ Provides access to Reset Manager Per2ModRst. Enables DMA
+ channels for ARM PrimeCell PL330 via reset release.
+
+config CMD_CAT
+ bool "cat"
+ help
+ Print file to standard output
+
+config CMD_ECHO
+ bool "echo"
+ default y
+ help
+ Echo args to console
+
+config CMD_ITEST
+ bool "itest"
+ default y
+ help
+ Return true/false on integer compare.
+
+config CMD_SOURCE
+ bool "source"
+ default y
+ help
+ Run script from memory
+
+config CMD_SETEXPR
+ bool "setexpr"
+ default y
+ help
+ Evaluate boolean and math expressions and store the result in an env
+ variable.
+ Also supports loading the value at a memory location into a variable.
+ If CONFIG_REGEX is enabled, setexpr also supports a gsub function.
+
+config CMD_SETEXPR_FMT
+ bool "setexpr_fmt"
+ depends on CMD_SETEXPR
+ help
+ Evaluate format string expression and store result in an environment
+ variable.
+
+config CMD_XXD
+ bool "xxd"
+ help
+ Print file as hexdump to standard output
+
+endmenu
+
+if NET || NET_LWIP
+
+menuconfig CMD_NET
+ bool "Network commands"
+ default y
+
+if CMD_NET
+
+if NET
+
+config CMD_BOOTP
+ bool "bootp"
+ default y
+ help
+ bootp - boot image via network using BOOTP/TFTP protocol
+
+config CMD_DHCP6
+ bool "dhcp6"
+ depends on IPV6
+ help
+ Boot image via network using DHCPv6/TFTP protocol using IPv6.
+
+ Will perform 4-message exchange with DHCPv6 server, requesting
+ the minimum required options to TFTP boot. Complies with RFC 8415.
+
+if CMD_DHCP6
+
+config DHCP6_PXE_DHCP_OPTION
+ bool "Request & store 'pxe_configfile' from DHCP6 server"
+
+config DHCP6_ENTERPRISE_ID
+ int "Enterprise ID to send in DHCPv6 Vendor Class Option"
+ default 0
+
+endif
+
+config BOOTP_MAY_FAIL
+ bool "Allow for the BOOTP/DHCP server to not be found"
+ depends on CMD_BOOTP
+ help
+ If the DHCP server is not found after the configured retry count, the
+ call will fail instead of starting over. This can be used to fail
+ over to Link-local IP address configuration if the DHCP server is not
+ available.
+
+config BOOTP_BOOTPATH
+ bool "Request & store 'rootpath' from BOOTP/DHCP server"
+ default y
+ depends on CMD_BOOTP
+ help
+ Even though the config is called BOOTP_BOOTPATH, it stores the
+ path in the variable 'rootpath'.
+
+config BOOTP_VENDOREX
+ bool "Support vendor extensions from BOOTP/DHCP server"
+ depends on CMD_BOOTP
+
+config BOOTP_BOOTFILESIZE
+ bool "Request & store 'bootfilesize' from BOOTP/DHCP server"
+ depends on CMD_BOOTP
+
+config BOOTP_DNS
+ bool "Request & store 'dnsip' from BOOTP/DHCP server"
+ default y
+ depends on CMD_BOOTP
+ help
+ The primary DNS server is stored as 'dnsip'. If two servers are
+ returned, you must set BOOTP_DNS2 to store that second server IP
+ also.
+
+config BOOTP_DNS2
+ bool "Store 'dnsip2' from BOOTP/DHCP server"
+ depends on BOOTP_DNS
+ help
+ If a DHCP client requests the DNS server IP from a DHCP server,
+ it is possible that more than one DNS serverip is offered to the
+ client. If CONFIG_BOOTP_DNS2 is enabled, the secondary DNS
+ server IP will be stored in the additional environment
+ variable "dnsip2". The first DNS serverip is always
+ stored in the variable "dnsip", when BOOTP_DNS is defined.
+
+config BOOTP_GATEWAY
+ bool "Request & store 'gatewayip' from BOOTP/DHCP server"
+ default y
+ depends on CMD_BOOTP
+
+config BOOTP_HOSTNAME
+ bool "Request & store 'hostname' from BOOTP/DHCP server"
+ default y
+ depends on CMD_BOOTP
+ help
+ The name may or may not be qualified with the local domain name.
+
+config BOOTP_PREFER_SERVERIP
+ bool "serverip variable takes precedent over DHCP server IP."
+ depends on CMD_BOOTP
+ help
+ By default a BOOTP/DHCP reply will overwrite the 'serverip' variable.
+
+ With this option enabled, the 'serverip' variable in the environment
+ takes precedence over DHCP server IP and will only be set by the DHCP
+ server if not already set in the environment.
+
+config BOOTP_SUBNETMASK
+ bool "Request & store 'netmask' from BOOTP/DHCP server"
+ default y
+ depends on CMD_BOOTP
+
+config BOOTP_NISDOMAIN
+ bool "Request & store 'nisdomain' from BOOTP/DHCP server"
+ depends on CMD_BOOTP
+
+config BOOTP_NTPSERVER
+ bool "Request & store 'ntpserverip' from BOOTP/DHCP server"
+ depends on CMD_BOOTP
+
+config BOOTP_TIMEOFFSET
+ bool "Request & store 'timeoffset' from BOOTP/DHCP server"
+ depends on CMD_BOOTP && CMD_SNTP
+
+config CMD_PCAP
+ bool "pcap capture"
+ help
+ Selecting this will allow capturing all Ethernet packets and store
+ them in physical memory in a PCAP formated file,
+ later to be analyzed by PCAP reader application (IE. WireShark).
+
+config BOOTP_PXE
+ bool "Send PXE client arch to BOOTP/DHCP server"
+ default y
+ depends on CMD_BOOTP && CMD_PXE
+ help
+ Supported for ARM, ARM64, and x86 for now.
+
+config DHCP_PXE_CLIENTARCH
+ hex "DCHCP client system architecture type"
+ depends on BOOTP_PXE || CMD_DHCP6
+ default 0x16 if ARM64 # arm 64 uboot
+ default 0x15 if ARM # arm 32 uboot
+ default 0x0 if X86 # x86 BIOS
+ default 0xFF # DHCP option not sent
+ help
+ DHCP option 93 (defined in RFC4578) or DHCPv6 option 61 (defined in
+ RFC 5970) is used to transmit the client system architecture type
+ to the DHCP server. The DHCP server may use this information to
+ choose the boot file. For a complete list of assigned values see
+ https://www.iana.org/assignments/dhcpv6-parameters#processor-architecture.
+
+ If the value is set to the reserved value 0xFF, the DHCP option will
+ not be sent by U-Boot.
+
+config BOOTP_PXE_DHCP_OPTION
+ bool "Request & store 'pxe_configfile' from BOOTP/DHCP server"
+ default y
+ depends on BOOTP_PXE
+
+config BOOTP_VCI_STRING
+ string
+ depends on CMD_BOOTP
+ default "U-Boot.armv7" if CPU_V7A || CPU_V7M || CPU_V7R
+ default "U-Boot.armv8" if ARM64
+ default "U-Boot.arm" if ARM
+ default "U-Boot"
+
+config BOOTP_RANDOM_XID
+ bool "Send random transaction ID to BOOTP/DHCP server"
+ depends on CMD_BOOTP && (LIB_RAND || LIB_HW_RAND)
+ help
+ Selecting this will allow for a random transaction ID to get
+ selected for each BOOTP/DHCPv4 exchange.
+
+config CMD_TFTPPUT
+ bool "tftp put"
+ depends on CMD_TFTPBOOT
+ help
+ TFTP put command, for uploading files to a server
+
+config CMD_TFTPSRV
+ bool "tftpsrv"
+ depends on CMD_TFTPBOOT
+ help
+ Act as a TFTP server and boot the first received file
+
+config NET_TFTP_VARS
+ bool "Control TFTP timeout and count through environment"
+ depends on CMD_TFTPBOOT
+ default y
+ help
+ If set, allows controlling the TFTP timeout through the
+ environment variable tftptimeout, and the TFTP maximum
+ timeout count through the variable tftptimeoutcountmax.
+ If unset, timeout and maximum are hard-defined as 1 second
+ and 10 timouts per TFTP transfer.
+
+config CMD_RARP
+ bool "rarpboot"
+ help
+ Boot image via network using RARP/TFTP protocol
+
+config CMD_NFS
+ bool "nfs"
+ help
+ Boot image via network using NFS protocol.
+
+config NFS_TIMEOUT
+ int "Timeout in milliseconds for NFS mounts"
+ depends on CMD_NFS
+ default 2000
+ help
+ Timeout in milliseconds used in NFS protocol. If you encounter
+ "ERROR: Cannot umount" in nfs command, try longer timeout such as
+ 10000.
+
+config SYS_DISABLE_AUTOLOAD
+ bool "Disable automatically loading files over the network"
+ depends on CMD_BOOTP || CMD_DHCP || CMD_NFS || CMD_RARP
+ help
+ Typically, commands such as "dhcp" will attempt to automatically
+ load a file from the network, once the initial network configuration
+ is complete. Enable this option to disable this behavior and instead
+ require files to be loaded over the network by subsequent commands.
+
+config CMD_PING6
+ bool "ping6"
+ depends on IPV6
+ default y if (CMD_PING && IPV6)
+ help
+ Send ICMPv6 ECHO_REQUEST to network host
+
+config CMD_CDP
+ bool "cdp"
+ help
+ The cdp command is used to announce the U-Boot device in the network
+ and to retrieve configuration data including the VLAN id using the
+ proprietary Cisco Discovery Protocol (CDP).
+
+config CMD_LINK_LOCAL
+ bool "linklocal"
+ depends on (LIB_RAND || LIB_HW_RAND)
+ help
+ Acquire a network IP address using the link-local protocol
+
+config CMD_NCSI
+ bool "ncsi"
+ depends on PHY_NCSI
+ help
+ Manually configure the attached NIC via NC-SI.
+ Normally this happens automatically before other network
+ operations.
+
+config IPV6_ROUTER_DISCOVERY
+ bool "Do IPv6 router discovery"
+ depends on IPV6
+ help
+ Will automatically perform router solicitation on first IPv6
+ network operation
+
+config CMD_ETHSW
+ bool "ethsw"
+ help
+ Allow control of L2 Ethernet switch commands. These are supported
+ by the vsc9953 Ethernet driver at present. Sub-commands allow
+ operations such as enabling / disabling a port and
+ viewing/maintaining the filtering database (FDB)
+
+config CMD_WOL
+ bool "wol"
+ help
+ Wait for wake-on-lan Magic Packet
+
+endif # if NET
+
+config CMD_DHCP
+ bool "dhcp"
+ select PROT_DHCP_LWIP if NET_LWIP
+ help
+ Boot image via network using DHCP/TFTP protocol
+
+config CMD_DNS
+ bool "dns"
+ select DNS
+ help
+ Lookup the IP of a hostname
+
+config CMD_MII
+ bool "mii"
+ imply CMD_MDIO
+ help
+ If set, allows 802.3(clause 22) MII Management functions interface access
+ The management interface specified in Clause 22 provides
+ a simple, two signal, serial interface to connect a
+ Station Management entity and a managed PHY for providing access
+ to management parameters and services.
+ The interface is referred to as the MII management interface.
+
+config MII_INIT
+ bool "Call mii_init() in the mii command"
+ depends on CMD_MII && (MPC8XX_FEC || FSLDMAFE || MCFFEC)
+
+config CMD_MDIO
+ bool "mdio"
+ depends on PHYLIB
+ help
+ If set, allows Enable 802.3(clause 45) MDIO interface registers access
+ The MDIO interface is orthogonal to the MII interface and extends
+ it by adding access to more registers through indirect addressing.
+
+config CMD_PING
+ bool "ping"
+ select PROT_RAW_LWIP if NET_LWIP
+ help
+ Send ICMP ECHO_REQUEST to network host
+
+config CMD_SNTP
+ bool "sntp"
+ select PROT_UDP if NET
+ select PROT_UDP_LWIP if NET_LWIP
+ help
+ Synchronize RTC via network
+
+config CMD_TFTPBOOT
+ bool "tftp"
+ select PROT_UDP_LWIP if NET_LWIP
+ default y
+ help
+ tftpboot - load file via network using TFTP protocol
+
+config CMD_WGET
+ bool "wget"
+ default y if SANDBOX || ARCH_QEMU
+ select WGET
+ help
+ wget is a simple command to download kernel, or other files,
+ from a http server over TCP.
+
+config WGET_HTTPS
+ bool "wget https"
+ depends on CMD_WGET
+ depends on PROT_TCP_LWIP
+ depends on MBEDTLS_LIB
+ depends on DM_RNG
+ select SHA256
+ select RSA
+ select ASYMMETRIC_KEY_TYPE
+ select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ select X509_CERTIFICATE_PARSER
+ select PKCS7_MESSAGE_PARSER
+ select MBEDTLS_LIB_TLS
+ select RSA_VERIFY_WITH_PKEY
+ select X509_CERTIFICATE_PARSER
+ select PKCS7_MESSAGE_PARSER
+ help
+ Enable TLS over http for wget.
+
+config WGET_CACERT
+ bool "wget cacert"
+ depends on CMD_WGET
+ depends on WGET_HTTPS
+ help
+ Adds the "cacert" sub-command to wget to provide root certificates
+ to the HTTPS engine. Must be in DER format.
+
+config WGET_BUILTIN_CACERT
+ bool "Built-in CA certificates"
+ depends on WGET_HTTPS
+ select BUILD_BIN2C
+
+config WGET_BUILTIN_CACERT_PATH
+ string "Path to root certificates"
+ depends on WGET_BUILTIN_CACERT
+ default "cacert.crt"
+ help
+ Set this to the path to a DER-encoded X509 file containing
+ Certification Authority certificates, a.k.a. root certificates, for
+ the purpose of authenticating HTTPS connections.
+
+config CMD_PXE
+ bool "pxe"
+ select PXE_UTILS
+ imply CMD_TFTPBOOT
+ help
+ Boot image via network using PXE protocol
+
+endif # if CMD_NET
+
+endif # NET || NET_LWIP
+
+menu "Misc commands"
+
+config CMD_2048
+ bool "Play 2048"
+ help
+ This is a simple sliding block puzzle game designed by Italian web
+ developer Gabriele Cirulli. The game's objective is to slide numbered
+ tiles on a grid to combine them to create a tile with the number
+ 2048.
+
+ This needs ANSI support on your terminal to work. It is not fully
+ functional on a video device.
+
+config CMD_BMP
+ bool "Enable 'bmp' command"
+ depends on VIDEO
+ select BMP
+ help
+ This provides a way to obtain information about a BMP-format image
+ and to display it. BMP (which presumably stands for BitMaP) is a
+ file format defined by Microsoft which supports images of various
+ depths, formats and compression methods. Headers on the file
+ determine the formats used. This command can be used by first loading
+ the image into RAM, then using this command to look at it or display
+ it.
+
+config CMD_BOOTCOUNT
+ bool "bootcount"
+ depends on BOOTCOUNT_LIMIT
+ help
+ Enable the bootcount command, which allows interrogation and
+ reset of the bootcounter.
+
+config CMD_BSP
+ bool "Enable board-specific commands"
+ help
+ (deprecated: instead, please define a Kconfig option for each command)
+
+ Some boards have board-specific commands which are only enabled
+ during developemnt and need to be turned off for production. This
+ option provides a way to control this. The commands that are enabled
+ vary depending on the board.
+
+config CMD_BLOCK_CACHE
+ bool "blkcache - control and stats for block cache"
+ depends on BLOCK_CACHE
+ default y if BLOCK_CACHE
+ help
+ Enable the blkcache command, which can be used to control the
+ operation of the cache functions.
+ This is most useful when fine-tuning the operation of the cache
+ during development, but also allows the cache to be disabled when
+ it might hurt performance (e.g. when using the ums command).
+
+config CMD_BLKMAP
+ bool "blkmap - Composable virtual block devices"
+ depends on BLKMAP
+ default y if BLKMAP
+ help
+ Create virtual block devices that are backed by various sources,
+ e.g. RAM, or parts of an existing block device. Though much more
+ rudimentary, it borrows a lot of ideas from Linux's device mapper
+ subsystem.
+
+ Example use-cases:
+ - Treat a region of RAM as a block device, i.e. a RAM disk. This let's
+ you extract files from filesystem images stored in RAM (perhaps as a
+ result of a TFTP transfer).
+ - Create a virtual partition on an existing device. This let's you
+ access filesystems that aren't stored at an exact partition
+ boundary. A common example is a filesystem image embedded in an FIT
+ image.
+
+config CMD_BUTTON
+ bool "button"
+ depends on BUTTON
+ default y if BUTTON
+ help
+ Enable the 'button' command which allows to get the status of
+ buttons supported by the board. The buttonss can be listed with
+ 'button list' and state can be known with 'button <label>'.
+ Any button drivers can be controlled with this command, e.g.
+ button_gpio.
+
+config CMD_CACHE
+ bool "icache or dcache"
+ help
+ Enable the "icache" and "dcache" commands
+
+config CMD_CONITRACE
+ bool "conitrace - trace console input codes"
+ help
+ Enable the 'conitrace' command which displays the codes received
+ from the console input as hexadecimal numbers.
+
+config CMD_CLS
+ bool "Enable clear screen command 'cls'"
+ default y if LCD || VIDEO
+ help
+ Enable the 'cls' command which clears the screen contents
+ on video frame buffer.
+
+config CMD_EFIDEBUG
+ bool "efidebug - display/configure UEFI environment"
+ depends on EFI_LOADER
+ select EFI_DEVICE_PATH_TO_TEXT
+ help
+ Enable the 'efidebug' command which provides a subset of UEFI
+ shell utility with simplified functionality. It will be useful
+ particularly for managing boot parameters as well as examining
+ various EFI status for debugging.
+
+config CMD_EFICONFIG
+ bool "eficonfig - provide menu-driven uefi variables maintenance interface"
+ default y if !HAS_BOARD_SIZE_LIMIT
+ depends on EFI_BOOTMGR
+ select MENU
+ help
+ Enable the 'eficonfig' command which provides the menu-driven UEFI
+ variable maintenance interface.
+
+config CMD_EXCEPTION
+ bool "exception - raise exception"
+ depends on ARM || RISCV || SANDBOX || X86
+ help
+ Enable the 'exception' command which allows to raise an exception.
+
+config CMD_LED
+ bool "led"
+ depends on LED
+ default y if LED
+ help
+ Enable the 'led' command which allows for control of LEDs supported
+ by the board. The LEDs can be listed with 'led list' and controlled
+ with led on/off/togle/blink. Any LED drivers can be controlled with
+ this command, e.g. led_gpio.
+
+config CMD_INI
+ bool "ini"
+ help
+ Enable the 'ini' command which allows a .ini file to be parsed and
+ placed into environment variables. Please check the source code for
+ this as there is no documentation.
+
+config CMD_DATE
+ bool "date"
+ default y if DM_RTC
+ select LIB_DATE
+ help
+ Enable the 'date' command for getting/setting the time/date in RTC
+ devices.
+
+config CMD_RTC
+ bool "rtc"
+ depends on DM_RTC
+ default y if X86
+ help
+ Enable the 'rtc' command for low-level access to RTC devices.
+
+config CMD_TIME
+ bool "time"
+ help
+ Run commands and summarize execution time.
+
+config CMD_GETTIME
+ bool "gettime - read elapsed time"
+ help
+ Enable the 'gettime' command which reads the elapsed time since
+ U-Boot started running. This shows the time in seconds and
+ milliseconds. See also the 'bootstage' command which provides more
+ flexibility for boot timing.
+
+config CMD_PAUSE
+ bool "pause command"
+ help
+ Delay execution waiting for any user input.
+ Useful to allow the user to read a failure log.
+
+config CMD_RNG
+ bool "rng command"
+ depends on DM_RNG
+ default y if SANDBOX
+ select HEXDUMP
+ help
+ Print bytes from the hardware random number generator.
+
+config CMD_KASLRSEED
+ bool "kaslrseed"
+ depends on DM_RNG
+ help
+ Set the kaslr-seed in the chosen node with entropy provided by a
+ hardware random number generator.
+
+config CMD_SLEEP
+ bool "sleep"
+ default y
+ help
+ Delay execution for some time
+
+config CMD_MP
+ bool "support for multiprocessor commands"
+ depends on MP && !CPU
+ default y
+ help
+ This enables commands to bringup different processors
+ in multiprocessor cases.
+
+config CMD_TIMER
+ bool "timer"
+ help
+ Access the system timer.
+
+config CMD_SOUND
+ bool "sound"
+ depends on SOUND
+ help
+ This provides basic access to the U-Boot's sound support. The main
+ feature is to play a beep.
+
+ sound init - set up sound system
+ sound play - play a sound
+
+config CMD_SYSBOOT
+ bool "sysboot"
+ select PXE_UTILS
+ help
+ Boot image via local extlinux.conf file
+
+config CMD_QFW
+ bool "qfw"
+ select QFW
+ default y if TARGET_QEMU_ARM_32BIT || TARGET_QEMU_ARM_64BIT || \
+ TARGET_QEMU_X86 || TARGET_QEMU_X86_64
+ help
+ This provides access to the QEMU firmware interface. The main
+ feature is to allow easy loading of files passed to qemu-system
+ via -kernel / -initrd
+
+config CMD_PSTORE
+ bool "pstore"
+ help
+ This provides access to Linux PStore with Rammoops backend. The main
+ feature is to allow to display or save PStore records.
+
+ See doc/pstore.rst for more information.
+
+if CMD_PSTORE
+
+config CMD_PSTORE_MEM_ADDR
+ hex "Memory Address"
+ depends on CMD_PSTORE
+ help
+ Base addr used for PStore ramoops memory, should be identical to
+ ramoops.mem_address parameter used by kernel
+
+config CMD_PSTORE_MEM_SIZE
+ hex "Memory size"
+ depends on CMD_PSTORE
+ default "0x10000"
+ help
+ Size of PStore ramoops memory, should be identical to ramoops.mem_size
+ parameter used by kernel, a power of 2 and larger than the sum of the
+ record sizes
+
+config CMD_PSTORE_RECORD_SIZE
+ hex "Dump record size"
+ depends on CMD_PSTORE
+ default "0x1000"
+ help
+ Size of each dump done on oops/panic, should be identical to
+ ramoops.record_size parameter used by kernel and a power of 2
+ Must be non-zero
+
+config CMD_PSTORE_CONSOLE_SIZE
+ hex "Kernel console log size"
+ depends on CMD_PSTORE
+ default "0x1000"
+ help
+ Size of kernel console log, should be identical to
+ ramoops.console_size parameter used by kernel and a power of 2
+ Must be non-zero
+
+config CMD_PSTORE_FTRACE_SIZE
+ hex "FTrace log size"
+ depends on CMD_PSTORE
+ default "0x1000"
+ help
+ Size of ftrace log, should be identical to ramoops.ftrace_size
+ parameter used by kernel and a power of 2
+
+config CMD_PSTORE_PMSG_SIZE
+ hex "User space message log size"
+ depends on CMD_PSTORE
+ default "0x1000"
+ help
+ Size of user space message log, should be identical to
+ ramoops.pmsg_size parameter used by kernel and a power of 2
+
+config CMD_PSTORE_ECC_SIZE
+ int "ECC size"
+ depends on CMD_PSTORE
+ default "0"
+ help
+ if non-zero, the option enables ECC support and specifies ECC buffer
+ size in bytes (1 is a special value, means 16 bytes ECC), should be
+ identical to ramoops.ramoops_ecc parameter used by kernel
+
+endif
+
+source "cmd/mvebu/Kconfig"
+
+config CMD_TERMINAL
+ bool "terminal - provides a way to attach a serial terminal"
+ help
+ Provides a 'cu'-like serial terminal command. This can be used to
+ access other serial ports from the system console. The terminal
+ is very simple with no special processing of characters. As with
+ cu, you can press ~. (tilde followed by period) to exit.
+
+config CMD_UUID
+ bool "uuid, guid - generation of unique IDs"
+ select LIB_UUID
+ help
+ This enables two commands:
+
+ uuid - generate random Universally Unique Identifier
+ guid - generate Globally Unique Identifier based on random UUID
+
+ The two commands are very similar except for the endianness of the
+ output.
+
+config CMD_VIDCONSOLE
+ bool "lcdputs and setcurs"
+ depends on VIDEO
+ default y
+ help
+ Enabling this will provide 'setcurs' and 'lcdputs' commands which
+ support cursor positioning and drawing strings on the video
+ console (framebuffer).
+
+ The name 'lcdputs' is a bit of a misnomer, but so named because the
+ video device is often an LCD.
+
+config CMD_SELECT_FONT
+ bool "select font size"
+ depends on VIDEO
+ default y if CONSOLE_TRUETYPE
+ help
+ Enabling this will provide 'font' command.
+ Allows font selection at runtime.
+
+endmenu
+
+source "cmd/ti/Kconfig"
+
+config CMD_BOOTSTAGE
+ bool "Enable the 'bootstage' command"
+ depends on BOOTSTAGE
+ help
+ Add a 'bootstage' command which supports printing a report
+ and un/stashing of bootstage data.
+
+menu "Power commands"
+config CMD_PMIC
+ bool "Enable Driver Model PMIC command"
+ depends on DM_PMIC
+ help
+ This is the pmic command, based on a driver model pmic's API.
+ Command features are unchanged:
+ - list - list pmic devices
+ - pmic dev <id> - show or [set] operating pmic device (NEW)
+ - pmic dump - dump registers
+ - pmic read address - read byte of register at address
+ - pmic write address - write byte to register at address
+ The only one change for this command is 'dev' subcommand.
+
+config CMD_REGULATOR
+ bool "Enable Driver Model REGULATOR command"
+ depends on DM_REGULATOR
+ help
+ This command is based on driver model regulator's API.
+ User interface features:
+ - list - list regulator devices
+ - regulator dev <id> - show or [set] operating regulator device
+ - regulator info - print constraints info
+ - regulator status - print operating status
+ - regulator value <val] <-f> - print/[set] voltage value [uV]
+ - regulator current <val> - print/[set] current value [uA]
+ - regulator mode <id> - print/[set] operating mode id
+ - regulator enable - enable the regulator output
+ - regulator disable - disable the regulator output
+
+ The '-f' (force) option can be used for set the value which exceeds
+ the limits, which are found in device-tree and are kept in regulator's
+ uclass plat structure.
+
+endmenu
+
+menu "Security commands"
+config CMD_AES
+ bool "Enable the 'aes' command"
+ select AES
+ help
+ This provides a means to encrypt and decrypt data using the AES
+ (Advanced Encryption Standard). This algorithm uses a symetric key
+ and is widely used as a streaming cipher. Different key lengths are
+ supported by the algorithm but this command only supports 128 bits
+ at present.
+
+config CMD_BLOB
+ bool "Enable the 'blob' command"
+ depends on !MX6ULL && !MX6SLL && !MX6SL
+ depends on SYS_FSL_SEC_COMPAT >= 4
+ select IMX_HAB if ARCH_MX6 || ARCH_MX7 || ARCH_MX7ULP || ARCH_IMX8M
+ help
+ This is used with the Freescale secure boot mechanism.
+
+ Freescale's SEC block has built-in Blob Protocol which provides
+ a method for protecting user-defined data across system power
+ cycles. SEC block protects data in a data structure called a Blob,
+ which provides both confidentiality and integrity protection.
+
+ Encapsulating data as a blob
+ Each time that the Blob Protocol is used to protect data, a
+ different randomly generated key is used to encrypt the data.
+ This random key is itself encrypted using a key which is derived
+ from SoC's non-volatile secret key and a 16 bit Key identifier.
+ The resulting encrypted key along with encrypted data is called a
+ blob. The non-volatile secure key is available for use only during
+ secure boot.
+
+ During decapsulation, the reverse process is performed to get back
+ the original data.
+
+ Sub-commands:
+ blob enc - encapsulating data as a cryptgraphic blob
+ blob dec - decapsulating cryptgraphic blob to get the data
+
+ Syntax:
+
+ blob enc src dst len km
+
+ Encapsulate and create blob of data $len bytes long
+ at address $src and store the result at address $dst.
+ $km is the 16 byte key modifier is also required for
+ generation/use as key for cryptographic operation. Key
+ modifier should be 16 byte long.
+
+ blob dec src dst len km
+
+ Decapsulate the blob of data at address $src and
+ store result of $len byte at addr $dst.
+ $km is the 16 byte key modifier is also required for
+ generation/use as key for cryptographic operation. Key
+ modifier should be 16 byte long.
+
+config CMD_HASH
+ bool "Support 'hash' command"
+ select HASH
+ help
+ This provides a way to hash data in memory using various supported
+ algorithms (such as SHA1, MD5, CRC32). The computed digest can be
+ saved to memory or to an environment variable. It is also possible
+ to verify a hash against data in memory.
+
+config CMD_HVC
+ bool "Support the 'hvc' command"
+ depends on ARM_SMCCC
+ help
+ Allows issuing Hypervisor Calls (HVCs). Mostly useful for
+ development and testing.
+
+config CMD_SMC
+ bool "Support the 'smc' command"
+ depends on ARM_SMCCC
+ help
+ Allows issuing Secure Monitor Calls (SMCs). Mostly useful for
+ development and testing.
+
+config HASH_VERIFY
+ bool "hash -v"
+ depends on CMD_HASH
+ help
+ Add -v option to verify data against a hash.
+
+config CMD_SCP03
+ bool "scp03 - SCP03 enable and rotate/provision operations"
+ depends on SCP03
+ help
+ This command provides access to a Trusted Application
+ running in a TEE to request Secure Channel Protocol 03
+ (SCP03) enablement and/or rotation of its SCP03 keys.
+
+config CMD_TPM_V1
+ bool
+
+config CMD_TPM_V2
+ bool
+
+config CMD_TPM
+ bool "Enable the 'tpm' command"
+ depends on TPM_V1 || TPM_V2
+ select CMD_TPM_V1 if TPM_V1
+ select CMD_TPM_V2 if TPM_V2
+ help
+ This provides a means to talk to a TPM from the command line. A wide
+ range of commands if provided - see 'tpm help' for details. The
+ command requires a suitable TPM on your board and the correct driver
+ must be enabled.
+
+if CMD_TPM
+
+config CMD_TPM_TEST
+ bool "Enable the 'tpm test' command"
+ depends on TPM_V1
+ help
+ This provides a a series of tests to confirm that the TPMv1.x is
+ working correctly. The tests cover initialisation, non-volatile RAM,
+ extend, global lock and checking that timing is within expectations.
+ The tests pass correctly on Infineon TPMs but may need to be adjusted
+ for other devices.
+
+endif
+
+endmenu
+
+menu "Firmware commands"
+config CMD_CROS_EC
+ bool "Enable crosec command"
+ depends on CROS_EC
+ default y
+ help
+ Enable command-line access to the Chrome OS EC (Embedded
+ Controller). This provides the 'crosec' command which has
+ a number of sub-commands for performing EC tasks such as
+ updating its flash, accessing a small saved context area
+ and talking to the I2C bus behind the EC (if there is one).
+
+config CMD_SCMI
+ bool "Enable scmi command"
+ depends on SCMI_FIRMWARE
+ help
+ This command provides user interfaces to several SCMI (System
+ Control and Management Interface) protocols available on Arm
+ platforms to manage system resources.
+endmenu
+
+menu "Filesystem commands"
+config CMD_BTRFS
+ bool "Enable the 'btrsubvol' command"
+ select FS_BTRFS
+ help
+ This enables the 'btrsubvol' command to list subvolumes
+ of a BTRFS filesystem. There are no special commands for
+ listing BTRFS directories or loading BTRFS files - this
+ can be done by the generic 'fs' commands (see CMD_FS_GENERIC)
+ when BTRFS is enabled (see FS_BTRFS).
+
+config CMD_CBFS
+ bool "Enable the 'cbfs' command"
+ depends on FS_CBFS
+ help
+ Define this to enable support for reading from a Coreboot
+ filesystem. This is a ROM-based filesystem used for accessing files
+ on systems that use coreboot as the first boot-loader and then load
+ U-Boot to actually boot the Operating System. Available commands are
+ cbfsinit, cbfsinfo, cbfsls and cbfsload.
+
+config CMD_CRAMFS
+ bool "Enable the 'cramfs' command"
+ depends on FS_CRAMFS
+ help
+ This provides commands for dealing with CRAMFS (Compressed ROM
+ filesystem). CRAMFS is useful when space is tight since files are
+ compressed. Two commands are provided:
+
+ cramfsls - lists files in a cramfs image
+ cramfsload - loads a file from a cramfs image
+
+config CMD_EROFS
+ bool "EROFS command support"
+ select FS_EROFS
+ help
+ Support for the EROFS fs
+
+config CMD_EXT2
+ bool "ext2 command support"
+ select FS_EXT4
+ help
+ Enables EXT2 FS command
+
+config CMD_EXT4
+ bool "ext4 command support"
+ select FS_EXT4
+ help
+ Enables EXT4 FS command
+
+config CMD_EXT4_WRITE
+ depends on CMD_EXT4
+ bool "ext4 write command support"
+ select EXT4_WRITE
+ help
+ Enables EXT4 FS write command
+
+config CMD_FAT
+ bool "FAT command support"
+ select FS_FAT
+ help
+ Support for the FAT fs
+
+config CMD_SQUASHFS
+ bool "SquashFS command support"
+ select FS_SQUASHFS
+ help
+ Enables SquashFS filesystem commands (e.g. load, ls).
+
+config CMD_FS_GENERIC
+ bool "filesystem commands"
+ help
+ Enables filesystem commands (e.g. load, ls) that work for multiple
+ fs types.
+
+config CMD_FS_UUID
+ bool "fsuuid command"
+ help
+ Enables fsuuid command for filesystem UUID.
+
+config CMD_JFFS2
+ bool "jffs2 command"
+ select FS_JFFS2
+ help
+ Enables commands to support the JFFS2 (Journalling Flash File System
+ version 2) filesystem. This enables fsload, ls and fsinfo which
+ provide the ability to load files, list directories and obtain
+ filesystem information.
+
+config JFFS2_DEV
+ string "Default device for JFFS2"
+ depends on CMD_JFFS2
+ default "nor0"
+ help
+ The default device to use with the jffs2 command.
+
+config JFFS2_PART_OFFSET
+ hex "Default offset within flash to locate the JFFS2 image"
+ depends on CMD_JFFS2
+ default 0x0
+ help
+ The default offset within flash to locate the JFFS2 image.
+
+config JFFS2_PART_SIZE
+ hex "Default size of JFFS2 partition"
+ depends on CMD_JFFS2
+ default 0xFFFFFFFF
+ help
+ The default size of the JFFS2 partition
+
+config CMD_MTDPARTS
+ bool "MTD partition support"
+ depends on !COMPILE_TEST
+ depends on MTD
+ select MTD_PARTITIONS
+ help
+ MTD partitioning tool support.
+ It is strongly encouraged to avoid using this command
+ anymore along with 'sf', 'nand', 'onenand'. One can still
+ declare the partitions in the mtdparts environment variable
+ but better use the MTD stack and the 'mtd' command instead.
+
+config CMD_MTDPARTS_SPREAD
+ bool "Padd partition size to take account of bad blocks"
+ depends on CMD_MTDPARTS
+ help
+ This enables the 'spread' sub-command of the mtdparts command.
+ This command will modify the existing mtdparts variable by increasing
+ the size of the partitions such that 1) each partition's net size is
+ at least as large as the size specified in the mtdparts variable and
+ 2) each partition starts on a good block.
+
+config CMD_MTDPARTS_SHOW_NET_SIZES
+ bool "Show net size (w/o bad blocks) of partitions"
+ depends on CMD_MTDPARTS
+ help
+ Adds two columns to the printed partition table showing the
+ effective usable size of a partition, if bad blocks are taken
+ into account.
+
+config MTDIDS_DEFAULT
+ string "Default MTD IDs"
+ depends on MTD || SPI_FLASH
+ depends on !SYS_MTDPARTS_RUNTIME
+ help
+ Defines a default MTD IDs list for use with MTD partitions in the
+ Linux MTD command line partitions format.
+
+config MTDPARTS_DEFAULT
+ string "Default MTD partition scheme"
+ depends on MTD || SPI_FLASH
+ depends on !SYS_MTDPARTS_RUNTIME
+ help
+ Defines a default MTD partitioning scheme in the Linux MTD command
+ line partitions format
+
+config CMD_ZFS
+ bool "zfs - Access of ZFS filesystem"
+ help
+ This provides commands to accessing a ZFS filesystem, commonly used
+ on Solaris systems. Two sub-commands are provided:
+
+ zfsls - list files in a directory
+ zfsload - load a file
+
+ See doc/README.zfs for more details.
+
+endmenu
+
+menu "Debug commands"
+
+config CMD_CBSYSINFO
+ bool "cbsysinfo"
+ depends on X86
+ default y if SYS_COREBOOT
+ help
+ This provides information about the coreboot sysinfo table stored in
+ memory by coreboot before jumping to U-Boot. It can be useful for
+ debugging the beaaviour of coreboot or U-Boot.
+
+config CMD_CBCMOS
+ bool "cbcmos"
+ depends on X86
+ default y if SYS_COREBOOT
+ help
+ This provides information options to check the CMOS RAM checksum,
+ if present, as well as to update it.
+
+ It is useful when coreboot CMOS-RAM settings must be examined or
+ updated.
+
+config CMD_CYCLIC
+ bool "cyclic - Show information about cyclic functions"
+ depends on CYCLIC
+ default y
+ help
+ This enables the 'cyclic' command which provides information about
+ cyclic execution functions. This infrastructure allows registering
+ functions to be executed cyclically, e.g. every 100ms. These commands
+ are supported:
+
+ cyclic list - list cyclic functions
+ cyclic cyclic demo <cycletime_ms> <delay_us> - register cyclic
+ demo function
+
+ See doc/develop/cyclic.rst for more details.
+
+config CMD_DIAG
+ bool "diag - Board diagnostics"
+ help
+ This command provides access to board diagnostic tests. These are
+ called Power-on Self Tests (POST). The command allows listing of
+ available tests and running either all the tests, or specific tests
+ identified by name.
+
+config CMD_EVENT
+ bool "event - Show information about events"
+ depends on EVENT
+ default y if EVENT_DEBUG
+ help
+ This enables the 'event' command which provides information about
+ events and event-handler routines. This can help to device event
+ hadling.
+
+config CMD_IRQ
+ bool "irq - Show information about interrupts"
+ depends on NIOS2 || PPC || X86
+ help
+ This enables two commands:
+
+ interrupts - enable or disable interrupts
+ irqinfo - print device-specific interrupt information
+
+config CMD_KGDB
+ bool "kgdb - Allow debugging of U-Boot with gdb"
+ depends on PPC
+ help
+ This enables a 'kgdb' command which allows gdb to connect to U-Boot
+ over a serial link for debugging purposes. This allows
+ single-stepping, inspecting variables, etc. This is supported only
+ on PowerPC at present.
+
+config CMD_LOG
+ bool "log - Generation, control and access to logging"
+ select LOG
+ select GETOPT
+ help
+ This provides access to logging features. It allows the output of
+ log data to be controlled to a limited extent (setting up the default
+ maximum log level for emitting of records). It also provides access
+ to a command used for testing the log system.
+
+config CMD_TRACE
+ bool "trace - Support tracing of function calls and timing"
+ depends on TRACE
+ default y
+ help
+ Enables a command to control using of function tracing within
+ U-Boot. This allows recording of call traces including timing
+ information. The command can write data to memory for exporting
+ for analysis (e.g. using bootchart). See doc/develop/trace.rst
+ for full details.
+
+config CMD_AVB
+ bool "avb - Android Verified Boot 2.0 operations"
+ depends on AVB_VERIFY
+ help
+ Enables a "avb" command to perform verification of partitions using
+ Android Verified Boot 2.0 functionality. It includes such subcommands:
+ avb init - initialize avb2 subsystem
+ avb read_rb - read rollback index
+ avb write_rb - write rollback index
+ avb is_unlocked - check device lock state
+ avb get_uuid - read and print uuid of a partition
+ avb read_part - read data from partition
+ avb read_part_hex - read data from partition and output to stdout
+ avb write_part - write data to partition
+ avb verify - run full verification chain
+
+config CMD_STACKPROTECTOR_TEST
+ bool "Test command for stack protector"
+ depends on STACKPROTECTOR
+ help
+ Enable stackprot_test command
+ The stackprot_test command will force a stack overrun to test
+ the stack smashing detection mechanisms.
+
+endmenu
+
+config CMD_UBI
+ tristate "Enable UBI - Unsorted block images commands"
+ select MTD_UBI
+ help
+ UBI is a software layer above MTD layer which admits use of LVM-like
+ logical volumes on top of MTD devices, hides some complexities of
+ flash chips like wear and bad blocks and provides some other useful
+ capabilities. Please, consult the MTD web site for more details
+ (www.linux-mtd.infradead.org). Activate this option if you want
+ to use U-Boot UBI commands.
+ It is also strongly encouraged to also enable CONFIG_MTD to get full
+ partition support.
+
+config CMD_UBI_RENAME
+ bool "Enable rename"
+ depends on CMD_UBI
+ help
+ Enable a "ubi" command to rename ubi volume:
+ ubi rename <oldname> <newname>
+
+config CMD_UBIFS
+ tristate "Enable UBIFS - Unsorted block images filesystem commands"
+ depends on CMD_UBI
+ default y if CMD_UBI
+ select LZO
+ select GZIP
+ help
+ UBIFS is a file system for flash devices which works on top of UBI.
+
+config CMD_MESON
+ bool "Amlogic Meson commands"
+ depends on ARCH_MESON
+ default y
+ help
+ Enable useful commands for the Meson Soc family developed by Amlogic Inc.
+
+config CMD_SPAWN
+ bool "spawn and wait commands"
+ depends on UTHREAD
+ help
+ spawn runs a command in the background and sets the job_id environment
+ variable. wait is used to suspend the shell execution until one or more
+ jobs are complete.
+
+config CMD_SPAWN_NUM_JOBS
+ int "Maximum number of simultaneous jobs for spawn"
+ default 16
+ help
+ Job identifiers are in the range 1..CMD_SPAWN_NUM_JOBS. In other words
+ there can be no more that CMD_SPAWN_NUM_JOBS running simultaneously.
+ When a jobs exits, its identifier is available to be re-used by the next
+ spawn command.
+
+endif
diff --git a/cmd/Makefile b/cmd/Makefile
new file mode 100644
index 00000000000..25479907797
--- /dev/null
+++ b/cmd/Makefile
@@ -0,0 +1,301 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2004-2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+
+ifndef CONFIG_XPL_BUILD
+# core command
+obj-y += boot.o
+obj-$(CONFIG_CMD_BOOTM) += bootm.o
+obj-$(CONFIG_CMD_HELP) += help.o
+obj-y += panic.o
+obj-y += version.o
+
+# command
+obj-$(CONFIG_CMD_ARMFFA) += armffa.o
+obj-$(CONFIG_CMD_2048) += 2048.o
+obj-$(CONFIG_CMD_ACPI) += acpi.o
+obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o
+obj-$(CONFIG_CMD_AES) += aes.o
+obj-$(CONFIG_CMD_ADC) += adc.o
+obj-$(CONFIG_CMD_ARMFLASH) += armflash.o
+obj-$(CONFIG_BLK) += blk_common.o
+obj-$(CONFIG_CMD_BOOTDEV) += bootdev.o
+obj-$(CONFIG_CMD_BOOTFLOW) += bootflow.o
+obj-$(CONFIG_CMD_BOOTMETH) += bootmeth.o
+obj-$(CONFIG_CMD_BOOTSTD) += bootstd.o
+obj-$(CONFIG_CMD_SOURCE) += source.o
+obj-$(CONFIG_CMD_BCB) += bcb.o
+obj-$(CONFIG_CMD_BDI) += bdinfo.o
+obj-$(CONFIG_CMD_BIND) += bind.o
+obj-$(CONFIG_CMD_BINOP) += binop.o
+obj-$(CONFIG_CMD_BLKMAP) += blkmap.o
+obj-$(CONFIG_CMD_BLOBLIST) += bloblist.o
+obj-$(CONFIG_CMD_BLOCK_CACHE) += blkcache.o
+obj-$(CONFIG_CMD_BMP) += bmp.o
+obj-$(CONFIG_CMD_BOOTCOUNT) += bootcount.o
+obj-$(CONFIG_CMD_BOOTEFI) += bootefi.o
+obj-$(CONFIG_CMD_BOOTMENU) += bootmenu.o
+obj-$(CONFIG_CMD_BOOTSTAGE) += bootstage.o
+obj-$(CONFIG_CMD_BOOTZ) += bootz.o
+obj-$(CONFIG_CMD_BOOTI) += booti.o
+obj-$(CONFIG_CMD_BTRFS) += btrfs.o
+obj-$(CONFIG_CMD_BUTTON) += button.o
+obj-$(CONFIG_CMD_C5_PL330_DMA) += c5_pl330_dma.o
+obj-$(CONFIG_CMD_CAT) += cat.o
+obj-$(CONFIG_CMD_CACHE) += cache.o
+obj-$(CONFIG_CMD_CBFS) += cbfs.o
+obj-$(CONFIG_CMD_CEDIT) += cedit.o
+obj-$(CONFIG_CMD_CLK) += clk.o
+obj-$(CONFIG_CMD_CLS) += cls.o
+obj-$(CONFIG_CMD_CONFIG) += config.o
+obj-$(CONFIG_CMD_CONITRACE) += conitrace.o
+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
+obj-$(CONFIG_CMD_ADTIMG) += adtimg.o
+obj-$(CONFIG_CMD_ABOOTIMG) += abootimg.o
+obj-$(CONFIG_CMD_CYCLIC) += cyclic.o
+obj-$(CONFIG_CMD_EVENT) += event.o
+obj-$(CONFIG_CMD_EXTENSION) += extension_board.o
+obj-$(CONFIG_CMD_ECHO) += echo.o
+obj-$(CONFIG_ENV_IS_IN_EEPROM) += eeprom.o
+obj-$(CONFIG_CMD_EEPROM) += eeprom.o
+obj-$(CONFIG_EFI_CLIENT) += efi.o efi_common.o
+obj-$(CONFIG_CMD_EFIDEBUG) += efidebug.o efi_common.o
+obj-$(CONFIG_CMD_EFICONFIG) += eficonfig.o
+ifdef CONFIG_CMD_EFICONFIG
+ifdef CONFIG_EFI_MM_COMM_TEE
+obj-$(CONFIG_EFI_SECURE_BOOT) += eficonfig_sbkey.o
+endif
+endif
+obj-$(CONFIG_CMD_ELF) += elf.o
+obj-$(CONFIG_CMD_EROFS) += erofs.o
+obj-$(CONFIG_HUSH_PARSER) += exit.o
+obj-$(CONFIG_CMD_EXT4) += ext4.o
+obj-$(CONFIG_CMD_EXT2) += ext2.o
+obj-$(CONFIG_CMD_FAT) += fat.o
+obj-$(CONFIG_CMD_FDT) += fdt.o
+obj-$(CONFIG_CMD_SQUASHFS) += sqfs.o
+obj-$(CONFIG_CMD_SELECT_FONT) += font.o
+obj-$(CONFIG_CMD_FLASH) += flash.o
+obj-$(CONFIG_CMD_FPGA) += fpga.o
+obj-$(CONFIG_CMD_FPGAD) += fpgad.o
+obj-$(CONFIG_CMD_FS_GENERIC) += fs.o
+obj-$(CONFIG_CMD_FUSE) += fuse.o
+obj-$(CONFIG_CMD_FWU_METADATA) += fwu_mdata.o
+obj-$(CONFIG_CMD_GETTIME) += gettime.o
+obj-$(CONFIG_CMD_GPIO) += gpio.o
+obj-$(CONFIG_CMD_HISTORY) += history.o
+obj-$(CONFIG_CMD_HVC) += smccc.o
+obj-$(CONFIG_CMD_I2C) += i2c.o
+obj-$(CONFIG_CMD_I3C) += i3c.o
+obj-$(CONFIG_CMD_IOTRACE) += iotrace.o
+obj-$(CONFIG_CMD_HASH) += hash.o
+obj-$(CONFIG_CMD_IDE) += ide.o disk.o
+obj-$(CONFIG_CMD_INI) += ini.o
+obj-$(CONFIG_CMD_IRQ) += irq.o
+obj-$(CONFIG_CMD_ITEST) += itest.o
+obj-$(CONFIG_CMD_JFFS2) += jffs2.o
+obj-$(CONFIG_CMD_CRAMFS) += cramfs.o
+obj-$(CONFIG_LED_STATUS_CMD) += legacy_led.o
+obj-$(CONFIG_CMD_LED) += led.o
+obj-$(CONFIG_CMD_LICENSE) += license.o
+obj-y += load.o
+obj-$(CONFIG_CMD_LOG) += log.o
+obj-$(CONFIG_CMD_LSBLK) += lsblk.o
+obj-$(CONFIG_CMD_MD5SUM) += md5sum.o
+obj-$(CONFIG_CMD_MEMORY) += mem.o
+obj-$(CONFIG_CMD_MEMINFO) += meminfo.o
+obj-$(CONFIG_CMD_IO) += io.o
+obj-$(CONFIG_CMD_MII) += mii.o
+obj-$(CONFIG_CMD_MISC) += misc.o
+obj-$(CONFIG_CMD_MDIO) += mdio.o
+obj-$(CONFIG_CMD_PAUSE) += pause.o
+obj-$(CONFIG_CMD_SLEEP) += sleep.o
+obj-$(CONFIG_CMD_MMC) += mmc.o
+obj-$(CONFIG_CMD_OPTEE_RPMB) += optee_rpmb.o
+obj-$(CONFIG_CMD_OPTEE) += optee.o
+obj-$(CONFIG_CMD_MP) += mp.o
+obj-$(CONFIG_CMD_MTD) += mtd.o
+obj-$(CONFIG_CMD_MTDPARTS) += mtdparts.o
+obj-$(CONFIG_CMD_CLONE) += clone.o
+ifneq ($(CONFIG_CMD_NAND)$(CONFIG_CMD_SF),)
+obj-y += legacy-mtd-utils.o
+endif
+obj-$(CONFIG_CMD_MUX) += mux.o
+obj-$(CONFIG_CMD_NAND) += nand.o
+ifdef CONFIG_NET
+obj-$(CONFIG_CMD_NET) += net.o net-common.o
+else ifdef CONFIG_NET_LWIP
+obj-$(CONFIG_CMD_NET) += net-common.o
+obj-y += lwip/
+endif
+obj-$(CONFIG_ENV_SUPPORT) += nvedit.o
+obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o
+obj-$(CONFIG_CMD_ONENAND) += onenand.o
+obj-$(CONFIG_CMD_OSD) += osd.o
+obj-$(CONFIG_CMD_PART) += part.o
+obj-$(CONFIG_CMD_PCAP) += pcap.o
+ifdef CONFIG_PCI
+obj-$(CONFIG_CMD_PCI) += pci.o
+obj-$(CONFIG_CMD_PCI_MPS) += pci_mps.o
+endif
+obj-$(CONFIG_CMD_PINMUX) += pinmux.o
+obj-$(CONFIG_CMD_PMC) += pmc.o
+obj-$(CONFIG_CMD_PSTORE) += pstore.o
+obj-$(CONFIG_CMD_PWM) += pwm.o
+obj-$(CONFIG_CMD_PXE) += pxe.o
+obj-$(CONFIG_CMD_WOL) += wol.o
+obj-$(CONFIG_CMD_QFW) += qfw.o
+obj-$(CONFIG_CMD_READ) += read.o
+obj-$(CONFIG_CMD_WRITE) += read.o
+obj-$(CONFIG_CMD_REGINFO) += reginfo.o
+obj-$(CONFIG_CMD_REMOTEPROC) += remoteproc.o
+obj-$(CONFIG_CMD_RNG) += rng.o
+obj-$(CONFIG_CMD_KASLRSEED) += kaslrseed.o
+obj-$(CONFIG_CMD_RKMTD) += rkmtd.o
+obj-$(CONFIG_CMD_ROCKUSB) += rockusb.o
+obj-$(CONFIG_CMD_RTC) += rtc.o
+obj-$(CONFIG_SANDBOX) += host.o
+obj-$(CONFIG_CMD_SATA) += sata.o
+obj-$(CONFIG_CMD_NVME) += nvme.o
+obj-$(CONFIG_SANDBOX) += sb.o
+obj-$(CONFIG_CMD_SF) += sf.o
+obj-$(CONFIG_CMD_SCMI) += scmi.o
+obj-$(CONFIG_CMD_SCSI) += scsi.o disk.o
+obj-$(CONFIG_CMD_SHA1SUM) += sha1sum.o
+obj-$(CONFIG_CMD_SEAMA) += seama.o
+obj-$(CONFIG_CMD_SETEXPR) += setexpr.o
+obj-$(CONFIG_CMD_SETEXPR_FMT) += printf.o
+obj-$(CONFIG_CMD_SPI) += spi.o
+obj-$(CONFIG_CMD_STRINGS) += strings.o
+obj-$(CONFIG_CMD_SMBIOS) += smbios.o
+obj-$(CONFIG_CMD_SMC) += smccc.o
+obj-$(CONFIG_CMD_SYSBOOT) += sysboot.o
+obj-$(CONFIG_CMD_STACKPROTECTOR_TEST) += stackprot_test.o
+obj-$(CONFIG_CMD_TCPM) += tcpm.o
+obj-$(CONFIG_CMD_TEMPERATURE) += temperature.o
+obj-$(CONFIG_CMD_TERMINAL) += terminal.o
+obj-$(CONFIG_CMD_TIME) += time.o
+obj-$(CONFIG_CMD_TIMER) += timer.o
+obj-$(CONFIG_CMD_TRACE) += trace.o
+obj-$(CONFIG_HUSH_PARSER) += test.o
+obj-$(CONFIG_CMD_TPM) += tpm-common.o
+obj-$(CONFIG_CMD_TPM_V1) += tpm-v1.o
+obj-$(CONFIG_CMD_TPM_TEST) += tpm_test.o
+obj-$(CONFIG_CMD_TPM_V2) += tpm-v2.o
+obj-$(CONFIG_CMD_CROS_EC) += cros_ec.o
+obj-$(CONFIG_CMD_UBI) += ubi.o
+obj-$(CONFIG_CMD_UBIFS) += ubifs.o
+obj-$(CONFIG_CMD_UNLZ4) += unlz4.o
+obj-$(CONFIG_CMD_UNZIP) += unzip.o
+obj-$(CONFIG_CMD_UPL) += upl.o
+obj-$(CONFIG_CMD_VIRTIO) += virtio.o
+obj-$(CONFIG_CMD_WDT) += wdt.o
+obj-$(CONFIG_CMD_LZMADEC) += lzmadec.o
+obj-$(CONFIG_CMD_UFS) += ufs.o
+obj-$(CONFIG_CMD_USB) += usb.o disk.o
+obj-$(CONFIG_CMD_VIDCONSOLE) += video.o
+
+obj-$(CONFIG_CMD_FASTBOOT) += fastboot.o
+obj-$(CONFIG_CMD_FS_UUID) += fs_uuid.o
+
+obj-$(CONFIG_CMD_USB_MASS_STORAGE) += usb_mass_storage.o
+obj-$(CONFIG_CMD_USB_SDP) += usb_gadget_sdp.o
+obj-$(CONFIG_CMD_THOR_DOWNLOAD) += thordown.o
+obj-$(CONFIG_CMD_VBE) += vbe.o
+obj-$(CONFIG_CMD_XIMG) += ximg.o
+obj-$(CONFIG_CMD_XXD) += xxd.o
+obj-$(CONFIG_CMD_SPL) += spl.o
+obj-$(CONFIG_CMD_W1) += w1.o
+obj-$(CONFIG_CMD_ZIP) += zip.o
+obj-$(CONFIG_CMD_ZFS) += zfs.o
+
+obj-$(CONFIG_CMD_DFU) += dfu.o
+obj-$(CONFIG_CMD_GPT) += gpt.o
+obj-$(CONFIG_CMD_MBR) += mbr.o
+obj-$(CONFIG_CMD_ETHSW) += ethsw.o
+obj-$(CONFIG_CMD_AXI) += axi.o
+obj-$(CONFIG_CMD_PVBLOCK) += pvblock.o
+
+# Power
+obj-$(CONFIG_CMD_PMIC) += pmic.o
+obj-$(CONFIG_CMD_REGULATOR) += regulator.o
+
+obj-$(CONFIG_CMD_BLOB) += blob.o
+
+# Android Verified Boot 2.0
+obj-$(CONFIG_CMD_AVB) += avb.o
+
+# Foundries.IO SCP03
+obj-$(CONFIG_CMD_SCP03) += scp03.o
+
+obj-$(CONFIG_HUSH_SELECTABLE) += cli.o
+
+obj-$(CONFIG_CMD_SPAWN) += spawn.o
+
+obj-$(CONFIG_ARM) += arm/
+obj-$(CONFIG_RISCV) += riscv/
+obj-$(CONFIG_SANDBOX) += sandbox/
+obj-$(CONFIG_X86) += x86/
+
+# Meson
+obj-$(CONFIG_CMD_MESON) += meson/
+
+obj-$(CONFIG_ARCH_MVEBU) += mvebu/
+
+# TI
+obj-$(CONFIG_ARCH_KEYSTONE) += ti/
+obj-$(CONFIG_ARCH_K3) += ti/
+obj-$(CONFIG_ARCH_OMAP2PLUS) += ti/
+endif # !CONFIG_XPL_BUILD
+
+obj-$(CONFIG_$(PHASE_)CMD_TLV_EEPROM) += tlv_eeprom.o
+
+obj-$(CONFIG_CMD_BCM_EXT_UTILS) += broadcom/
+
+filechk_data_gz = (echo "static const char data_gz[] ="; cat $< | scripts/bin2c; echo ";")
+
+filechk_data_size = \
+ (echo "static const size_t data_size = "; \
+ cat $< | wc -c; echo ";")
+
+# "config" command
+$(obj)/config.o: $(obj)/config_data_gz.h $(obj)/config_data_size.h
+
+targets += config_data.gz
+$(obj)/config_data.gz: $(KCONFIG_CONFIG) FORCE
+ $(call if_changed,gzip)
+
+targets += config_data_gz.h
+$(obj)/config_data_gz.h: $(obj)/config_data.gz FORCE
+ $(call filechk,data_gz)
+
+targets += config_data_size.h
+$(obj)/config_data_size.h: $(KCONFIG_CONFIG) FORCE
+ $(call filechk,data_size)
+
+# "license" command
+$(obj)/license.o: $(obj)/license_data_gz.h $(obj)/license_data_size.h
+
+targets += license_data.gz
+$(obj)/license_data.gz: $(srctree)/Licenses/gpl-2.0.txt FORCE
+ $(call if_changed,gzip)
+
+targets += license_data_gz.h
+$(obj)/license_data_gz.h: $(obj)/license_data.gz FORCE
+ $(call filechk,data_gz)
+
+targets += license_data_size.h
+$(obj)/license_data_size.h: $(srctree)/Licenses/gpl-2.0.txt FORCE
+ $(call filechk,data_size)
+
+CFLAGS_ethsw.o := -Wno-enum-conversion
diff --git a/cmd/abootimg.c b/cmd/abootimg.c
new file mode 100644
index 00000000000..6fb52153786
--- /dev/null
+++ b/cmd/abootimg.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2020
+ * Sam Protsenko <joe.skb7@gmail.com>
+ */
+
+#include <android_image.h>
+#include <command.h>
+#include <env.h>
+#include <image.h>
+#include <mapmem.h>
+
+#define abootimg_addr() \
+ (_abootimg_addr == -1 ? image_load_addr : _abootimg_addr)
+
+/* Please use abootimg_addr() macro to obtain the boot image address */
+static ulong _abootimg_addr = -1;
+static ulong _ainit_bootimg_addr = -1;
+static ulong _avendor_bootimg_addr = -1;
+
+ulong get_abootimg_addr(void)
+{
+ return (_abootimg_addr == -1 ? image_load_addr : _abootimg_addr);
+}
+
+void set_abootimg_addr(ulong addr)
+{
+ _abootimg_addr = addr;
+}
+
+ulong get_ainit_bootimg_addr(void)
+{
+ return _ainit_bootimg_addr;
+}
+
+ulong get_avendor_bootimg_addr(void)
+{
+ return _avendor_bootimg_addr;
+}
+
+void set_avendor_bootimg_addr(ulong addr)
+{
+ _avendor_bootimg_addr = addr;
+}
+
+static int abootimg_get_ver(int argc, char *const argv[])
+{
+ const struct andr_boot_img_hdr_v0 *hdr;
+ int res = CMD_RET_SUCCESS;
+
+ if (argc > 1)
+ return CMD_RET_USAGE;
+
+ hdr = map_sysmem(abootimg_addr(), sizeof(*hdr));
+ if (!is_android_boot_image_header(hdr)) {
+ printf("Error: Boot Image header is incorrect\n");
+ res = CMD_RET_FAILURE;
+ goto exit;
+ }
+
+ if (argc == 0)
+ printf("%u\n", hdr->header_version);
+ else
+ env_set_ulong(argv[0], hdr->header_version);
+
+exit:
+ unmap_sysmem(hdr);
+ return res;
+}
+
+static int abootimg_get_recovery_dtbo(int argc, char *const argv[])
+{
+ ulong addr;
+ u32 size;
+
+ if (argc > 2)
+ return CMD_RET_USAGE;
+
+ if (!android_image_get_dtbo(abootimg_addr(), &addr, &size))
+ return CMD_RET_FAILURE;
+
+ if (argc == 0) {
+ printf("%lx\n", addr);
+ } else {
+ env_set_hex(argv[0], addr);
+ if (argc == 2)
+ env_set_hex(argv[1], size);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int abootimg_get_dtb_load_addr(int argc, char *const argv[])
+{
+ if (argc > 1)
+ return CMD_RET_USAGE;
+ struct andr_image_data img_data = {0};
+ const struct andr_boot_img_hdr_v0 *hdr;
+ const struct andr_vnd_boot_img_hdr *vhdr = NULL;
+
+ hdr = map_sysmem(abootimg_addr(), sizeof(*hdr));
+ if (get_avendor_bootimg_addr() != -1)
+ vhdr = map_sysmem(get_avendor_bootimg_addr(), sizeof(*vhdr));
+
+ if (!android_image_get_data(hdr, vhdr, &img_data)) {
+ if (get_avendor_bootimg_addr() != -1)
+ unmap_sysmem(vhdr);
+ unmap_sysmem(hdr);
+ return CMD_RET_FAILURE;
+ }
+
+ if (get_avendor_bootimg_addr() != -1)
+ unmap_sysmem(vhdr);
+ unmap_sysmem(hdr);
+
+ if (img_data.header_version < 2) {
+ printf("Error: header_version must be >= 2 for this\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (!img_data.dtb_load_addr) {
+ printf("Error: failed to read dtb_load_addr\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc == 0)
+ printf("%lx\n", (ulong)img_data.dtb_load_addr);
+ else
+ env_set_hex(argv[0], (ulong)img_data.dtb_load_addr);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int abootimg_get_dtb_by_index(int argc, char *const argv[])
+{
+ const char *index_str;
+ u32 num;
+ char *endp;
+ ulong addr;
+ u32 size;
+
+ if (argc < 1 || argc > 3)
+ return CMD_RET_USAGE;
+
+ index_str = argv[0] + strlen("--index=");
+ if (index_str[0] == '\0') {
+ printf("Error: Wrong index num\n");
+ return CMD_RET_FAILURE;
+ }
+
+ num = simple_strtoul(index_str, &endp, 0);
+ if (*endp != '\0') {
+ printf("Error: Wrong index num\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (!android_image_get_dtb_by_index(abootimg_addr(),
+ get_avendor_bootimg_addr(), num,
+ &addr, &size)) {
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc == 1) {
+ printf("%lx\n", addr);
+ } else {
+ if (env_set_hex(argv[1], addr)) {
+ printf("Error: Can't set [addr_var]\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc == 3) {
+ if (env_set_hex(argv[2], size)) {
+ printf("Error: Can't set [size_var]\n");
+ return CMD_RET_FAILURE;
+ }
+ }
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int abootimg_get_dtb(int argc, char *const argv[])
+{
+ if (argc < 1)
+ return CMD_RET_USAGE;
+
+ if (strstr(argv[0], "--index="))
+ return abootimg_get_dtb_by_index(argc, argv);
+
+ return CMD_RET_USAGE;
+}
+
+static int do_abootimg_addr(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *endp;
+ ulong img_addr;
+
+ if (argc < 2 || argc > 4)
+ return CMD_RET_USAGE;
+
+ img_addr = hextoul(argv[1], &endp);
+ if (*endp != '\0') {
+ printf("Error: Wrong image address\n");
+ return CMD_RET_FAILURE;
+ }
+
+ _abootimg_addr = img_addr;
+
+ if (argc > 2) {
+ img_addr = simple_strtoul(argv[2], &endp, 16);
+ if (*endp != '\0') {
+ printf("Error: Wrong vendor_boot image address\n");
+ return CMD_RET_FAILURE;
+ }
+
+ _avendor_bootimg_addr = img_addr;
+ }
+
+ if (argc == 4) {
+ img_addr = simple_strtoul(argv[3], &endp, 16);
+ if (*endp != '\0') {
+ printf("Error: Wrong init_boot image address\n");
+ return CMD_RET_FAILURE;
+ }
+
+ _ainit_bootimg_addr = img_addr;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_abootimg_get(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *param;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ param = argv[1];
+ argc -= 2;
+ argv += 2;
+ if (!strcmp(param, "ver"))
+ return abootimg_get_ver(argc, argv);
+ else if (!strcmp(param, "recovery_dtbo"))
+ return abootimg_get_recovery_dtbo(argc, argv);
+ else if (!strcmp(param, "dtb_load_addr"))
+ return abootimg_get_dtb_load_addr(argc, argv);
+ else if (!strcmp(param, "dtb"))
+ return abootimg_get_dtb(argc, argv);
+
+ return CMD_RET_USAGE;
+}
+
+static int do_abootimg_dump(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ if (!strcmp(argv[1], "dtb")) {
+ if (android_image_print_dtb_contents(abootimg_addr()))
+ return CMD_RET_FAILURE;
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static struct cmd_tbl cmd_abootimg_sub[] = {
+ U_BOOT_CMD_MKENT(addr, 4, 1, do_abootimg_addr, "", ""),
+ U_BOOT_CMD_MKENT(dump, 2, 1, do_abootimg_dump, "", ""),
+ U_BOOT_CMD_MKENT(get, 5, 1, do_abootimg_get, "", ""),
+};
+
+static int do_abootimg(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ cp = find_cmd_tbl(argv[1], cmd_abootimg_sub,
+ ARRAY_SIZE(cmd_abootimg_sub));
+
+ /* Strip off leading 'abootimg' command argument */
+ argc--;
+ argv++;
+
+ if (!cp || argc > cp->maxargs)
+ return CMD_RET_USAGE;
+ if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp))
+ return CMD_RET_SUCCESS;
+
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(
+ abootimg, CONFIG_SYS_MAXARGS, 0, do_abootimg,
+ "manipulate Android Boot Image",
+ "addr <boot_img_addr> [<vendor_boot_img_addr> [<init_boot_img_addr>]]\n"
+ " - set the address in RAM where boot image is located\n"
+ " ($loadaddr is used by default)\n"
+ "abootimg dump dtb\n"
+ " - print info for all DT blobs in DTB area\n"
+ "abootimg get ver [varname]\n"
+ " - get header version\n"
+ "abootimg get recovery_dtbo [addr_var [size_var]]\n"
+ " - get address and size (hex) of recovery DTBO area in the image\n"
+ " [addr_var]: variable name to contain DTBO area address\n"
+ " [size_var]: variable name to contain DTBO area size\n"
+ "abootimg get dtb_load_addr [varname]\n"
+ " - get load address (hex) of DTB, from image header\n"
+ "abootimg get dtb --index=<num> [addr_var [size_var]]\n"
+ " - get address and size (hex) of DT blob in the image by index\n"
+ " <num>: index number of desired DT blob in DTB area\n"
+ " [addr_var]: variable name to contain DT blob address\n"
+ " [size_var]: variable name to contain DT blob size"
+);
diff --git a/cmd/acpi.c b/cmd/acpi.c
new file mode 100644
index 00000000000..bb243202009
--- /dev/null
+++ b/cmd/acpi.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2019 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+#include <command.h>
+#include <display_options.h>
+#include <log.h>
+#include <mapmem.h>
+#include <tables_csum.h>
+#include <acpi/acpi_table.h>
+#include <asm/acpi_table.h>
+#include <asm/global_data.h>
+#include <linux/errno.h>
+#include <dm/acpi.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char *show_checksum(void *ptr, uint size, bool chksums)
+{
+ uint checksum;
+
+ if (!chksums)
+ return "";
+ checksum = table_compute_checksum(ptr, size);
+
+ return checksum ? " bad" : " OK";
+}
+
+/**
+ * dump_hdr() - Dump an ACPI header
+ *
+ * Except for the Firmware ACPI Control Structure (FACS)
+ * additionally show the revision information.
+ *
+ * @hdr: ACPI header to dump
+ */
+static void dump_hdr(struct acpi_table_header *hdr, bool chksums)
+{
+ bool has_hdr = memcmp(hdr->signature, "FACS", ACPI_NAME_LEN);
+
+ printf("%.*s %16lx %5x", ACPI_NAME_LEN, hdr->signature,
+ (ulong)map_to_sysmem(hdr), hdr->length);
+ if (has_hdr) {
+ printf(" v%02d %.6s %.8s %x %.4s %x%s\n", hdr->revision,
+ hdr->oem_id, hdr->oem_table_id, hdr->oem_revision,
+ hdr->creator_id, hdr->creator_revision,
+ show_checksum(hdr, hdr->length, chksums));
+ } else {
+ printf("\n");
+ }
+}
+
+static int dump_table_name(const char *sig)
+{
+ struct acpi_table_header *hdr;
+
+ hdr = acpi_find_table(sig);
+ if (!hdr)
+ return -ENOENT;
+ printf("%.*s @ %16lx\n", ACPI_NAME_LEN, hdr->signature,
+ (ulong)nomap_to_sysmem(hdr));
+ print_buffer(0, hdr, 1, hdr->length, 0);
+
+ return 0;
+}
+
+static void list_fadt(struct acpi_fadt *fadt, bool chksums)
+{
+ if (fadt->header.revision >= 3 && fadt->x_dsdt)
+ dump_hdr(nomap_sysmem(fadt->x_dsdt, 0), chksums);
+ else if (fadt->dsdt)
+ dump_hdr(nomap_sysmem(fadt->dsdt, 0), chksums);
+ if (!IS_ENABLED(CONFIG_X86) && !IS_ENABLED(CONFIG_SANDBOX) &&
+ !(fadt->flags & ACPI_FADT_HW_REDUCED_ACPI))
+ log_err("FADT not ACPI-hardware-reduced-compliant\n");
+ if (fadt->header.revision >= 3 && fadt->x_firmware_ctrl)
+ dump_hdr(nomap_sysmem(fadt->x_firmware_ctrl, 0), chksums);
+ else if (fadt->firmware_ctrl)
+ dump_hdr(nomap_sysmem(fadt->firmware_ctrl, 0), chksums);
+}
+
+static void list_rsdt(struct acpi_rsdp *rsdp, bool chksums)
+{
+ int len, i, count;
+ struct acpi_rsdt *rsdt;
+ struct acpi_xsdt *xsdt;
+
+ if (rsdp->rsdt_address) {
+ rsdt = nomap_sysmem(rsdp->rsdt_address, 0);
+ dump_hdr(&rsdt->header, chksums);
+ }
+ if (rsdp->xsdt_address) {
+ xsdt = nomap_sysmem(rsdp->xsdt_address, 0);
+ dump_hdr(&xsdt->header, chksums);
+ len = xsdt->header.length - sizeof(xsdt->header);
+ count = len / sizeof(u64);
+ } else if (rsdp->rsdt_address) {
+ len = rsdt->header.length - sizeof(rsdt->header);
+ count = len / sizeof(u32);
+ } else {
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ struct acpi_table_header *hdr;
+ u64 entry;
+
+ if (rsdp->xsdt_address)
+ entry = xsdt->entry[i];
+ else
+ entry = rsdt->entry[i];
+ if (!entry)
+ break;
+ hdr = nomap_sysmem(entry, 0);
+ dump_hdr(hdr, chksums);
+ if (!memcmp(hdr->signature, "FACP", ACPI_NAME_LEN))
+ list_fadt((struct acpi_fadt *)hdr, chksums);
+ }
+}
+
+static void list_rsdp(struct acpi_rsdp *rsdp, bool chksums)
+{
+ printf("RSDP %16lx %5x v%02d %.6s%s%s\n",
+ (ulong)map_to_sysmem(rsdp), rsdp->length, rsdp->revision,
+ rsdp->oem_id, show_checksum(rsdp, 0x14, chksums),
+ show_checksum(rsdp, rsdp->length, chksums));
+ list_rsdt(rsdp, chksums);
+}
+
+static int do_acpi_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct acpi_rsdp *rsdp;
+ bool chksums;
+
+ chksums = argc >= 2 && !strcmp("-c", argv[1]);
+ rsdp = map_sysmem(gd_acpi_start(), 0);
+ if (!rsdp) {
+ printf("No ACPI tables present\n");
+ return 0;
+ }
+ printf("Name Base Size Detail\n"
+ "---- ---------------- ----- ----------------------------\n");
+ list_rsdp(rsdp, chksums);
+
+ return 0;
+}
+
+static int do_acpi_set(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong val;
+
+ if (argc < 2) {
+ printf("ACPI pointer: %lx\n", gd_acpi_start());
+ } else {
+ val = hextoul(argv[1], NULL);
+ printf("Setting ACPI pointer to %lx\n", val);
+ gd_set_acpi_start(val);
+ }
+
+ return 0;
+}
+
+static int do_acpi_items(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool dump_contents;
+
+ dump_contents = argc >= 2 && !strcmp("-d", argv[1]);
+ if (!IS_ENABLED(CONFIG_ACPIGEN)) {
+ printf("Not supported (enable ACPIGEN)\n");
+ return CMD_RET_FAILURE;
+ }
+ acpi_dump_items(dump_contents ? ACPI_DUMP_CONTENTS : ACPI_DUMP_LIST);
+
+ return 0;
+}
+
+static int do_acpi_dump(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *name;
+ char sig[ACPI_NAME_LEN];
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ name = argv[1];
+ if (strlen(name) != ACPI_NAME_LEN) {
+ printf("Table name '%s' must be four characters\n", name);
+ return CMD_RET_FAILURE;
+ }
+ str_to_upper(name, sig, ACPI_NAME_LEN);
+ ret = dump_table_name(sig);
+ if (ret) {
+ printf("Table '%.*s' not found\n", ACPI_NAME_LEN, sig);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(acpi,
+ "list [-c] - list ACPI tables [check checksums]\n"
+ "acpi items [-d] - List/dump each piece of ACPI data from devices\n"
+ "acpi set [<addr>] - Set or show address of ACPI tables\n"
+ "acpi dump <name> - Dump ACPI table");
+
+U_BOOT_CMD_WITH_SUBCMDS(acpi, "ACPI tables", acpi_help_text,
+ U_BOOT_SUBCMD_MKENT(list, 2, 1, do_acpi_list),
+ U_BOOT_SUBCMD_MKENT(items, 2, 1, do_acpi_items),
+ U_BOOT_SUBCMD_MKENT(set, 2, 1, do_acpi_set),
+ U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_acpi_dump));
diff --git a/cmd/adc.c b/cmd/adc.c
new file mode 100644
index 00000000000..334ba7fdeca
--- /dev/null
+++ b/cmd/adc.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+#include <command.h>
+#include <dm.h>
+#include <env.h>
+#include <adc.h>
+#include <linux/printk.h>
+
+static int do_adc_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int ret, err;
+
+ ret = err = uclass_first_device_check(UCLASS_ADC, &dev);
+
+ while (dev) {
+ printf("- %s status: %i\n", dev->name, ret);
+
+ ret = uclass_next_device_check(&dev);
+ if (ret)
+ err = ret;
+ }
+
+ return err ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+static int do_adc_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ unsigned int data_mask, ch_mask;
+ int ret, vss, vdd;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ ret = uclass_get_device_by_name(UCLASS_ADC, argv[1], &dev);
+ if (ret) {
+ printf("Unknown ADC device %s\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("ADC Device '%s' :\n", argv[1]);
+
+ ret = adc_channel_mask(dev, &ch_mask);
+ if (!ret)
+ printf("channel mask: %x\n", ch_mask);
+
+ ret = adc_data_mask(dev, &data_mask);
+ if (!ret)
+ printf("data mask: %x\n", data_mask);
+
+ ret = adc_vdd_value(dev, &vdd);
+ if (!ret)
+ printf("vdd: %duV\n", vdd);
+
+ ret = adc_vss_value(dev, &vss);
+ if (!ret)
+ printf("vss: %duV\n", vss);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_adc_single(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *varname = NULL;
+ struct udevice *dev;
+ unsigned int data;
+ int ret, uV, val;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ if (argc >= 4)
+ varname = argv[3];
+
+ ret = adc_channel_single_shot(argv[1], simple_strtol(argv[2], NULL, 0),
+ &data);
+ if (ret) {
+ printf("Error getting single shot for device %s channel %s (ret=%d)\n",
+ argv[1], argv[2], ret);
+ return CMD_RET_FAILURE;
+ }
+
+ ret = uclass_get_device_by_name(UCLASS_ADC, argv[1], &dev);
+ if (!ret && !adc_raw_to_uV(dev, data, &uV)) {
+ val = uV;
+ printf("%u, %d uV\n", data, uV);
+ } else {
+ val = data;
+ printf("%u\n", data);
+ }
+
+ if (varname)
+ env_set_ulong(varname, val);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_adc_scan(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct adc_channel ch[ADC_MAX_CHANNEL];
+ struct udevice *dev;
+ unsigned int ch_mask;
+ int i, chan, ret, uV;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ ret = uclass_get_device_by_name(UCLASS_ADC, argv[1], &dev);
+ if (ret) {
+ pr_err("Can't get the ADC %s: %d\n", argv[1], ret);
+ return CMD_RET_FAILURE;
+ }
+
+ switch (argc) {
+ case 3:
+ ch_mask = simple_strtoul(argv[2], NULL, 0);
+ if (ch_mask)
+ break;
+ case 2:
+ ret = adc_channel_mask(dev, &ch_mask);
+ if (ret) {
+ pr_err("Can't get mask for %s: %d\n", dev->name, ret);
+ return CMD_RET_FAILURE;
+ }
+ break;
+ }
+
+ ret = adc_channels_single_shot(dev->name, ch_mask, ch);
+ if (ret) {
+ pr_err("Can't get single shot for %s (chans mask: 0x%x): %d\n",
+ dev->name, ch_mask, ret);
+ return CMD_RET_FAILURE;
+ }
+
+ for (chan = 0, i = 0; chan < ADC_MAX_CHANNEL; chan++) {
+ if (!(ch_mask & ADC_CHANNEL(chan)))
+ continue;
+ if (!adc_raw_to_uV(dev, ch[i].data, &uV))
+ printf("[%02d]: %u, %d uV\n", ch[i].id, ch[i].data, uV);
+ else
+ printf("[%02d]: %u\n", ch[i].id, ch[i].data);
+ i++;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_LONGHELP(adc,
+ "list - list ADC devices\n"
+ "adc info <name> - Get ADC device info\n"
+ "adc single <name> <channel> [varname] - Get Single data of ADC device channel\n"
+ "adc scan <name> [channel mask] - Scan all [or masked] ADC channels\n");
+
+U_BOOT_CMD_WITH_SUBCMDS(adc, "ADC sub-system", adc_help_text,
+ U_BOOT_SUBCMD_MKENT(list, 1, 1, do_adc_list),
+ U_BOOT_SUBCMD_MKENT(info, 2, 1, do_adc_info),
+ U_BOOT_SUBCMD_MKENT(single, 4, 1, do_adc_single),
+ U_BOOT_SUBCMD_MKENT(scan, 3, 1, do_adc_scan));
diff --git a/cmd/addrmap.c b/cmd/addrmap.c
new file mode 100644
index 00000000000..f7e4d9206de
--- /dev/null
+++ b/cmd/addrmap.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <command.h>
+#include <addr_map.h>
+
+static int do_addrmap(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int i;
+
+ printf(" vaddr paddr size\n");
+ printf("================ ================ ================\n");
+
+ for (i = 0; i < CONFIG_SYS_NUM_ADDR_MAP; i++) {
+ if (address_map[i].size == 0)
+ continue;
+
+ printf("%16.8lx %16.8llx %16.8llx\n",
+ address_map[i].vaddr,
+ (unsigned long long)address_map[i].paddr,
+ (unsigned long long)address_map[i].size);
+ }
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ addrmap, 1, 1, do_addrmap,
+ "List non-identity virtual-physical memory mappings for 32-bit CPUs",
+ ""
+);
diff --git a/cmd/adtimg.c b/cmd/adtimg.c
new file mode 100644
index 00000000000..53f33764fbe
--- /dev/null
+++ b/cmd/adtimg.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018 Linaro Ltd.
+ * Sam Protsenko <semen.protsenko@linaro.org>
+ * Eugeniu Rosca <rosca.eugeniu@gmail.com>
+ */
+
+#include <command.h>
+#include <env.h>
+#include <vsprintf.h>
+#include <image-android-dt.h>
+
+#define OPT_INDEX "--index"
+
+/*
+ * Current/working DTB/DTBO Android image address.
+ * Similar to 'working_fdt' variable in 'fdt' command.
+ */
+static ulong working_img;
+
+static int do_adtimg_addr(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *endp;
+ ulong hdr_addr;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ hdr_addr = hextoul(argv[1], &endp);
+ if (*endp != '\0') {
+ printf("Error: Wrong image address '%s'\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ /*
+ * Allow users to set an address prior to copying the DTB/DTBO
+ * image to that same address, i.e. skip header verification.
+ */
+
+ working_img = hdr_addr;
+ return CMD_RET_SUCCESS;
+}
+
+static int adtimg_check_working_img(void)
+{
+ if (!working_img) {
+ printf("Error: Please, call 'adtimg addr <addr>'. Aborting!\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (!android_dt_check_header(working_img)) {
+ printf("Error: Invalid image header at 0x%lx\n", working_img);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_adtimg_dump(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (argc != 1)
+ return CMD_RET_USAGE;
+
+ if (adtimg_check_working_img() != CMD_RET_SUCCESS)
+ return CMD_RET_FAILURE;
+
+ android_dt_print_contents(working_img);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int adtimg_getopt_u32(char * const opt, char * const name, u32 *optval)
+{
+ char *endp, *str;
+ u32 val;
+
+ if (!opt || !name || !optval)
+ return CMD_RET_FAILURE;
+
+ str = strchr(opt, '=');
+ if (!str) {
+ printf("Error: Option '%s' not followed by '='\n", name);
+ return CMD_RET_FAILURE;
+ }
+
+ if (*++str == '\0') {
+ printf("Error: Option '%s=' not followed by value\n", name);
+ return CMD_RET_FAILURE;
+ }
+
+ val = simple_strtoul(str, &endp, 0);
+ if (*endp != '\0') {
+ printf("Error: Wrong integer value '%s=%s'\n", name, str);
+ return CMD_RET_FAILURE;
+ }
+
+ *optval = val;
+ return CMD_RET_SUCCESS;
+}
+
+static int adtimg_getopt_index(int argc, char *const argv[], u32 *index,
+ char **avar, char **svar)
+{
+ int ret;
+
+ if (!argv || !avar || !svar)
+ return CMD_RET_FAILURE;
+
+ if (argc > 3) {
+ printf("Error: Unexpected argument '%s'\n", argv[3]);
+ return CMD_RET_FAILURE;
+ }
+
+ ret = adtimg_getopt_u32(argv[0], OPT_INDEX, index);
+ if (ret != CMD_RET_SUCCESS)
+ return ret;
+
+ if (argc > 1)
+ *avar = argv[1];
+ if (argc > 2)
+ *svar = argv[2];
+
+ return CMD_RET_SUCCESS;
+}
+
+static int adtimg_get_dt_by_index(int argc, char *const argv[])
+{
+ ulong addr;
+ u32 index, size;
+ int ret;
+ char *avar = NULL, *svar = NULL;
+
+ ret = adtimg_getopt_index(argc, argv, &index, &avar, &svar);
+ if (ret != CMD_RET_SUCCESS)
+ return ret;
+
+ if (!android_dt_get_fdt_by_index(working_img, index, &addr, &size))
+ return CMD_RET_FAILURE;
+
+ if (avar && svar) {
+ ret = env_set_hex(avar, addr);
+ if (ret) {
+ printf("Error: Can't set '%s' to 0x%lx\n", avar, addr);
+ return CMD_RET_FAILURE;
+ }
+ ret = env_set_hex(svar, size);
+ if (ret) {
+ printf("Error: Can't set '%s' to 0x%x\n", svar, size);
+ return CMD_RET_FAILURE;
+ }
+ } else if (avar) {
+ ret = env_set_hex(avar, addr);
+ if (ret) {
+ printf("Error: Can't set '%s' to 0x%lx\n", avar, addr);
+ return CMD_RET_FAILURE;
+ }
+ printf("0x%x (%d)\n", size, size);
+ } else {
+ printf("0x%lx, 0x%x (%d)\n", addr, size, size);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int adtimg_get_dt(int argc, char *const argv[])
+{
+ if (argc < 2) {
+ printf("Error: No options passed to '%s'\n", argv[0]);
+ return CMD_RET_FAILURE;
+ }
+
+ /* Strip off leading 'dt' command argument */
+ argc--;
+ argv++;
+
+ if (!strncmp(argv[0], OPT_INDEX, sizeof(OPT_INDEX) - 1))
+ return adtimg_get_dt_by_index(argc, argv);
+
+ printf("Error: Option '%s' not supported\n", argv[0]);
+ return CMD_RET_FAILURE;
+}
+
+static int do_adtimg_get(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (argc < 2) {
+ printf("Error: No arguments passed to '%s'\n", argv[0]);
+ return CMD_RET_FAILURE;
+ }
+
+ if (adtimg_check_working_img() != CMD_RET_SUCCESS)
+ return CMD_RET_FAILURE;
+
+ /* Strip off leading 'get' command argument */
+ argc--;
+ argv++;
+
+ if (!strcmp(argv[0], "dt"))
+ return adtimg_get_dt(argc, argv);
+
+ printf("Error: Wrong argument '%s'\n", argv[0]);
+ return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl cmd_adtimg_sub[] = {
+ U_BOOT_CMD_MKENT(addr, CONFIG_SYS_MAXARGS, 1, do_adtimg_addr, "", ""),
+ U_BOOT_CMD_MKENT(dump, CONFIG_SYS_MAXARGS, 1, do_adtimg_dump, "", ""),
+ U_BOOT_CMD_MKENT(get, CONFIG_SYS_MAXARGS, 1, do_adtimg_get, "", ""),
+};
+
+static int do_adtimg(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ cp = find_cmd_tbl(argv[1], cmd_adtimg_sub, ARRAY_SIZE(cmd_adtimg_sub));
+
+ /* Strip off leading 'adtimg' command argument */
+ argc--;
+ argv++;
+
+ if (!cp || argc > cp->maxargs)
+ return CMD_RET_USAGE;
+ if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp))
+ return CMD_RET_SUCCESS;
+
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(
+ adtimg, CONFIG_SYS_MAXARGS, 0, do_adtimg,
+ "manipulate dtb/dtbo Android image",
+ "addr <addr> - Set image location to <addr>\n"
+ "adtimg dump - Print out image contents\n"
+ "adtimg get dt --index=<index> [avar [svar]] - Get DT address/size by index\n"
+ "\n"
+ "Legend:\n"
+ " - <addr>: DTB/DTBO image address (hex) in RAM\n"
+ " - <index>: index (hex/dec) of desired DT in the image\n"
+ " - <avar>: variable name to contain DT address (hex)\n"
+ " - <svar>: variable name to contain DT size (hex)"
+);
diff --git a/cmd/aes.c b/cmd/aes.c
new file mode 100644
index 00000000000..3fd83013ffe
--- /dev/null
+++ b/cmd/aes.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014 Marek Vasut <marex@denx.de>
+ * Copyright (C) 2025 Ion Agorria <ion@agorria.com>
+ *
+ * Command for AES-[128/192/256] operations.
+ */
+
+#include <command.h>
+#include <uboot_aes.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <linux/compiler.h>
+#include <mapmem.h>
+#include <vsprintf.h>
+#include <dm/uclass.h>
+#include <dm/device.h>
+
+u32 aes_get_key_len(char *command)
+{
+ u32 key_len = AES128_KEY_LENGTH;
+
+ if (!strcmp(command, "aes.192"))
+ key_len = AES192_KEY_LENGTH;
+ else if (!strcmp(command, "aes.256"))
+ key_len = AES256_KEY_LENGTH;
+
+ return key_len;
+}
+
+int aes_get_driver(struct udevice **dev)
+{
+ int ret;
+
+ ret = uclass_get_device(UCLASS_AES, 0, dev);
+ if (ret) {
+ printf("Failed to get AES driver: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int cmd_aes_cbc_simple(int argc, char *const argv[], u32 key_len)
+{
+ uint32_t key_addr, iv_addr, src_addr, dst_addr, len;
+ uint8_t *key_ptr, *iv_ptr, *src_ptr, *dst_ptr;
+ u8 key_exp[AES256_EXPAND_KEY_LENGTH];
+ u32 aes_blocks;
+ int enc;
+
+ if (argc != 7)
+ return CMD_RET_USAGE;
+
+ if (!strncmp(argv[1], "enc", 3))
+ enc = 1;
+ else if (!strncmp(argv[1], "dec", 3))
+ enc = 0;
+ else
+ return CMD_RET_USAGE;
+
+ key_addr = hextoul(argv[2], NULL);
+ iv_addr = hextoul(argv[3], NULL);
+ src_addr = hextoul(argv[4], NULL);
+ dst_addr = hextoul(argv[5], NULL);
+ len = hextoul(argv[6], NULL);
+
+ key_ptr = (uint8_t *)map_sysmem(key_addr, key_len);
+ iv_ptr = (uint8_t *)map_sysmem(iv_addr, 128 / 8);
+ src_ptr = (uint8_t *)map_sysmem(src_addr, len);
+ dst_ptr = (uint8_t *)map_sysmem(dst_addr, len);
+
+ /* First we expand the key. */
+ aes_expand_key(key_ptr, key_len, key_exp);
+
+ /* Calculate the number of AES blocks to encrypt. */
+ aes_blocks = DIV_ROUND_UP(len, AES_BLOCK_LENGTH);
+
+ if (enc)
+ aes_cbc_encrypt_blocks(key_len, key_exp, iv_ptr, src_ptr,
+ dst_ptr, aes_blocks);
+ else
+ aes_cbc_decrypt_blocks(key_len, key_exp, iv_ptr, src_ptr,
+ dst_ptr, aes_blocks);
+
+ unmap_sysmem(key_ptr);
+ unmap_sysmem(iv_ptr);
+ unmap_sysmem(src_ptr);
+ unmap_sysmem(dst_ptr);
+
+ return CMD_RET_SUCCESS;
+}
+
+int cmd_aes_get_slots(void)
+{
+ struct udevice *dev;
+ u8 slots;
+ int ret;
+
+ ret = aes_get_driver(&dev);
+ if (ret)
+ return ret;
+
+ slots = dm_aes_get_available_key_slots(dev);
+ printf("Available slots: %d\n", slots);
+
+ return CMD_RET_SUCCESS;
+}
+
+int cmd_aes_set_key(int argc, char *const argv[], u32 key_len)
+{
+ struct udevice *dev;
+ u32 key_addr, slot;
+ u8 *key_ptr;
+ int ret;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ ret = aes_get_driver(&dev);
+ if (ret)
+ return ret;
+
+ key_addr = hextoul(argv[2], NULL);
+ slot = hextoul(argv[3], NULL);
+
+ key_ptr = (uint8_t *)map_sysmem(key_addr, key_len);
+
+ ret = dm_aes_set_key_for_key_slot(dev, key_len * 8, key_ptr, slot);
+ unmap_sysmem(key_ptr);
+ if (ret) {
+ printf("Unable to set key at slot: %d\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+int cmd_aes_select_slot(int argc, char *const argv[], u32 key_len)
+{
+ struct udevice *dev;
+ u32 slot;
+ int ret;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ ret = aes_get_driver(&dev);
+ if (ret)
+ return ret;
+
+ slot = hextoul(argv[2], NULL);
+
+ ret = dm_aes_select_key_slot(dev, key_len * 8, slot);
+ if (ret) {
+ printf("Unable to select key slot: %d\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+int cmd_aes_ecb(int argc, char *const argv[], u32 key_len)
+{
+ struct udevice *dev;
+ u32 src_addr, dst_addr, len;
+ u8 *src_ptr, *dst_ptr;
+ u32 aes_blocks;
+ int enc, ret;
+
+ if (argc != 6)
+ return CMD_RET_USAGE;
+
+ ret = aes_get_driver(&dev);
+ if (ret)
+ return ret;
+
+ if (!strncmp(argv[1], "enc", 3))
+ enc = 1;
+ else if (!strncmp(argv[1], "dec", 3))
+ enc = 0;
+ else
+ return CMD_RET_USAGE;
+
+ src_addr = hextoul(argv[3], NULL);
+ dst_addr = hextoul(argv[4], NULL);
+ len = hextoul(argv[5], NULL);
+
+ src_ptr = (uint8_t *)map_sysmem(src_addr, len);
+ dst_ptr = (uint8_t *)map_sysmem(dst_addr, len);
+
+ /* Calculate the number of AES blocks to encrypt. */
+ aes_blocks = DIV_ROUND_UP(len, AES_BLOCK_LENGTH);
+
+ if (enc)
+ ret = dm_aes_ecb_encrypt(dev, src_ptr, dst_ptr, aes_blocks);
+ else
+ ret = dm_aes_ecb_decrypt(dev, src_ptr, dst_ptr, aes_blocks);
+
+ unmap_sysmem(src_ptr);
+ unmap_sysmem(dst_ptr);
+
+ if (ret) {
+ printf("Unable to do ecb operation: %d\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+int cmd_aes_cbc(int argc, char *const argv[], u32 key_len)
+{
+ struct udevice *dev;
+ u32 iv_addr, src_addr, dst_addr, len;
+ u8 *iv_ptr, *src_ptr, *dst_ptr;
+ u32 aes_blocks;
+ int enc, ret;
+
+ if (argc != 7)
+ return CMD_RET_USAGE;
+
+ ret = aes_get_driver(&dev);
+ if (ret)
+ return ret;
+
+ if (!strncmp(argv[1], "enc", 3))
+ enc = 1;
+ else if (!strncmp(argv[1], "dec", 3))
+ enc = 0;
+ else
+ return CMD_RET_USAGE;
+
+ iv_addr = hextoul(argv[3], NULL);
+ src_addr = hextoul(argv[4], NULL);
+ dst_addr = hextoul(argv[5], NULL);
+ len = hextoul(argv[6], NULL);
+
+ iv_ptr = (uint8_t *)map_sysmem(iv_addr, AES_BLOCK_LENGTH);
+ src_ptr = (uint8_t *)map_sysmem(src_addr, len);
+ dst_ptr = (uint8_t *)map_sysmem(dst_addr, len);
+
+ /* Calculate the number of AES blocks to encrypt. */
+ aes_blocks = DIV_ROUND_UP(len, AES_BLOCK_LENGTH);
+
+ if (enc)
+ ret = dm_aes_cbc_encrypt(dev, iv_ptr, src_ptr, dst_ptr, aes_blocks);
+ else
+ ret = dm_aes_cbc_decrypt(dev, iv_ptr, src_ptr, dst_ptr, aes_blocks);
+
+ unmap_sysmem(iv_ptr);
+ unmap_sysmem(src_ptr);
+ unmap_sysmem(dst_ptr);
+
+ if (ret) {
+ printf("Unable to do cbc operation: %d\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * do_aes() - Handle the "aes" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ */
+static int do_aes(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ u32 key_len;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ key_len = aes_get_key_len(argv[0]);
+
+ if (!strncmp(argv[1], "enc", 3) || !strncmp(argv[1], "dec", 3))
+ return cmd_aes_cbc_simple(argc, argv, key_len);
+ else if (CONFIG_IS_ENABLED(DM_AES) && !strncmp(argv[1], "get_slots", 9))
+ return cmd_aes_get_slots();
+ else if (CONFIG_IS_ENABLED(DM_AES) && !strncmp(argv[1], "set_key", 7))
+ return cmd_aes_set_key(argc, argv, key_len);
+ else if (CONFIG_IS_ENABLED(DM_AES) && !strncmp(argv[1], "select_slot", 11))
+ return cmd_aes_select_slot(argc, argv, key_len);
+ else if (CONFIG_IS_ENABLED(DM_AES) && !strncmp(argv[1], "ecb", 3))
+ return cmd_aes_ecb(argc, argv, key_len);
+ else if (CONFIG_IS_ENABLED(DM_AES) && !strncmp(argv[1], "cbc", 3))
+ return cmd_aes_cbc(argc, argv, key_len);
+ else
+ return CMD_RET_USAGE;
+}
+
+/***************************************************/
+U_BOOT_LONGHELP(aes,
+ "[.128,.192,.256] enc key iv src dst len - CBC encrypt block of data $len bytes long\n"
+ " at address $src using a key at address\n"
+ " $key with initialization vector at address\n"
+ " $iv. Store the result at address $dst.\n"
+ " The $len size must be multiple of 16 bytes.\n"
+ " The $key and $iv must be 16 bytes long.\n"
+ "aes [.128,.192,.256] dec key iv src dst len - CBC decrypt block of data $len bytes long\n"
+ " at address $src using a key at address\n"
+ " $key with initialization vector at address\n"
+ " $iv. Store the result at address $dst.\n"
+ " The $len size must be multiple of 16 bytes.\n"
+ " The $key and $iv must be 16 bytes long."
+
+#if CONFIG_IS_ENABLED(DM_AES)
+ "\n"
+ "aes get_slots - Gives number of available key slots\n"
+ "aes [.128,.192,.256] set_key key slot - Load key at address $key into the slot $slot\n"
+ "aes [.128,.192,.256] select_slot slot - Select current active key slot\n"
+ "aes [.128,.192,.256] ecb enc src dst len - ECB encrypt block of data $len bytes long\n"
+ " at address $src using a key at current\n"
+ " slot. Store the result at address $dst.\n"
+ " The $len size must be multiple of 16 bytes.\n"
+ "aes [.128,.192,.256] ecb dec src dst len - ECB decrypt block of data $len bytes long\n"
+ " at address $src using a key at current\n"
+ " slot. Store the result at address $dst.\n"
+ " The $len size must be multiple of 16 bytes.\n"
+ "aes [.128,.192,.256] cbc enc iv src dst len - CBC encrypt block of data $len bytes long\n"
+ " at address $src using a key at current\n"
+ " slot with initialization vector at address\n"
+ " $iv. Store the result at address $dst.\n"
+ " The $len size must be multiple of 16 bytes.\n"
+ " The $iv must be 16 bytes long.\n"
+ "aes [.128,.192,.256] cbc dec iv src dst len - CBC decrypt block of data $len bytes long\n"
+ " at address $src using a key at current\n"
+ " slot with initialization vector at address\n"
+ " $iv. Store the result at address $dst.\n"
+ " The $len size must be multiple of 16 bytes.\n"
+ " The $iv must be 16 bytes long."
+#endif
+);
+
+U_BOOT_CMD(
+ aes, 7, 1, do_aes,
+ "AES 128/192/256 operations",
+ aes_help_text
+);
diff --git a/cmd/arm/Makefile b/cmd/arm/Makefile
new file mode 100644
index 00000000000..94367dcb459
--- /dev/null
+++ b/cmd/arm/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+ifdef CONFIG_ARM64
+obj-$(CONFIG_CMD_EXCEPTION) += exception64.o
+else
+obj-$(CONFIG_CMD_EXCEPTION) += exception.o
+endif
diff --git a/cmd/arm/exception.c b/cmd/arm/exception.c
new file mode 100644
index 00000000000..8857f121604
--- /dev/null
+++ b/cmd/arm/exception.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The 'exception' command can be used for testing exception handling.
+ *
+ * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <command.h>
+
+static int do_unaligned(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ /*
+ * The LDRD instruction requires the data source to be four byte aligned
+ * even if strict alignment fault checking is disabled in the system
+ * control register.
+ */
+ asm volatile (
+ "MOV r5, sp\n"
+ "ADD r5, #1\n"
+ "LDRD r6, r7, [r5]\n");
+ return CMD_RET_FAILURE;
+}
+
+static int do_breakpoint(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ asm volatile ("BKPT #123\n");
+ return CMD_RET_FAILURE;
+}
+
+static int do_undefined(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ /*
+ * 0xe7f...f. is undefined in ARM mode
+ * 0xde.. is undefined in Thumb mode
+ */
+ asm volatile (".word 0xe7f7defb\n");
+ return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl cmd_sub[] = {
+ U_BOOT_CMD_MKENT(breakpoint, CONFIG_SYS_MAXARGS, 1, do_breakpoint,
+ "", ""),
+ U_BOOT_CMD_MKENT(unaligned, CONFIG_SYS_MAXARGS, 1, do_unaligned,
+ "", ""),
+ U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined,
+ "", ""),
+};
+
+U_BOOT_LONGHELP(exception,
+ "<ex>\n"
+ " The following exceptions are available:\n"
+ " breakpoint - prefetch abort\n"
+ " unaligned - data abort\n"
+ " undefined - undefined instruction\n");
+
+#include <exception.h>
diff --git a/cmd/arm/exception64.c b/cmd/arm/exception64.c
new file mode 100644
index 00000000000..4c5b953168c
--- /dev/null
+++ b/cmd/arm/exception64.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The 'exception' command can be used for testing exception handling.
+ *
+ * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <command.h>
+#include <linux/bitops.h>
+
+static int do_undefined(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ /*
+ * Instructions starting with the upper 16 bits all 0 are permanently
+ * undefined. The lower 16 bits can be used for some kind of immediate.
+ * --- ARMv8 ARM (ARM DDI 0487G.a C6.2.339: "UDF")
+ */
+ asm volatile (".word 0x00001234\n");
+
+ return CMD_RET_FAILURE;
+}
+
+/*
+ * The ID_AA64MMFR2_EL1 register name is only know to binutils for ARMv8.2
+ * and later architecture revisions. However the register is valid regardless
+ * of binutils architecture support or the core the code is running on, so
+ * just use the generic encoding.
+ */
+#define ID_AA64MMFR2_EL1 "S3_0_C0_C7_2"
+
+static int do_unaligned(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ uint64_t reg;
+
+ /*
+ * The unaligned LDAR access below is only guaranteed to generate an
+ * alignment fault on cores not implementing FEAT_LSE2. To avoid false
+ * negatives, check this condition before we exectute LDAR.
+ */
+ asm ("mrs %0, "ID_AA64MMFR2_EL1"\n" : "=r" (reg));
+ if (reg & GENMASK(35, 32)) {
+ printf("unaligned access check only supported on pre-v8.4 cores\n");
+ return CMD_RET_FAILURE;
+ }
+
+ /*
+ * The load acquire instruction requires the data source to be
+ * naturally aligned, and will fault even if strict alignment fault
+ * checking is disabled (but only without FEAT_LSE2).
+ * --- ARMv8 ARM (ARM DDI 0487G.a B2.5.2: "Alignment of data accesses")
+ */
+ asm volatile (
+ "mov x1, sp\n\t"
+ "orr x1, x1, #3\n\t"
+ "ldar x0, [x1]\n"
+ ::: "x0", "x1" );
+
+ return CMD_RET_FAILURE;
+}
+
+static int do_breakpoint(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ asm volatile ("brk #123\n");
+
+ return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl cmd_sub[] = {
+ U_BOOT_CMD_MKENT(breakpoint, CONFIG_SYS_MAXARGS, 1, do_breakpoint,
+ "", ""),
+ U_BOOT_CMD_MKENT(unaligned, CONFIG_SYS_MAXARGS, 1, do_unaligned,
+ "", ""),
+ U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined,
+ "", ""),
+};
+
+U_BOOT_LONGHELP(exception,
+ "<ex>\n"
+ " The following exceptions are available:\n"
+ " breakpoint - breakpoint instruction exception\n"
+ " unaligned - unaligned LDAR data abort\n"
+ " undefined - undefined instruction exception\n");
+
+#include <exception.h>
diff --git a/cmd/armffa.c b/cmd/armffa.c
new file mode 100644
index 00000000000..181e31bc49a
--- /dev/null
+++ b/cmd/armffa.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+ */
+#include <arm_ffa.h>
+#include <command.h>
+#include <dm.h>
+#include <mapmem.h>
+#include <stdlib.h>
+#include <asm/io.h>
+
+/* Select the right physical address formatting according to the platform */
+#ifdef CONFIG_PHYS_64BIT
+#define PhysAddrLength "ll"
+#else
+#define PhysAddrLength ""
+#endif
+#define PHYS_ADDR_LN "%" PhysAddrLength "x"
+
+/**
+ * ffa_get_dev() - Return the FF-A device
+ * @devp: pointer to the FF-A device
+ *
+ * Search for the FF-A device.
+ *
+ * Return:
+ * 0 on success. Otherwise, failure
+ */
+static int ffa_get_dev(struct udevice **devp)
+{
+ int ret;
+
+ ret = uclass_first_device_err(UCLASS_FFA, devp);
+ if (ret) {
+ log_err("Cannot find FF-A bus device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * do_ffa_getpart() - implementation of the getpart subcommand
+ * @cmdtp: Command Table
+ * @flag: flags
+ * @argc: number of arguments
+ * @argv: arguments
+ *
+ * Query a secure partition information. The secure partition UUID is provided
+ * as an argument. The function uses the arm_ffa driver
+ * partition_info_get operation which implements FFA_PARTITION_INFO_GET
+ * ABI to retrieve the data. The input UUID string is expected to be in big
+ * endian format.
+ *
+ * Return:
+ *
+ * CMD_RET_SUCCESS: on success, otherwise failure
+ */
+static int do_ffa_getpart(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 count = 0;
+ int ret;
+ struct ffa_partition_desc *descs;
+ u32 i;
+ struct udevice *dev;
+
+ if (argc != 2) {
+ log_err("Missing argument\n");
+ return CMD_RET_USAGE;
+ }
+
+ ret = ffa_get_dev(&dev);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ /* Ask the driver to fill the buffer with the SPs info */
+
+ ret = ffa_partition_info_get(dev, argv[1], &count, &descs);
+ if (ret) {
+ log_err("Failure in querying partition(s) info (error code: %d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ /* SPs found , show the partition information */
+ for (i = 0; i < count ; i++) {
+ log_info("Partition: id = %x , exec_ctxt %x , properties %x\n",
+ descs[i].info.id,
+ descs[i].info.exec_ctxt,
+ descs[i].info.properties);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * do_ffa_ping() - implementation of the ping subcommand
+ * @cmdtp: Command Table
+ * @flag: flags
+ * @argc: number of arguments
+ * @argv: arguments
+ *
+ * Send data to a secure partition. The secure partition UUID is provided
+ * as an argument. Use the arm_ffa driver sync_send_receive operation
+ * which implements FFA_MSG_SEND_DIRECT_{REQ,RESP} ABIs to send/receive data.
+ *
+ * Return:
+ *
+ * CMD_RET_SUCCESS: on success, otherwise failure
+ */
+static int do_ffa_ping(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct ffa_send_direct_data msg = {
+ .data0 = 0xaaaaaaaa,
+ .data1 = 0xbbbbbbbb,
+ .data2 = 0xcccccccc,
+ .data3 = 0xdddddddd,
+ .data4 = 0xeeeeeeee,
+ };
+ u16 part_id;
+ int ret;
+ struct udevice *dev;
+
+ if (argc != 2) {
+ log_err("Missing argument\n");
+ return CMD_RET_USAGE;
+ }
+
+ part_id = strtoul(argv[1], NULL, 16);
+ if (!part_id) {
+ log_err("Partition ID can not be 0\n");
+ return CMD_RET_USAGE;
+ }
+
+ ret = ffa_get_dev(&dev);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ ret = ffa_sync_send_receive(dev, part_id, &msg, 1);
+ if (!ret) {
+ u8 cnt;
+
+ log_info("SP response:\n[LSB]\n");
+ for (cnt = 0;
+ cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64);
+ cnt++)
+ log_info("%llx\n", ((u64 *)&msg)[cnt]);
+ return CMD_RET_SUCCESS;
+ }
+
+ log_err("Sending direct request error (%d)\n", ret);
+ return CMD_RET_FAILURE;
+}
+
+/**
+ *do_ffa_devlist() - implementation of the devlist subcommand
+ * @cmdtp: [in] Command Table
+ * @flag: flags
+ * @argc: number of arguments
+ * @argv: arguments
+ *
+ * Query the device belonging to the UCLASS_FFA
+ * class.
+ *
+ * Return:
+ *
+ * CMD_RET_SUCCESS: on success, otherwise failure
+ */
+static int do_ffa_devlist(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = ffa_get_dev(&dev);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ log_info("device %s, addr " PHYS_ADDR_LN ", driver %s, ops " PHYS_ADDR_LN "\n",
+ dev->name,
+ map_to_sysmem(dev),
+ dev->driver->name,
+ map_to_sysmem(dev->driver->ops));
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_LONGHELP(armffa,
+ "getpart <partition UUID>\n"
+ " - lists the partition(s) info\n"
+ "ping <partition ID>\n"
+ " - sends a data pattern to the specified partition\n"
+ "devlist\n"
+ " - displays information about the FF-A device/driver\n");
+
+U_BOOT_CMD_WITH_SUBCMDS(armffa, "Arm FF-A test command", armffa_help_text,
+ U_BOOT_SUBCMD_MKENT(getpart, 2, 1, do_ffa_getpart),
+ U_BOOT_SUBCMD_MKENT(ping, 2, 1, do_ffa_ping),
+ U_BOOT_SUBCMD_MKENT(devlist, 1, 1, do_ffa_devlist));
diff --git a/cmd/armflash.c b/cmd/armflash.c
new file mode 100644
index 00000000000..cde275c881b
--- /dev/null
+++ b/cmd/armflash.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015
+ * Linus Walleij, Linaro
+ *
+ * Support for ARM Flash Partitions
+ */
+#include <command.h>
+#include <console.h>
+#include <env.h>
+#include <flash.h>
+#include <vsprintf.h>
+#include <linux/string.h>
+#include <asm/io.h>
+
+#define MAX_REGIONS 4
+#define MAX_IMAGES 32
+
+struct afs_region {
+ u32 load_address;
+ u32 size;
+ u32 offset;
+};
+
+struct afs_image {
+ flash_info_t *flinfo;
+ const char *name;
+ u32 version;
+ u32 entrypoint;
+ u32 attributes;
+ u32 region_count;
+ struct afs_region regions[MAX_REGIONS];
+ ulong flash_mem_start;
+ ulong flash_mem_end;
+};
+
+static struct afs_image afs_images[MAX_IMAGES];
+static int num_afs_images;
+
+static u32 compute_crc(ulong start, u32 len)
+{
+ u32 sum = 0;
+ int i;
+
+ if (len % 4 != 0) {
+ printf("bad checksumming\n");
+ return 0;
+ }
+
+ for (i = 0; i < len; i += 4) {
+ u32 val;
+
+ val = readl((void *)start + i);
+ if (val > ~sum)
+ sum++;
+ sum += val;
+ }
+ return ~sum;
+}
+
+static void parse_bank(ulong bank)
+{
+ int i;
+ ulong flstart, flend;
+ flash_info_t *info;
+
+ info = &flash_info[bank];
+ if (info->flash_id != FLASH_MAN_CFI) {
+ printf("Bank %lu: missing or unknown FLASH type\n", bank);
+ return;
+ }
+ if (!info->sector_count) {
+ printf("Bank %lu: no FLASH sectors\n", bank);
+ return;
+ }
+
+ flstart = info->start[0];
+ flend = flstart + info->size;
+
+ for (i = 0; i < info->sector_count; ++i) {
+ ulong secend;
+ u32 foot1, foot2;
+
+ if (ctrlc())
+ break;
+
+ if (i == info->sector_count-1)
+ secend = flend;
+ else
+ secend = info->start[i+1];
+
+ /* Check for v1 header */
+ foot1 = readl((void *)secend - 0x0c);
+ if (foot1 == 0xA0FFFF9FU) {
+ struct afs_image *afi = &afs_images[num_afs_images];
+ ulong imginfo;
+
+ afi->flinfo = info;
+ afi->version = 1;
+ afi->flash_mem_start = readl((void *)secend - 0x10);
+ afi->flash_mem_end = readl((void *)secend - 0x14);
+ afi->attributes = readl((void *)secend - 0x08);
+ /* Adjust to even address */
+ imginfo = afi->flash_mem_end + afi->flash_mem_end % 4;
+ /* Record as a single region */
+ afi->region_count = 1;
+ afi->regions[0].offset = readl((void *)imginfo + 0x04);
+ afi->regions[0].load_address =
+ readl((void *)imginfo + 0x08);
+ afi->regions[0].size = readl((void *)imginfo + 0x0C);
+ afi->entrypoint = readl((void *)imginfo + 0x10);
+ afi->name = (const char *)imginfo + 0x14;
+ num_afs_images++;
+ }
+
+ /* Check for v2 header */
+ foot1 = readl((void *)secend - 0x04);
+ foot2 = readl((void *)secend - 0x08);
+ /* This makes up the string "HSLFTOOF" flash footer */
+ if (foot1 == 0x464F4F54U && foot2 == 0x464C5348U) {
+ struct afs_image *afi = &afs_images[num_afs_images];
+ ulong imginfo;
+ u32 block_start, block_end;
+ int j;
+
+ afi->flinfo = info;
+ afi->version = readl((void *)secend - 0x0c);
+ imginfo = secend - 0x30 - readl((void *)secend - 0x10);
+ afi->name = (const char *)secend - 0x30;
+
+ afi->entrypoint = readl((void *)imginfo+0x08);
+ afi->attributes = readl((void *)imginfo+0x0c);
+ afi->region_count = readl((void *)imginfo+0x10);
+ block_start = readl((void *)imginfo+0x54);
+ block_end = readl((void *)imginfo+0x58);
+ afi->flash_mem_start = afi->flinfo->start[block_start];
+ afi->flash_mem_end = afi->flinfo->start[block_end];
+
+ /*
+ * Check footer CRC, the algorithm saves the inverse
+ * checksum as part of the summed words, and thus
+ * the result should be zero.
+ */
+ if (compute_crc(imginfo + 8, 0x88) != 0) {
+ printf("BAD CRC on ARM image info\n");
+ printf("(continuing anyway)\n");
+ }
+
+ /* Parse regions */
+ for (j = 0; j < afi->region_count; j++) {
+ afi->regions[j].load_address =
+ readl((void *)imginfo+0x14 + j*0x10);
+ afi->regions[j].size =
+ readl((void *)imginfo+0x18 + j*0x10);
+ afi->regions[j].offset =
+ readl((void *)imginfo+0x1c + j*0x10);
+ /*
+ * At offset 0x20 + j*0x10 there is a region
+ * checksum which seems to be the running
+ * sum + 3, however since we anyway checksum
+ * the entire footer this is skipped over for
+ * checking here.
+ */
+ }
+ num_afs_images++;
+ }
+ }
+}
+
+static void parse_flash(void)
+{
+ ulong bank;
+
+ /* We have already parsed the images in flash */
+ if (num_afs_images > 0)
+ return;
+ for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank)
+ parse_bank(bank);
+}
+
+static int load_image(const char * const name, const ulong address)
+{
+ struct afs_image *afi = NULL;
+ int i;
+ loff_t len_read = 0;
+
+ parse_flash();
+ for (i = 0; i < num_afs_images; i++) {
+ struct afs_image *tmp = &afs_images[i];
+
+ if (!strcmp(tmp->name, name)) {
+ afi = tmp;
+ break;
+ }
+ }
+ if (!afi) {
+ printf("image \"%s\" not found in flash\n", name);
+ return CMD_RET_FAILURE;
+ }
+
+ for (i = 0; i < afi->region_count; i++) {
+ ulong from, to;
+ u32 size;
+
+ from = afi->flash_mem_start + afi->regions[i].offset;
+ if (address) {
+ to = address;
+ } else if (afi->regions[i].load_address) {
+ to = afi->regions[i].load_address;
+ } else {
+ printf("no valid load address\n");
+ return CMD_RET_FAILURE;
+ }
+
+ size = afi->regions[i].size;
+ memcpy((void *)to, (void *)from, size);
+
+ printf("loaded region %d from %08lX to %08lX, %08X bytes\n",
+ i,
+ from,
+ to,
+ size);
+
+ len_read += size;
+ }
+
+ env_set_hex("filesize", len_read);
+
+ return CMD_RET_SUCCESS;
+}
+
+static void print_images(void)
+{
+ int i;
+
+ parse_flash();
+ for (i = 0; i < num_afs_images; i++) {
+ struct afs_image *afi = &afs_images[i];
+ int j;
+
+ printf("Image: \"%s\" (v%d):\n", afi->name, afi->version);
+ printf(" Entry point: 0x%08X\n", afi->entrypoint);
+ printf(" Attributes: 0x%08X: ", afi->attributes);
+ if (afi->attributes == 0x01)
+ printf("ARM executable");
+ if (afi->attributes == 0x08)
+ printf("ARM backup");
+ printf("\n");
+ printf(" Flash mem start: 0x%08lX\n",
+ afi->flash_mem_start);
+ printf(" Flash mem end: 0x%08lX\n",
+ afi->flash_mem_end);
+ for (j = 0; j < afi->region_count; j++) {
+ printf(" region %d\n"
+ " load address: %08X\n"
+ " size: %08X\n"
+ " offset: %08X\n",
+ j,
+ afi->regions[j].load_address,
+ afi->regions[j].size,
+ afi->regions[j].offset);
+ }
+ }
+}
+
+static int exists(const char * const name)
+{
+ int i;
+
+ parse_flash();
+ for (i = 0; i < num_afs_images; i++) {
+ struct afs_image *afi = &afs_images[i];
+
+ if (strcmp(afi->name, name) == 0)
+ return CMD_RET_SUCCESS;
+ }
+ return CMD_RET_FAILURE;
+}
+
+static int do_afs(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int ret = CMD_RET_SUCCESS;
+
+ if (argc == 1) {
+ print_images();
+ } else if (argc == 3 && !strcmp(argv[1], "exists")) {
+ ret = exists(argv[2]);
+ } else if (argc == 3 && !strcmp(argv[1], "load")) {
+ ret = load_image(argv[2], 0x0);
+ } else if (argc == 4 && !strcmp(argv[1], "load")) {
+ ulong load_addr;
+
+ load_addr = hextoul(argv[3], NULL);
+ ret = load_image(argv[2], load_addr);
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ return ret;
+}
+
+U_BOOT_CMD(afs, 4, 0, do_afs, "show AFS partitions",
+ "no arguments\n"
+ " - list images in flash\n"
+ "exists <image>\n"
+ " - returns 1 if an image exists, else 0\n"
+ "load <image>\n"
+ " - load an image to the location indicated in the header\n"
+ "load <image> 0x<address>\n"
+ " - load an image to the location specified\n");
diff --git a/cmd/avb.c b/cmd/avb.c
new file mode 100644
index 00000000000..8fbd48ee5a2
--- /dev/null
+++ b/cmd/avb.c
@@ -0,0 +1,485 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018, Linaro Limited
+ */
+
+#include <avb_verify.h>
+#include <command.h>
+#include <env.h>
+#include <image.h>
+#include <malloc.h>
+#include <mmc.h>
+
+#define AVB_BOOTARGS "avb_bootargs"
+
+static struct AvbOps *avb_ops;
+
+int do_avb_init(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ unsigned long mmc_dev;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ mmc_dev = hextoul(argv[1], NULL);
+
+ if (avb_ops)
+ avb_ops_free(avb_ops);
+
+ avb_ops = avb_ops_alloc(mmc_dev);
+ if (avb_ops)
+ return CMD_RET_SUCCESS;
+ else
+ printf("Can't allocate AvbOps");
+
+ printf("Failed to initialize AVB\n");
+
+ return CMD_RET_FAILURE;
+}
+
+int do_avb_read_part(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *part;
+ s64 offset;
+ size_t bytes, bytes_read = 0;
+ void *buffer;
+ int ret;
+
+ if (!avb_ops) {
+ printf("AVB is not initialized, please run 'avb init <id>'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc != 5)
+ return CMD_RET_USAGE;
+
+ part = argv[1];
+ offset = hextoul(argv[2], NULL);
+ bytes = hextoul(argv[3], NULL);
+ buffer = (void *)hextoul(argv[4], NULL);
+
+ ret = avb_ops->read_from_partition(avb_ops, part, offset,
+ bytes, buffer, &bytes_read);
+ if (ret == AVB_IO_RESULT_OK) {
+ printf("Read %zu bytes\n", bytes_read);
+ return CMD_RET_SUCCESS;
+ }
+
+ printf("Failed to read from partition '%s', err = %d\n",
+ part, ret);
+
+ return CMD_RET_FAILURE;
+}
+
+int do_avb_read_part_hex(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *part;
+ s64 offset;
+ size_t bytes, bytes_read = 0;
+ char *buffer;
+ int ret;
+
+ if (!avb_ops) {
+ printf("AVB is not initialized, please run 'avb init <id>'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ part = argv[1];
+ offset = hextoul(argv[2], NULL);
+ bytes = hextoul(argv[3], NULL);
+
+ buffer = malloc(bytes);
+ if (!buffer) {
+ printf("Failed to tlb_allocate buffer for data\n");
+ return CMD_RET_FAILURE;
+ }
+ memset(buffer, 0, bytes);
+
+ ret = avb_ops->read_from_partition(avb_ops, part, offset,
+ bytes, buffer, &bytes_read);
+ if (ret == AVB_IO_RESULT_OK) {
+ printf("Requested %zu, read %zu bytes\n", bytes, bytes_read);
+ printf("Data: ");
+ for (int i = 0; i < bytes_read; i++)
+ printf("%02X", buffer[i]);
+
+ printf("\n");
+
+ free(buffer);
+ return CMD_RET_SUCCESS;
+ }
+
+ printf("Failed to read from partition '%s', err = %d\n",
+ part, ret);
+
+ free(buffer);
+ return CMD_RET_FAILURE;
+}
+
+int do_avb_write_part(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *part;
+ s64 offset;
+ size_t bytes;
+ void *buffer;
+ int ret;
+
+ if (!avb_ops) {
+ printf("AVB is not initialized, please run 'avb init <id>'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc != 5)
+ return CMD_RET_USAGE;
+
+ part = argv[1];
+ offset = hextoul(argv[2], NULL);
+ bytes = hextoul(argv[3], NULL);
+ buffer = (void *)hextoul(argv[4], NULL);
+
+ ret = avb_ops->write_to_partition(avb_ops, part, offset,
+ bytes, buffer);
+ if (ret == AVB_IO_RESULT_OK) {
+ printf("Wrote %zu bytes\n", bytes);
+ return CMD_RET_SUCCESS;
+ }
+
+ printf("Failed to write in partition '%s', err = %d\n",
+ part, ret);
+
+ return CMD_RET_FAILURE;
+}
+
+int do_avb_read_rb(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ size_t index;
+ u64 rb_idx;
+ int ret;
+
+ if (!avb_ops) {
+ printf("AVB is not initialized, please run 'avb init <id>'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ index = (size_t)hextoul(argv[1], NULL);
+
+ ret = avb_ops->read_rollback_index(avb_ops, index, &rb_idx);
+ if (ret == AVB_IO_RESULT_OK) {
+ printf("Rollback index: %llx\n", rb_idx);
+ return CMD_RET_SUCCESS;
+ }
+
+ printf("Failed to read rollback index id = %zu, err = %d\n",
+ index, ret);
+
+ return CMD_RET_FAILURE;
+}
+
+int do_avb_write_rb(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ size_t index;
+ u64 rb_idx;
+ int ret;
+
+ if (!avb_ops) {
+ printf("AVB is not initialized, please run 'avb init <id>'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ index = (size_t)hextoul(argv[1], NULL);
+ rb_idx = hextoul(argv[2], NULL);
+
+ ret = avb_ops->write_rollback_index(avb_ops, index, rb_idx);
+ if (ret == AVB_IO_RESULT_OK)
+ return CMD_RET_SUCCESS;
+
+ printf("Failed to write rollback index id = %zu, err = %d\n",
+ index, ret);
+
+ return CMD_RET_FAILURE;
+}
+
+int do_avb_get_uuid(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ const char *part;
+ char buffer[UUID_STR_LEN + 1];
+ int ret;
+
+ if (!avb_ops) {
+ printf("AVB is not initialized, please run 'avb init <id>'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ part = argv[1];
+
+ ret = avb_ops->get_unique_guid_for_partition(avb_ops, part,
+ buffer,
+ UUID_STR_LEN + 1);
+ if (ret == AVB_IO_RESULT_OK) {
+ printf("'%s' UUID: %s\n", part, buffer);
+ return CMD_RET_SUCCESS;
+ }
+
+ printf("Failed to read partition '%s' UUID, err = %d\n",
+ part, ret);
+
+ return CMD_RET_FAILURE;
+}
+
+int do_avb_verify_part(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ const char * const requested_partitions[] = {"boot", NULL};
+ AvbSlotVerifyResult slot_result;
+ AvbSlotVerifyData *out_data;
+ enum avb_boot_state boot_state;
+ char *cmdline;
+ char *extra_args;
+ char *slot_suffix = "";
+ int ret;
+
+ bool unlocked = false;
+ int res = CMD_RET_FAILURE;
+
+ if (!avb_ops) {
+ printf("AVB is not initialized, please run 'avb init <id>'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc < 1 || argc > 2)
+ return CMD_RET_USAGE;
+
+ if (argc == 2)
+ slot_suffix = argv[1];
+
+ printf("## Android Verified Boot 2.0 version %s\n",
+ avb_version_string());
+
+ ret = avb_ops->read_is_device_unlocked(avb_ops, &unlocked);
+ if (ret != AVB_IO_RESULT_OK) {
+ printf("Can't determine device lock state, err = %d\n",
+ ret);
+ return CMD_RET_FAILURE;
+ }
+
+ slot_result =
+ avb_slot_verify(avb_ops,
+ requested_partitions,
+ slot_suffix,
+ unlocked,
+ AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
+ &out_data);
+
+ /*
+ * LOCKED devices with custom root of trust setup is not supported (YELLOW)
+ */
+ if (slot_result == AVB_SLOT_VERIFY_RESULT_OK) {
+ printf("Verification passed successfully\n");
+
+ /*
+ * ORANGE state indicates that device may be freely modified.
+ * Device integrity is left to the user to verify out-of-band.
+ */
+ if (unlocked)
+ boot_state = AVB_ORANGE;
+ else
+ boot_state = AVB_GREEN;
+
+ /* export boot state to AVB_BOOTARGS env var */
+ extra_args = avb_set_state(avb_ops, boot_state);
+ if (extra_args)
+ cmdline = append_cmd_line(out_data->cmdline,
+ extra_args);
+ else
+ cmdline = out_data->cmdline;
+
+ env_set(AVB_BOOTARGS, cmdline);
+
+ res = CMD_RET_SUCCESS;
+ } else {
+ printf("Verification failed, reason: %s\n", str_avb_slot_error(slot_result));
+ }
+
+ if (out_data)
+ avb_slot_verify_data_free(out_data);
+
+ return res;
+}
+
+int do_avb_is_unlocked(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ bool unlock;
+ int ret;
+
+ if (!avb_ops) {
+ printf("AVB is not initialized, please run 'avb init <id>'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc != 1) {
+ printf("--%s(-1)\n", __func__);
+ return CMD_RET_USAGE;
+ }
+
+ ret = avb_ops->read_is_device_unlocked(avb_ops, &unlock);
+ if (ret == AVB_IO_RESULT_OK) {
+ printf("Unlocked = %d\n", unlock);
+ return CMD_RET_SUCCESS;
+ }
+
+ printf("Can't determine device lock state, err = %d\n",
+ ret);
+
+ return CMD_RET_FAILURE;
+}
+
+int do_avb_read_pvalue(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *name;
+ size_t bytes;
+ size_t bytes_read;
+ void *buffer;
+ char *endp;
+ int ret;
+
+ if (!avb_ops) {
+ printf("AVB is not initialized, please run 'avb init <id>'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ name = argv[1];
+ bytes = dectoul(argv[2], &endp);
+ if (*endp && *endp != '\n')
+ return CMD_RET_USAGE;
+
+ buffer = malloc(bytes);
+ if (!buffer)
+ return CMD_RET_FAILURE;
+
+ ret = avb_ops->read_persistent_value(avb_ops, name, bytes,
+ buffer, &bytes_read);
+ if (ret == AVB_IO_RESULT_OK) {
+ printf("Read %zu bytes, value = %s\n", bytes_read,
+ (char *)buffer);
+ free(buffer);
+ return CMD_RET_SUCCESS;
+ }
+
+ printf("Failed to read persistent value, err = %d\n", ret);
+
+ free(buffer);
+
+ return CMD_RET_FAILURE;
+}
+
+int do_avb_write_pvalue(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *name;
+ const char *value;
+ int ret;
+
+ if (!avb_ops) {
+ printf("AVB is not initialized, please run 'avb init <id>'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ name = argv[1];
+ value = argv[2];
+
+ ret = avb_ops->write_persistent_value(avb_ops, name,
+ strlen(value) + 1,
+ (const uint8_t *)value);
+ if (ret == AVB_IO_RESULT_OK) {
+ printf("Wrote %zu bytes\n", strlen(value) + 1);
+ return CMD_RET_SUCCESS;
+ }
+
+ printf("Failed to write persistent value `%s` = `%s`, err = %d\n",
+ name, value, ret);
+
+ return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl cmd_avb[] = {
+ U_BOOT_CMD_MKENT(init, 2, 0, do_avb_init, "", ""),
+ U_BOOT_CMD_MKENT(read_rb, 2, 0, do_avb_read_rb, "", ""),
+ U_BOOT_CMD_MKENT(write_rb, 3, 0, do_avb_write_rb, "", ""),
+ U_BOOT_CMD_MKENT(is_unlocked, 1, 0, do_avb_is_unlocked, "", ""),
+ U_BOOT_CMD_MKENT(get_uuid, 2, 0, do_avb_get_uuid, "", ""),
+ U_BOOT_CMD_MKENT(read_part, 5, 0, do_avb_read_part, "", ""),
+ U_BOOT_CMD_MKENT(read_part_hex, 4, 0, do_avb_read_part_hex, "", ""),
+ U_BOOT_CMD_MKENT(write_part, 5, 0, do_avb_write_part, "", ""),
+ U_BOOT_CMD_MKENT(verify, 2, 0, do_avb_verify_part, "", ""),
+#ifdef CONFIG_OPTEE_TA_AVB
+ U_BOOT_CMD_MKENT(read_pvalue, 3, 0, do_avb_read_pvalue, "", ""),
+ U_BOOT_CMD_MKENT(write_pvalue, 3, 0, do_avb_write_pvalue, "", ""),
+#endif
+};
+
+static int do_avb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ cp = find_cmd_tbl(argv[1], cmd_avb, ARRAY_SIZE(cmd_avb));
+
+ argc--;
+ argv++;
+
+ if (!cp || argc > cp->maxargs)
+ return CMD_RET_USAGE;
+
+ if (flag == CMD_FLAG_REPEAT)
+ return CMD_RET_FAILURE;
+
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(
+ avb, 29, 0, do_avb,
+ "Provides commands for testing Android Verified Boot 2.0 functionality",
+ "init <dev> - initialize avb2 for <dev>\n"
+ "avb read_rb <num> - read rollback index at location <num>\n"
+ "avb write_rb <num> <rb> - write rollback index <rb> to <num>\n"
+ "avb is_unlocked - returns unlock status of the device\n"
+ "avb get_uuid <partname> - read and print uuid of partition <part>\n"
+ "avb read_part <partname> <offset> <num> <addr> - read <num> bytes from\n"
+ " partition <partname> to buffer <addr>\n"
+ "avb read_part_hex <partname> <offset> <num> - read <num> bytes from\n"
+ " partition <partname> and print to stdout\n"
+ "avb write_part <partname> <offset> <num> <addr> - write <num> bytes to\n"
+ " <partname> by <offset> using data from <addr>\n"
+#ifdef CONFIG_OPTEE_TA_AVB
+ "avb read_pvalue <name> <bytes> - read a persistent value <name>\n"
+ "avb write_pvalue <name> <value> - write a persistent value <name>\n"
+#endif
+ "avb verify [slot_suffix] - run verification process using hash data\n"
+ " from vbmeta structure\n"
+ " [slot_suffix] - _a, _b, etc (if vbmeta partition is slotted)\n"
+ );
diff --git a/cmd/axi.c b/cmd/axi.c
new file mode 100644
index 00000000000..3dbea0499de
--- /dev/null
+++ b/cmd/axi.c
@@ -0,0 +1,355 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2016
+ * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
+ *
+ * (C) Copyright 2017, 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <axi.h>
+#include <command.h>
+#include <console.h>
+#include <display_options.h>
+#include <dm.h>
+#include <log.h>
+
+/* Currently selected AXI bus device */
+static struct udevice *axi_cur_bus;
+/* Transmission size from last command */
+static uint dp_last_size;
+/* Address from last command */
+static uint dp_last_addr;
+/* Number of bytes to display from last command; default = 64 */
+static uint dp_last_length = 0x40;
+
+/**
+ * show_bus() - Show devices on a single AXI bus
+ * @bus: The AXI bus device to printt information for
+ */
+static void show_bus(struct udevice *bus)
+{
+ struct udevice *dev;
+
+ printf("Bus %d:\t%s", dev_seq(bus), bus->name);
+ if (device_active(bus))
+ printf(" (active)");
+ printf("\n");
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev))
+ printf(" %s\n", dev->name);
+}
+
+/**
+ * axi_set_cur_bus() - Set the currently active AXI bus
+ * @busnum: The number of the bus (i.e. its sequence number) that should be
+ * made active
+ *
+ * The operations supplied by this command operate only on the currently active
+ * bus.
+ *
+ * Return: 0 if OK, -ve on error
+ */
+static int axi_set_cur_bus(unsigned int busnum)
+{
+ struct udevice *bus;
+ struct udevice *dummy;
+ int ret;
+
+ /* Make sure that all sequence numbers are initialized */
+ for (uclass_first_device(UCLASS_AXI, &dummy);
+ dummy;
+ uclass_next_device(&dummy))
+ ;
+
+ ret = uclass_get_device_by_seq(UCLASS_AXI, busnum, &bus);
+ if (ret) {
+ debug("%s: No bus %d\n", __func__, busnum);
+ return ret;
+ }
+ axi_cur_bus = bus;
+
+ return 0;
+}
+
+/**
+ * axi_get_cur_bus() - Retrieve the currently active AXI bus device
+ * @busp: Pointer to a struct udevice that receives the currently active bus
+ * device
+ *
+ * Return: 0 if OK, -ve on error
+ */
+static int axi_get_cur_bus(struct udevice **busp)
+{
+ if (!axi_cur_bus) {
+ puts("No AXI bus selected\n");
+ return -ENODEV;
+ }
+ *busp = axi_cur_bus;
+
+ return 0;
+}
+
+/*
+ * Command handlers
+ */
+
+static int do_axi_show_bus(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dummy;
+
+ /* Make sure that all sequence numbers are initialized */
+ for (uclass_first_device(UCLASS_AXI, &dummy);
+ dummy;
+ uclass_next_device(&dummy))
+ ;
+
+ if (argc == 1) {
+ /* show all busses */
+ struct udevice *bus;
+
+ for (uclass_first_device(UCLASS_AXI, &bus);
+ bus;
+ uclass_next_device(&bus))
+ show_bus(bus);
+ } else {
+ int i;
+
+ /* show specific bus */
+ i = dectoul(argv[1], NULL);
+
+ struct udevice *bus;
+ int ret;
+
+ ret = uclass_get_device_by_seq(UCLASS_AXI, i, &bus);
+ if (ret) {
+ printf("Invalid bus %d: err=%d\n", i, ret);
+ return CMD_RET_FAILURE;
+ }
+ show_bus(bus);
+ }
+
+ return 0;
+}
+
+static int do_axi_bus_num(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret = 0;
+ int bus_no;
+
+ if (argc == 1) {
+ /* querying current setting */
+ struct udevice *bus;
+
+ if (!axi_get_cur_bus(&bus))
+ bus_no = dev_seq(bus);
+ else
+ bus_no = -1;
+
+ printf("Current bus is %d\n", bus_no);
+ } else {
+ bus_no = dectoul(argv[1], NULL);
+ printf("Setting bus to %d\n", bus_no);
+
+ ret = axi_set_cur_bus(bus_no);
+ if (ret)
+ printf("Failure changing bus number (%d)\n", ret);
+ }
+
+ return ret ? CMD_RET_FAILURE : 0;
+}
+
+static int do_axi_md(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ /* Print that many bytes per line */
+ const uint DISP_LINE_LEN = 16;
+ u8 linebuf[DISP_LINE_LEN];
+ unsigned int k;
+ ulong addr, length, size;
+ ulong nbytes;
+ enum axi_size_t axisize;
+ int unitsize;
+
+ /*
+ * We use the last specified parameters, unless new ones are
+ * entered.
+ */
+ size = dp_last_size;
+ addr = dp_last_addr;
+ length = dp_last_length;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ if (!axi_cur_bus) {
+ puts("No AXI bus selected\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ size = dectoul(argv[1], NULL);
+
+ /*
+ * Address is specified since argc >= 3
+ */
+ addr = hextoul(argv[2], NULL);
+
+ /*
+ * If there's another parameter, it is the length to display;
+ * length is the number of objects, not number of bytes
+ */
+ if (argc > 3)
+ length = hextoul(argv[3], NULL);
+ }
+
+ switch (size) {
+ case 8:
+ axisize = AXI_SIZE_8;
+ unitsize = 1;
+ break;
+ case 16:
+ axisize = AXI_SIZE_16;
+ unitsize = 2;
+ break;
+ case 32:
+ axisize = AXI_SIZE_32;
+ unitsize = 4;
+ break;
+ default:
+ printf("Unknown read size '%lu'\n", size);
+ return CMD_RET_USAGE;
+ };
+
+ nbytes = length * unitsize;
+ do {
+ ulong linebytes = (nbytes > DISP_LINE_LEN) ?
+ DISP_LINE_LEN : nbytes;
+
+ for (k = 0; k < linebytes / unitsize; ++k) {
+ int ret = axi_read(axi_cur_bus, addr + k * unitsize,
+ linebuf + k * unitsize, axisize);
+
+ if (!ret) /* Continue if axi_read was successful */
+ continue;
+
+ if (ret == -ENOSYS)
+ printf("axi_read failed; read size not supported?\n");
+ else
+ printf("axi_read failed: err = %d\n", ret);
+
+ return CMD_RET_FAILURE;
+ }
+ print_buffer(addr, (void *)linebuf, unitsize,
+ linebytes / unitsize,
+ DISP_LINE_LEN / unitsize);
+
+ nbytes -= max(linebytes, 1UL);
+ addr += linebytes;
+
+ if (ctrlc())
+ break;
+ } while (nbytes > 0);
+
+ dp_last_size = size;
+ dp_last_addr = addr;
+ dp_last_length = length;
+
+ return 0;
+}
+
+static int do_axi_mw(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 writeval;
+ ulong addr, count, size;
+ enum axi_size_t axisize;
+
+ if (argc <= 3 || argc >= 6)
+ return CMD_RET_USAGE;
+
+ size = dectoul(argv[1], NULL);
+
+ switch (size) {
+ case 8:
+ axisize = AXI_SIZE_8;
+ break;
+ case 16:
+ axisize = AXI_SIZE_16;
+ break;
+ case 32:
+ axisize = AXI_SIZE_32;
+ break;
+ default:
+ printf("Unknown write size '%lu'\n", size);
+ return CMD_RET_USAGE;
+ };
+
+ /* Address is specified since argc > 4 */
+ addr = hextoul(argv[2], NULL);
+
+ /* Get the value to write */
+ writeval = hextoul(argv[3], NULL);
+
+ /* Count ? */
+ if (argc == 5)
+ count = hextoul(argv[4], NULL);
+ else
+ count = 1;
+
+ while (count-- > 0) {
+ int ret = axi_write(axi_cur_bus, addr + count * sizeof(u32),
+ &writeval, axisize);
+
+ if (ret) {
+ printf("axi_write failed: err = %d\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ return 0;
+}
+
+static struct cmd_tbl cmd_axi_sub[] = {
+ U_BOOT_CMD_MKENT(bus, 1, 1, do_axi_show_bus, "", ""),
+ U_BOOT_CMD_MKENT(dev, 1, 1, do_axi_bus_num, "", ""),
+ U_BOOT_CMD_MKENT(md, 4, 1, do_axi_md, "", ""),
+ U_BOOT_CMD_MKENT(mw, 5, 1, do_axi_mw, "", ""),
+};
+
+static int do_ihs_axi(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *c;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* Strip off leading 'axi' command argument */
+ argc--;
+ argv++;
+
+ /* Hand off rest of command line to sub-commands */
+ c = find_cmd_tbl(argv[0], &cmd_axi_sub[0], ARRAY_SIZE(cmd_axi_sub));
+
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+ else
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_LONGHELP(axi,
+ "bus - show AXI bus info\n"
+ "axi dev [bus] - show or set current AXI bus to bus number [bus]\n"
+ "axi md size addr [# of objects] - read from AXI device at address [addr] and data width [size] (one of 8, 16, 32)\n"
+ "axi mw size addr value [count] - write data [value] to AXI device at address [addr] and data width [size] (one of 8, 16, 32)\n");
+
+U_BOOT_CMD(axi, 7, 1, do_ihs_axi,
+ "AXI sub-system",
+ axi_help_text
+);
diff --git a/cmd/bcb.c b/cmd/bcb.c
new file mode 100644
index 00000000000..d6d944bd6b3
--- /dev/null
+++ b/cmd/bcb.c
@@ -0,0 +1,504 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Eugeniu Rosca <rosca.eugeniu@gmail.com>
+ *
+ * Command to read/modify/write Android BCB fields
+ */
+
+#include <android_bootloader_message.h>
+#include <bcb.h>
+#include <command.h>
+#include <env.h>
+#include <android_ab.h>
+#include <display_options.h>
+#include <log.h>
+#include <part.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <vsprintf.h>
+#include <linux/err.h>
+
+static const char * const fields[] = {
+ "command",
+ "status",
+ "recovery",
+ "stage"
+};
+
+static struct bootloader_message bcb __aligned(ARCH_DMA_MINALIGN) = { { 0 } };
+static struct disk_partition partition_data;
+
+static struct blk_desc *block;
+static struct disk_partition *partition = &partition_data;
+
+static int bcb_not_loaded(void)
+{
+ printf("Error: Please, load BCB first!\n");
+ return -1;
+}
+
+static int bcb_field_get(const char *name, char **fieldp, int *sizep)
+{
+ if (!strcmp(name, "command")) {
+ *fieldp = bcb.command;
+ *sizep = sizeof(bcb.command);
+ } else if (!strcmp(name, "status")) {
+ *fieldp = bcb.status;
+ *sizep = sizeof(bcb.status);
+ } else if (!strcmp(name, "recovery")) {
+ *fieldp = bcb.recovery;
+ *sizep = sizeof(bcb.recovery);
+ } else if (!strcmp(name, "stage")) {
+ *fieldp = bcb.stage;
+ *sizep = sizeof(bcb.stage);
+ } else if (!strcmp(name, "reserved")) {
+ *fieldp = bcb.reserved;
+ *sizep = sizeof(bcb.reserved);
+ } else {
+ printf("Error: Unknown bcb field '%s'\n", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void __bcb_reset(void)
+{
+ block = NULL;
+ partition = &partition_data;
+ memset(&partition_data, 0, sizeof(struct disk_partition));
+ memset(&bcb, 0, sizeof(struct bootloader_message));
+}
+
+static int __bcb_initialize(const char *iface, int devnum, const char *partp)
+{
+ char *endp;
+ int part, ret;
+
+ block = blk_get_dev(iface, devnum);
+ if (!block) {
+ ret = -ENODEV;
+ goto err_read_fail;
+ }
+
+ /*
+ * always select the first hwpart in case another
+ * blk operation selected a different hwpart
+ */
+ ret = blk_dselect_hwpart(block, 0);
+ if (IS_ERR_VALUE(ret)) {
+ ret = -ENODEV;
+ goto err_read_fail;
+ }
+
+ part = simple_strtoul(partp, &endp, 0);
+ if (*endp == '\0') {
+ ret = part_get_info(block, part, partition);
+ if (ret)
+ goto err_read_fail;
+ } else {
+ part = part_get_info_by_name(block, partp, partition);
+ if (part < 0) {
+ ret = part;
+ goto err_read_fail;
+ }
+ }
+
+ return CMD_RET_SUCCESS;
+
+err_read_fail:
+ printf("Error: %s %d:%s read failed (%d)\n", iface, devnum,
+ partition->name, ret);
+ __bcb_reset();
+ return CMD_RET_FAILURE;
+}
+
+static int __bcb_load(void)
+{
+ u64 cnt;
+ int ret;
+
+ cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), partition->blksz);
+ if (cnt > partition->size)
+ goto err_too_small;
+
+ if (blk_dread(block, partition->start, cnt, &bcb) != cnt) {
+ ret = -EIO;
+ goto err_read_fail;
+ }
+
+ debug("%s: Loaded from %d %d:%s\n", __func__, block->uclass_id,
+ block->devnum, partition->name);
+
+ return CMD_RET_SUCCESS;
+err_read_fail:
+ printf("Error: %d %d:%s read failed (%d)\n", block->uclass_id,
+ block->devnum, partition->name, ret);
+ goto err;
+err_too_small:
+ printf("Error: %d %d:%s too small!", block->uclass_id,
+ block->devnum, partition->name);
+err:
+ __bcb_reset();
+ return CMD_RET_FAILURE;
+}
+
+static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ int ret;
+ int devnum;
+ char *endp;
+ char *iface = "mmc";
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ if (argc == 4) {
+ iface = argv[1];
+ argc--;
+ argv++;
+ }
+
+ devnum = simple_strtoul(argv[1], &endp, 0);
+ if (*endp != '\0') {
+ printf("Error: Device id '%s' not a number\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ ret = __bcb_initialize(iface, devnum, argv[2]);
+ if (ret != CMD_RET_SUCCESS)
+ return ret;
+
+ return __bcb_load();
+}
+
+static int __bcb_set(const char *fieldp, const char *valp)
+{
+ int size, len;
+ char *field, *str, *found, *tmp;
+
+ if (bcb_field_get(fieldp, &field, &size))
+ return CMD_RET_FAILURE;
+
+ len = strlen(valp);
+ if (len >= size) {
+ printf("Error: sizeof('%s') = %d >= %d = sizeof(bcb.%s)\n",
+ valp, len, size, fieldp);
+ return CMD_RET_FAILURE;
+ }
+ str = strdup(valp);
+ if (!str) {
+ printf("Error: Out of memory while strdup\n");
+ return CMD_RET_FAILURE;
+ }
+
+ tmp = str;
+ field[0] = '\0';
+ while ((found = strsep(&tmp, ":"))) {
+ if (field[0] != '\0')
+ strcat(field, "\n");
+ strcat(field, found);
+ }
+ free(str);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_bcb_set(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ if (!block)
+ return bcb_not_loaded();
+
+ return __bcb_set(argv[1], argv[2]);
+}
+
+static int do_bcb_clear(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int size;
+ char *field;
+
+ if (!block)
+ return bcb_not_loaded();
+
+ if (argc == 1) {
+ memset(&bcb, 0, sizeof(bcb));
+ return CMD_RET_SUCCESS;
+ }
+
+ if (bcb_field_get(argv[1], &field, &size))
+ return CMD_RET_FAILURE;
+
+ memset(field, 0, size);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_bcb_test(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int size;
+ char *field;
+ char *op;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ if (!block)
+ return bcb_not_loaded();
+
+ op = argv[2];
+
+ if (bcb_field_get(argv[1], &field, &size))
+ return CMD_RET_FAILURE;
+
+ if (*op == '=' && *(op + 1) == '\0') {
+ if (!strncmp(argv[3], field, size))
+ return CMD_RET_SUCCESS;
+ else
+ return CMD_RET_FAILURE;
+ } else if (*op == '~' && *(op + 1) == '\0') {
+ if (!strstr(field, argv[3]))
+ return CMD_RET_FAILURE;
+ else
+ return CMD_RET_SUCCESS;
+ } else {
+ printf("Error: Unknown operator '%s'\n", op);
+ }
+
+ return CMD_RET_FAILURE;
+}
+
+static int do_bcb_dump(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int size;
+ char *field;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (!block)
+ return bcb_not_loaded();
+
+ if (bcb_field_get(argv[1], &field, &size))
+ return CMD_RET_FAILURE;
+
+ print_buffer((ulong)field - (ulong)&bcb, (void *)field, 1, size, 16);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int __bcb_store(void)
+{
+ u64 cnt;
+ int ret;
+
+ cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), partition->blksz);
+
+ if (blk_dwrite(block, partition->start, cnt, &bcb) != cnt) {
+ ret = -EIO;
+ goto err;
+ }
+
+ return CMD_RET_SUCCESS;
+err:
+ printf("Error: %d %d:%s write failed (%d)\n", block->uclass_id,
+ block->devnum, partition->name, ret);
+
+ return CMD_RET_FAILURE;
+}
+
+static int do_bcb_store(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ if (!block)
+ return bcb_not_loaded();
+
+ return __bcb_store();
+}
+
+int bcb_find_partition_and_load(const char *iface, int devnum, char *partp)
+{
+ int ret;
+
+ __bcb_reset();
+
+ ret = __bcb_initialize(iface, devnum, partp);
+ if (ret != CMD_RET_SUCCESS)
+ return ret;
+
+ return __bcb_load();
+}
+
+int bcb_load(struct blk_desc *block_description, struct disk_partition *disk_partition)
+{
+ __bcb_reset();
+
+ block = block_description;
+ partition = disk_partition;
+
+ return __bcb_load();
+}
+
+int bcb_set(enum bcb_field field, const char *value)
+{
+ if (field > BCB_FIELD_STAGE)
+ return CMD_RET_FAILURE;
+ return __bcb_set(fields[field], value);
+}
+
+int bcb_get(enum bcb_field field, char *value_out, size_t value_size)
+{
+ int size;
+ char *field_value;
+
+ if (field > BCB_FIELD_STAGE)
+ return CMD_RET_FAILURE;
+ if (bcb_field_get(fields[field], &field_value, &size))
+ return CMD_RET_FAILURE;
+
+ strlcpy(value_out, field_value, value_size);
+
+ return CMD_RET_SUCCESS;
+}
+
+int bcb_store(void)
+{
+ return __bcb_store();
+}
+
+void bcb_reset(void)
+{
+ __bcb_reset();
+}
+
+__maybe_unused static int do_bcb_ab_select(struct cmd_tbl *cmdtp,
+ int flag, int argc,
+ char * const argv[])
+{
+ int ret;
+ struct blk_desc *dev_desc;
+ struct disk_partition part_info;
+ char slot[2];
+ bool dec_tries = true;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ for (int i = 4; i < argc; i++) {
+ if (!strcmp(argv[i], "--no-dec"))
+ dec_tries = false;
+ else
+ return CMD_RET_USAGE;
+ }
+
+ /* Lookup the "misc" partition from argv[2] and argv[3] */
+ if (part_get_info_by_dev_and_name_or_num(argv[2], argv[3],
+ &dev_desc, &part_info,
+ false) < 0) {
+ return CMD_RET_FAILURE;
+ }
+
+ ret = ab_select_slot(dev_desc, &part_info, dec_tries);
+ if (ret < 0) {
+ printf("Android boot failed, error %d.\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ /* Android standard slot names are 'a', 'b', ... */
+ slot[0] = BOOT_SLOT_NAME(ret);
+ slot[1] = '\0';
+ env_set(argv[1], slot);
+ printf("ANDROID: Booting slot: %s\n", slot);
+
+ return CMD_RET_SUCCESS;
+}
+
+__maybe_unused static int do_bcb_ab_dump(struct cmd_tbl *cmdtp,
+ int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+ struct blk_desc *dev_desc;
+ struct disk_partition part_info;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ if (part_get_info_by_dev_and_name_or_num(argv[1], argv[2],
+ &dev_desc, &part_info,
+ false) < 0) {
+ return CMD_RET_FAILURE;
+ }
+
+ ret = ab_dump_abc(dev_desc, &part_info);
+ if (ret < 0) {
+ printf("Cannot dump ABC data, error %d.\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_LONGHELP(bcb,
+ "load <interface> <dev> <part> - load BCB from <interface> <dev>:<part>\n"
+ "load <dev> <part> - load BCB from mmc <dev>:<part>\n"
+ "bcb set <field> <val> - set BCB <field> to <val>\n"
+ "bcb clear [<field>] - clear BCB <field> or all fields\n"
+ "bcb test <field> <op> <val> - test BCB <field> against <val>\n"
+ "bcb dump <field> - dump BCB <field>\n"
+ "bcb store - store BCB back to <interface>\n"
+ "\n"
+#if IS_ENABLED(CONFIG_ANDROID_AB)
+ "bcb ab_select -\n"
+ " Select the slot used to boot from and register the boot attempt.\n"
+ " <slot_var_name> <interface> <dev[:part|#part_name]> [--no-dec]\n"
+ " - Load the slot metadata from the partition 'part' on\n"
+ " device type 'interface' instance 'dev' and store the active\n"
+ " slot in the 'slot_var_name' variable. This also updates the\n"
+ " Android slot metadata with a boot attempt, which can cause\n"
+ " successive calls to this function to return a different result\n"
+ " if the returned slot runs out of boot attempts.\n"
+ " - If 'part_name' is passed, preceded with a # instead of :, the\n"
+ " partition name whose label is 'part_name' will be looked up in\n"
+ " the partition table. This is commonly the \"misc\" partition.\n"
+ " - If '--no-dec' is set, the number of tries remaining will not\n"
+ " decremented for the selected boot slot\n"
+ "\n"
+ "bcb ab_dump -\n"
+ " Dump boot_control information from specific partition.\n"
+ " <interface> <dev[:part|#part_name]>\n"
+ "\n"
+#endif
+ "Legend:\n"
+ "<interface> - storage device interface (virtio, mmc, etc)\n"
+ "<dev> - storage device index containing the BCB partition\n"
+ "<part> - partition index or name containing the BCB\n"
+ "<field> - one of {command,status,recovery,stage,reserved}\n"
+ "<op> - the binary operator used in 'bcb test':\n"
+ " '=' returns true if <val> matches the string stored in <field>\n"
+ " '~' returns true if <val> matches a subset of <field>'s string\n"
+ "<val> - string/text provided as input to bcb {set,test}\n"
+ " NOTE: any ':' character in <val> will be replaced by line feed\n"
+ " during 'bcb set' and used as separator by upper layers\n"
+);
+
+U_BOOT_CMD_WITH_SUBCMDS(bcb,
+ "Load/set/clear/test/dump/store Android BCB fields", bcb_help_text,
+ U_BOOT_SUBCMD_MKENT(load, 4, 1, do_bcb_load),
+ U_BOOT_SUBCMD_MKENT(set, 3, 1, do_bcb_set),
+ U_BOOT_SUBCMD_MKENT(clear, 2, 1, do_bcb_clear),
+ U_BOOT_SUBCMD_MKENT(test, 4, 1, do_bcb_test),
+ U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_bcb_dump),
+ U_BOOT_SUBCMD_MKENT(store, 1, 1, do_bcb_store),
+#if IS_ENABLED(CONFIG_ANDROID_AB)
+ U_BOOT_SUBCMD_MKENT(ab_select, 5, 1, do_bcb_ab_select),
+ U_BOOT_SUBCMD_MKENT(ab_dump, 3, 1, do_bcb_ab_dump),
+#endif
+);
diff --git a/cmd/bdinfo.c b/cmd/bdinfo.c
new file mode 100644
index 00000000000..20c8c97f0cd
--- /dev/null
+++ b/cmd/bdinfo.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Implements the 'bd' command to show board information
+ *
+ * (C) Copyright 2003
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <env.h>
+#include <getopt.h>
+#include <lmb.h>
+#include <mapmem.h>
+#include <net.h>
+#include <serial.h>
+#include <video.h>
+#include <vsprintf.h>
+#include <asm/cache.h>
+#include <asm/global_data.h>
+#include <display_options.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void bdinfo_print_size(const char *name, uint64_t size)
+{
+ printf("%-12s= ", name);
+ print_size(size, "\n");
+}
+
+void bdinfo_print_str(const char *name, const char *str)
+{
+ printf("%-12s= %s\n", name, str);
+}
+
+void bdinfo_print_num_l(const char *name, ulong value)
+{
+ printf("%-12s= 0x%0*lx\n", name, 2 * (int)sizeof(value), value);
+}
+
+void bdinfo_print_num_ll(const char *name, unsigned long long value)
+{
+ printf("%-12s= 0x%.*llx\n", name, 2 * (int)sizeof(ulong), value);
+}
+
+static void print_eth(void)
+{
+ const int idx = eth_get_dev_index();
+ char ipstr[] = "ipaddr\0\0";
+ uchar enetaddr[6];
+ char name[10];
+ int ret;
+
+ if (idx)
+ sprintf(name, "eth%iaddr", idx);
+ else
+ strcpy(name, "ethaddr");
+
+ ret = eth_env_get_enetaddr_by_index("eth", idx, enetaddr);
+
+ printf("current eth = %s\n", eth_get_name());
+ if (!ret)
+ printf("%-12s= (not set)\n", name);
+ else
+ printf("%-12s= %pM\n", name, enetaddr);
+
+ if (idx > 0)
+ sprintf(ipstr, "ipaddr%d", idx);
+
+ printf("IP addr = %s\n", env_get(ipstr));
+}
+
+void bdinfo_print_mhz(const char *name, unsigned long hz)
+{
+ char buf[32];
+
+ printf("%-12s= %6s MHz\n", name, strmhz(buf, hz));
+}
+
+static void print_bi_dram(const struct bd_info *bd)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) {
+ if (bd->bi_dram[i].size) {
+ bdinfo_print_num_l("DRAM bank", i);
+ bdinfo_print_num_ll("-> start", bd->bi_dram[i].start);
+ bdinfo_print_num_ll("-> size", bd->bi_dram[i].size);
+ }
+ }
+}
+
+__weak void arch_print_bdinfo(void)
+{
+}
+
+static void show_video_info(void)
+{
+ const struct udevice *dev;
+ struct uclass *uc;
+
+ uclass_id_foreach_dev(UCLASS_VIDEO, dev, uc) {
+ printf("%-12s= %s %sactive\n", "Video", dev->name,
+ device_active(dev) ? "" : "in");
+ if (device_active(dev)) {
+ struct video_priv *upriv = dev_get_uclass_priv(dev);
+ struct video_uc_plat *plat = dev_get_uclass_plat(dev);
+
+ bdinfo_print_num_ll("FB base", (ulong)upriv->fb);
+ if (upriv->copy_fb) {
+ bdinfo_print_num_ll("FB copy",
+ (ulong)upriv->copy_fb);
+ bdinfo_print_num_l(" copy size",
+ plat->copy_size);
+ }
+ printf("%-12s= %dx%dx%d\n", "FB size", upriv->xsize,
+ upriv->ysize, 1 << upriv->bpix);
+ }
+ }
+}
+
+static void print_serial(struct udevice *dev)
+{
+ struct serial_device_info info;
+ int ret;
+
+ if (!dev || !IS_ENABLED(CONFIG_DM_SERIAL))
+ return;
+
+ ret = serial_getinfo(dev, &info);
+ if (ret)
+ return;
+
+ bdinfo_print_num_l("serial addr", info.addr);
+ bdinfo_print_num_l(" width", info.reg_width);
+ bdinfo_print_num_l(" shift", info.reg_shift);
+ bdinfo_print_num_l(" offset", info.reg_offset);
+ bdinfo_print_num_l(" clock", info.clock);
+}
+
+static int bdinfo_print_all(struct bd_info *bd)
+{
+#ifdef DEBUG
+ bdinfo_print_num_l("bd address", (ulong)bd);
+#endif
+ bdinfo_print_num_l("boot_params", (ulong)bd->bi_boot_params);
+ print_bi_dram(bd);
+ bdinfo_print_num_l("flashstart", (ulong)bd->bi_flashstart);
+ bdinfo_print_num_l("flashsize", (ulong)bd->bi_flashsize);
+ bdinfo_print_num_l("flashoffset", (ulong)bd->bi_flashoffset);
+ printf("baudrate = %u bps\n", gd->baudrate);
+ bdinfo_print_num_l("relocaddr", gd->relocaddr);
+ bdinfo_print_num_l("reloc off", gd->reloc_off);
+ printf("%-12s= %u-bit\n", "Build", (uint)sizeof(void *) * 8);
+ if (IS_ENABLED(CONFIG_CMD_NET) || IS_ENABLED(CONFIG_CMD_NET_LWIP))
+ print_eth();
+ bdinfo_print_num_l("fdt_blob", (ulong)map_to_sysmem(gd->fdt_blob));
+ if (IS_ENABLED(CONFIG_VIDEO))
+ show_video_info();
+#if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
+ bdinfo_print_num_l("multi_dtb_fit", (ulong)gd->multi_dtb_fit);
+#endif
+ if (IS_ENABLED(CONFIG_LMB) && gd->fdt_blob) {
+ lmb_dump_all_force();
+ if (IS_ENABLED(CONFIG_OF_REAL))
+ printf("devicetree = %s\n", fdtdec_get_srcname());
+ }
+ print_serial(gd->cur_serial_dev);
+
+ if (IS_ENABLED(CONFIG_CMD_BDINFO_EXTRA)) {
+ bdinfo_print_num_ll("stack ptr", (ulong)&bd);
+ bdinfo_print_num_ll("ram_top ptr", (ulong)gd->ram_top);
+ bdinfo_print_num_l("malloc base", gd_malloc_start());
+ }
+
+ arch_print_bdinfo();
+
+ return 0;
+}
+
+int do_bdinfo(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct bd_info *bd = gd->bd;
+ struct getopt_state gs;
+ int opt;
+
+ if (!CONFIG_IS_ENABLED(GETOPT) || argc == 1)
+ return bdinfo_print_all(bd);
+
+ getopt_init_state(&gs);
+ while ((opt = getopt(&gs, argc, argv, "aem")) > 0) {
+ switch (opt) {
+ case 'a':
+ return bdinfo_print_all(bd);
+ case 'e':
+ if (!IS_ENABLED(CONFIG_CMD_NET) &&
+ !IS_ENABLED(CONFIG_CMD_NET_LWIP))
+ return CMD_RET_USAGE;
+ print_eth();
+ return CMD_RET_SUCCESS;
+ case 'm':
+ print_bi_dram(bd);
+ return CMD_RET_SUCCESS;
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+ bdinfo, 2, 1, do_bdinfo,
+ "print Board Info structure",
+ ""
+);
diff --git a/cmd/bind.c b/cmd/bind.c
new file mode 100644
index 00000000000..c0d31f5eb16
--- /dev/null
+++ b/cmd/bind.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2018 JJ Hiblot <jjhiblot@ti.com>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/root.h>
+#include <dm/uclass-internal.h>
+
+static int bind_by_class_seq(const char *uclass, int seq,
+ const char *drv_name)
+{
+ static enum uclass_id uclass_id;
+ struct udevice *dev;
+ struct udevice *parent;
+ int ret;
+ struct driver *drv;
+
+ drv = lists_driver_lookup_name(drv_name);
+ if (!drv) {
+ printf("Cannot find driver '%s'\n", drv_name);
+ return -ENOENT;
+ }
+
+ uclass_id = uclass_get_by_name(uclass);
+ if (uclass_id == UCLASS_INVALID) {
+ printf("%s is not a valid uclass\n", uclass);
+ return -EINVAL;
+ }
+
+ ret = uclass_find_device_by_seq(uclass_id, seq, &parent);
+ if (!parent || ret) {
+ printf("Cannot find device %d of class %s\n", seq, uclass);
+ return ret;
+ }
+
+ ret = device_bind_with_driver_data(parent, drv, drv->name, 0,
+ ofnode_null(), &dev);
+ if (!dev || ret) {
+ printf("Unable to bind. err:%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int find_dev(const char *uclass, int seq, struct udevice **devp)
+{
+ static enum uclass_id uclass_id;
+ int rc;
+
+ uclass_id = uclass_get_by_name(uclass);
+ if (uclass_id == UCLASS_INVALID) {
+ printf("%s is not a valid uclass\n", uclass);
+ return -EINVAL;
+ }
+
+ rc = uclass_find_device_by_seq(uclass_id, seq, devp);
+ if (!*devp || rc) {
+ printf("Cannot find device %d of class %s\n", seq, uclass);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int unbind_by_class_seq(const char *uclass, int seq)
+{
+ int ret;
+ struct udevice *dev;
+
+ ret = find_dev(uclass, seq, &dev);
+ if (ret)
+ return ret;
+
+ ret = device_remove(dev, DM_REMOVE_NORMAL);
+ if (ret) {
+ printf("Unable to remove. err:%d\n", ret);
+ return ret;
+ }
+
+ ret = device_unbind(dev);
+ if (ret) {
+ printf("Unable to unbind. err:%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int unbind_child_by_class_seq(const char *uclass, int seq,
+ const char *drv_name)
+{
+ struct udevice *parent;
+ int ret;
+ struct driver *drv;
+
+ drv = lists_driver_lookup_name(drv_name);
+ if (!drv) {
+ printf("Cannot find driver '%s'\n", drv_name);
+ return -ENOENT;
+ }
+
+ ret = find_dev(uclass, seq, &parent);
+ if (ret)
+ return ret;
+
+ ret = device_chld_remove(parent, drv, DM_REMOVE_NORMAL);
+ if (ret)
+ printf("Unable to remove all. err:%d\n", ret);
+
+ ret = device_chld_unbind(parent, drv);
+ if (ret)
+ printf("Unable to unbind all. err:%d\n", ret);
+
+ return ret;
+}
+
+static int bind_by_node_path(const char *path, const char *drv_name)
+{
+ struct udevice *dev;
+ struct udevice *parent = NULL;
+ int ret;
+ ofnode ofnode;
+ struct driver *drv;
+
+ drv = lists_driver_lookup_name(drv_name);
+ if (!drv) {
+ printf("%s is not a valid driver name\n", drv_name);
+ return -ENOENT;
+ }
+
+ ofnode = ofnode_path(path);
+ if (!ofnode_valid(ofnode)) {
+ printf("%s is not a valid node path\n", path);
+ return -EINVAL;
+ }
+
+ while (ofnode_valid(ofnode)) {
+ if (!device_find_global_by_ofnode(ofnode, &parent))
+ break;
+ ofnode = ofnode_get_parent(ofnode);
+ }
+
+ if (!parent) {
+ printf("Cannot find a parent device for node path %s\n", path);
+ return -ENODEV;
+ }
+
+ ofnode = ofnode_path(path);
+ ret = lists_bind_fdt(parent, ofnode, &dev, drv, false);
+
+ if (!dev || ret) {
+ printf("Unable to bind. err:%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int unbind_by_node_path(const char *path)
+{
+ struct udevice *dev;
+ int ret;
+ ofnode ofnode;
+
+ ofnode = ofnode_path(path);
+ if (!ofnode_valid(ofnode)) {
+ printf("%s is not a valid node path\n", path);
+ return -EINVAL;
+ }
+
+ ret = device_find_global_by_ofnode(ofnode, &dev);
+
+ if (!dev || ret) {
+ printf("Cannot find a device with path %s\n", path);
+ return -ENODEV;
+ }
+
+ ret = device_remove(dev, DM_REMOVE_NORMAL);
+ if (ret) {
+ printf("Unable to remove. err:%d\n", ret);
+ return ret;
+ }
+
+ ret = device_unbind(dev);
+ if (ret) {
+ printf("Unable to unbind. err:%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int do_bind_unbind(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret = 0;
+ bool bind;
+ bool by_node;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ bind = (argv[0][0] == 'b');
+ by_node = (argv[1][0] == '/');
+
+ if (by_node && bind) {
+ if (argc != 3)
+ return CMD_RET_USAGE;
+ ret = bind_by_node_path(argv[1], argv[2]);
+ } else if (by_node && !bind) {
+ if (argc != 2)
+ return CMD_RET_USAGE;
+ ret = unbind_by_node_path(argv[1]);
+ } else if (!by_node && bind) {
+ int seq = (argc > 2) ? dectoul(argv[2], NULL) : 0;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+ ret = bind_by_class_seq(argv[1], seq, argv[3]);
+ } else if (!by_node && !bind) {
+ int seq = (argc > 2) ? dectoul(argv[2], NULL) : 0;
+
+ if (argc == 3)
+ ret = unbind_by_class_seq(argv[1], seq);
+ else if (argc == 4)
+ ret = unbind_child_by_class_seq(argv[1], seq,
+ argv[3]);
+ else
+ return CMD_RET_USAGE;
+ }
+
+ if (ret)
+ return CMD_RET_FAILURE;
+ else
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+ bind, 4, 0, do_bind_unbind,
+ "Bind a device to a driver",
+ "<node path> <driver>\n"
+ "bind <class> <seq> <driver>\n"
+ "Use 'dm tree' to list all devices registered in the driver model,\n"
+ "their path, class, sequence and current driver.\n"
+);
+
+U_BOOT_CMD(
+ unbind, 4, 0, do_bind_unbind,
+ "Unbind a device from a driver",
+ "<node path>\n"
+ "unbind <class> <seq>\n"
+ "unbind <class> <seq> <driver>\n"
+ "Use 'dm tree' to list all devices registered in the driver model,\n"
+ "their path, class, sequence and current driver.\n"
+);
diff --git a/cmd/binop.c b/cmd/binop.c
new file mode 100644
index 00000000000..10d91b5dbf2
--- /dev/null
+++ b/cmd/binop.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <command.h>
+#include <env.h>
+#include <hexdump.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <vsprintf.h>
+#include <linux/ctype.h>
+
+enum {
+ OP_ID_XOR,
+ OP_ID_AND,
+ OP_ID_OR,
+};
+
+void write_to_env_var(char *varname, u8 *result, ulong len)
+{
+ char *str_output;
+ char *str_ptr;
+ int i;
+
+ str_output = malloc(len * 2 + 1);
+ str_ptr = str_output;
+
+ for (i = 0; i < len; i++) {
+ sprintf(str_ptr, "%02x", result[i]);
+ str_ptr += 2;
+ }
+ *str_ptr = '\0';
+ env_set(varname, str_output);
+
+ free(str_output);
+}
+
+void read_from_env_var(char *varname, u8 *result)
+{
+ char *str_value;
+
+ str_value = env_get(varname);
+ if (str_value)
+ hex2bin(result, str_value, strlen(str_value) / 2);
+ else
+ hex2bin(result, varname, strlen(varname) / 2);
+}
+
+void read_from_mem(ulong addr, u8 *result, ulong len)
+{
+ u8 *src;
+
+ src = map_sysmem(addr, len);
+ memcpy(result, src, len);
+ unmap_sysmem(src);
+}
+
+void write_to_mem(char *varname, u8 *result, ulong len)
+{
+ ulong addr;
+ u8 *buf;
+
+ addr = hextoul(varname, NULL);
+ buf = map_sysmem(addr, len);
+ memcpy(buf, result, len);
+ unmap_sysmem(buf);
+}
+
+static int do_binop(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong len;
+ u8 *result, *src1, *src2;
+ char *oparg, *lenarg, *src1arg, *src2arg, *destarg;
+ int i, op;
+
+ if (argc < 5)
+ return CMD_RET_USAGE;
+
+ oparg = argv[1];
+ lenarg = argv[2];
+ src1arg = argv[3];
+ src2arg = argv[4];
+
+ if (!strcmp(oparg, "xor"))
+ op = OP_ID_XOR;
+ else if (!strcmp(oparg, "or"))
+ op = OP_ID_OR;
+ else if (!strcmp(oparg, "and"))
+ op = OP_ID_AND;
+ else
+ return CMD_RET_USAGE;
+
+ len = dectoul(lenarg, NULL);
+
+ src1 = malloc(len);
+ src2 = malloc(len);
+
+ if (*src1arg == '*')
+ read_from_mem(hextoul(src1arg + 1, NULL), src1, len);
+ else
+ read_from_env_var(src1arg, src1);
+
+ if (*src2arg == '*')
+ read_from_mem(hextoul(src2arg + 1, NULL), src2, len);
+ else
+ read_from_env_var(src2arg, src2);
+
+ result = malloc(len);
+
+ switch (op) {
+ case OP_ID_XOR:
+ for (i = 0; i < len; i++)
+ result[i] = src1[i] ^ src2[i];
+ break;
+ case OP_ID_OR:
+ for (i = 0; i < len; i++)
+ result[i] = src1[i] | src2[i];
+ break;
+ case OP_ID_AND:
+ for (i = 0; i < len; i++)
+ result[i] = src1[i] & src2[i];
+ break;
+ }
+
+ if (argc == 5) {
+ for (i = 0; i < len; i++) {
+ printf("%02x ", result[i]);
+ if (i % 16 == 15)
+ puts("\n");
+ }
+ puts("\n");
+
+ goto exit;
+ }
+
+ destarg = argv[5];
+
+ if (*destarg == '*')
+ write_to_mem(destarg + 1, result, len); /* Skip asterisk */
+ else
+ write_to_env_var(destarg, result, len);
+exit:
+ free(result);
+ free(src2);
+ free(src1);
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ binop, 6, 1, do_binop,
+ "compute binary operation",
+ "op count [*]src1 [*]src2 [[*]dest]\n"
+ " - compute binary operation of data at/in src1 and\n src2 (either *memaddr, env var name or hex string)\n and store result in/at dest, where op is one of\n xor, or, and."
+);
diff --git a/cmd/blk_common.c b/cmd/blk_common.c
new file mode 100644
index 00000000000..56529702a47
--- /dev/null
+++ b/cmd/blk_common.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Handling of common block commands
+ *
+ * Copyright (c) 2017 Google, Inc
+ *
+ * (C) Copyright 2000-2011
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <blk.h>
+#include <command.h>
+#include <mapmem.h>
+#include <vsprintf.h>
+
+int blk_common_cmd(int argc, char *const argv[], enum uclass_id uclass_id,
+ int *cur_devnump)
+{
+ const char *if_name = blk_get_uclass_name(uclass_id);
+
+ switch (argc) {
+ case 0:
+ case 1:
+ return CMD_RET_USAGE;
+ case 2:
+ if (strncmp(argv[1], "inf", 3) == 0) {
+ blk_list_devices(uclass_id);
+ return CMD_RET_SUCCESS;
+ } else if (strncmp(argv[1], "dev", 3) == 0) {
+ if (blk_print_device_num(uclass_id, *cur_devnump)) {
+ printf("\nno %s devices available\n", if_name);
+ return CMD_RET_FAILURE;
+ }
+ return CMD_RET_SUCCESS;
+ } else if (strncmp(argv[1], "part", 4) == 0) {
+ if (blk_list_part(uclass_id))
+ printf("\nno %s partition table available\n",
+ if_name);
+ return CMD_RET_SUCCESS;
+ }
+ return CMD_RET_USAGE;
+ case 3:
+ if (strncmp(argv[1], "dev", 3) == 0) {
+ int dev = (int)dectoul(argv[2], NULL);
+
+ if (!blk_show_device(uclass_id, dev)) {
+ *cur_devnump = dev;
+ printf("... is now current device\n");
+ } else {
+ return CMD_RET_FAILURE;
+ }
+ return CMD_RET_SUCCESS;
+ } else if (strncmp(argv[1], "part", 4) == 0) {
+ int dev = (int)dectoul(argv[2], NULL);
+
+ if (blk_print_part_devnum(uclass_id, dev)) {
+ printf("\n%s device %d not available\n",
+ if_name, dev);
+ return CMD_RET_FAILURE;
+ }
+ return CMD_RET_SUCCESS;
+ }
+ return CMD_RET_USAGE;
+
+ default: /* at least 4 args */
+ if (strcmp(argv[1], "read") == 0) {
+ phys_addr_t paddr = hextoul(argv[2], NULL);
+ lbaint_t blk = hextoul(argv[3], NULL);
+ ulong cnt = hextoul(argv[4], NULL);
+ struct blk_desc *desc;
+ void *vaddr;
+ ulong n;
+ int ret;
+
+ printf("\n%s read: device %d block # "LBAFU", count %lu ... ",
+ if_name, *cur_devnump, blk, cnt);
+
+ ret = blk_get_desc(uclass_id, *cur_devnump, &desc);
+ if (ret)
+ return CMD_RET_FAILURE;
+ vaddr = map_sysmem(paddr, desc->blksz * cnt);
+ n = blk_dread(desc, blk, cnt, vaddr);
+ unmap_sysmem(vaddr);
+
+ printf("%ld blocks read: %s\n", n,
+ n == cnt ? "OK" : "ERROR");
+ return n == cnt ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+ } else if (strcmp(argv[1], "write") == 0) {
+ phys_addr_t paddr = hextoul(argv[2], NULL);
+ lbaint_t blk = hextoul(argv[3], NULL);
+ ulong cnt = hextoul(argv[4], NULL);
+ struct blk_desc *desc;
+ void *vaddr;
+ ulong n;
+ int ret;
+
+ printf("\n%s write: device %d block # "LBAFU", count %lu ... ",
+ if_name, *cur_devnump, blk, cnt);
+
+ ret = blk_get_desc(uclass_id, *cur_devnump, &desc);
+ if (ret)
+ return CMD_RET_FAILURE;
+ vaddr = map_sysmem(paddr, desc->blksz * cnt);
+ n = blk_dwrite(desc, blk, cnt, vaddr);
+ unmap_sysmem(vaddr);
+
+ printf("%ld blocks written: %s\n", n,
+ n == cnt ? "OK" : "ERROR");
+ return n == cnt ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+ } else if (strcmp(argv[1], "erase") == 0) {
+ lbaint_t blk = hextoul(argv[2], NULL);
+ ulong cnt = hextoul(argv[3], NULL);
+ struct blk_desc *desc;
+ ulong n;
+
+ printf("\n%s erase: device %d block # "LBAFU", count %lu ... ",
+ if_name, *cur_devnump, blk, cnt);
+
+ if (blk_get_desc(uclass_id, *cur_devnump, &desc))
+ return CMD_RET_FAILURE;
+
+ n = blk_derase(desc, blk, cnt);
+
+ printf("%ld blocks erased: %s\n", n,
+ n == cnt ? "OK" : "ERROR");
+ return n == cnt ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+ } else {
+ return CMD_RET_USAGE;
+ }
+ }
+}
diff --git a/cmd/blkcache.c b/cmd/blkcache.c
new file mode 100644
index 00000000000..dbd03df14dc
--- /dev/null
+++ b/cmd/blkcache.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Nelson Integration, LLC 2016
+ * Author: Eric Nelson<eric@nelint.com>
+ *
+ */
+#include <command.h>
+#include <config.h>
+#include <malloc.h>
+#include <part.h>
+#include <vsprintf.h>
+
+static int blkc_show(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct block_cache_stats stats;
+ blkcache_stats(&stats);
+
+ printf("hits: %u\n"
+ "misses: %u\n"
+ "entries: %u\n"
+ "max blocks/entry: %u\n"
+ "max cache entries: %u\n",
+ stats.hits, stats.misses, stats.entries,
+ stats.max_blocks_per_entry, stats.max_entries);
+ return 0;
+}
+
+static int blkc_configure(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ unsigned blocks_per_entry, max_entries;
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ blocks_per_entry = simple_strtoul(argv[1], 0, 0);
+ max_entries = simple_strtoul(argv[2], 0, 0);
+ blkcache_configure(blocks_per_entry, max_entries);
+ printf("changed to max of %u entries of %u blocks each\n",
+ max_entries, blocks_per_entry);
+ return 0;
+}
+
+static struct cmd_tbl cmd_blkc_sub[] = {
+ U_BOOT_CMD_MKENT(show, 0, 0, blkc_show, "", ""),
+ U_BOOT_CMD_MKENT(configure, 3, 0, blkc_configure, "", ""),
+};
+
+static int do_blkcache(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct cmd_tbl *c;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* Strip off leading argument */
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], &cmd_blkc_sub[0], ARRAY_SIZE(cmd_blkc_sub));
+
+ if (!c)
+ return CMD_RET_USAGE;
+
+ return c->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(
+ blkcache, 4, 0, do_blkcache,
+ "block cache diagnostics and control",
+ "show - show and reset statistics\n"
+ "blkcache configure <blocks> <entries> "
+ "- set max blocks per entry and max cache entries\n"
+);
diff --git a/cmd/blkmap.c b/cmd/blkmap.c
new file mode 100644
index 00000000000..65edec899e2
--- /dev/null
+++ b/cmd/blkmap.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2023 Addiva Elektronik
+ * Author: Tobias Waldekranz <tobias@waldekranz.com>
+ */
+
+#include <blk.h>
+#include <blkmap.h>
+#include <command.h>
+#include <env.h>
+#include <malloc.h>
+#include <dm/device.h>
+
+static int blkmap_curr_dev;
+
+struct map_ctx {
+ struct udevice *dev;
+ lbaint_t blknr, blkcnt;
+};
+
+typedef int (*map_parser_fn)(struct map_ctx *ctx, int argc, char *const argv[]);
+
+struct map_handler {
+ const char *name;
+ map_parser_fn fn;
+};
+
+static int do_blkmap_map_linear(struct map_ctx *ctx, int argc,
+ char *const argv[])
+{
+ struct blk_desc *lbd;
+ int err, ldevnum;
+ lbaint_t lblknr;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ ldevnum = dectoul(argv[2], NULL);
+ lblknr = dectoul(argv[3], NULL);
+
+ lbd = blk_get_devnum_by_uclass_idname(argv[1], ldevnum);
+ if (!lbd) {
+ printf("Found no device matching \"%s %d\"\n",
+ argv[1], ldevnum);
+ return CMD_RET_FAILURE;
+ }
+
+ err = blkmap_map_linear(ctx->dev, ctx->blknr, ctx->blkcnt,
+ lbd->bdev, lblknr);
+ if (err) {
+ printf("Unable to map \"%s %d\" at block 0x" LBAF ": %d\n",
+ argv[1], ldevnum, ctx->blknr, err);
+
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Block 0x" LBAF "+0x" LBAF " mapped to block 0x" LBAF " of \"%s %d\"\n",
+ ctx->blknr, ctx->blkcnt, lblknr, argv[1], ldevnum);
+ return CMD_RET_SUCCESS;
+}
+
+static int do_blkmap_map_mem(struct map_ctx *ctx, int argc, char *const argv[])
+{
+ phys_addr_t addr;
+ int err;
+ bool preserve = false;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ addr = hextoul(argv[1], NULL);
+
+ if (argc == 3 && !strcmp(argv[2], "preserve"))
+ preserve = true;
+
+ err = blkmap_map_pmem(ctx->dev, ctx->blknr, ctx->blkcnt, addr,
+ preserve);
+ if (err) {
+ printf("Unable to map %#llx at block 0x" LBAF ": %d\n",
+ (unsigned long long)addr, ctx->blknr, err);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Block 0x" LBAF "+0x" LBAF " mapped to %#llx\n",
+ ctx->blknr, ctx->blkcnt, (unsigned long long)addr);
+ return CMD_RET_SUCCESS;
+}
+
+static struct map_handler map_handlers[] = {
+ { .name = "linear", .fn = do_blkmap_map_linear },
+ { .name = "mem", .fn = do_blkmap_map_mem },
+
+ { .name = NULL }
+};
+
+static int do_blkmap_map(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct map_handler *handler;
+ struct map_ctx ctx;
+
+ if (argc < 5)
+ return CMD_RET_USAGE;
+
+ ctx.dev = blkmap_from_label(argv[1]);
+ if (!ctx.dev) {
+ printf("\"%s\" is not the name of any known blkmap\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ ctx.blknr = hextoul(argv[2], NULL);
+ ctx.blkcnt = hextoul(argv[3], NULL);
+ argc -= 4;
+ argv += 4;
+
+ for (handler = map_handlers; handler->name; handler++) {
+ if (!strcmp(handler->name, argv[0]))
+ return handler->fn(&ctx, argc, argv);
+ }
+
+ printf("Unknown map type \"%s\"\n", argv[0]);
+ return CMD_RET_USAGE;
+}
+
+static int do_blkmap_create(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ const char *label;
+ int err;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ label = argv[1];
+
+ err = blkmap_create(label, NULL);
+ if (err) {
+ printf("Unable to create \"%s\": %d\n", label, err);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Created \"%s\"\n", label);
+ return CMD_RET_SUCCESS;
+}
+
+static int do_blkmap_destroy(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct udevice *dev;
+ const char *label;
+ int err;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ label = argv[1];
+
+ dev = blkmap_from_label(label);
+ if (!dev) {
+ printf("\"%s\" is not the name of any known blkmap\n", label);
+ return CMD_RET_FAILURE;
+ }
+
+ err = blkmap_destroy(dev);
+ if (err) {
+ printf("Unable to destroy \"%s\": %d\n", label, err);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Destroyed \"%s\"\n", label);
+ return CMD_RET_SUCCESS;
+}
+
+static int do_blkmap_get(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct udevice *dev;
+ const char *label;
+ int err;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ label = argv[1];
+
+ dev = blkmap_from_label(label);
+ if (!dev) {
+ printf("\"%s\" is not the name of any known blkmap\n", label);
+ return CMD_RET_FAILURE;
+ }
+
+ if (!strcmp(argv[2], "dev")) {
+ if (argc == 3) {
+ printf("%d\n", dev_seq(dev));
+ } else {
+ err = env_set_hex(argv[3], dev_seq(dev));
+ if (err)
+ return CMD_RET_FAILURE;
+ }
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_blkmap_common(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ /* The subcommand parsing pops the original argv[0] ("blkmap")
+ * which blk_common_cmd expects. Push it back again.
+ */
+ argc++;
+ argv--;
+
+ return blk_common_cmd(argc, argv, UCLASS_BLKMAP, &blkmap_curr_dev);
+}
+
+U_BOOT_CMD_WITH_SUBCMDS(
+ blkmap, "Composeable virtual block devices",
+ "info - list configured devices\n"
+ "blkmap part - list available partitions on current blkmap device\n"
+ "blkmap dev [<dev>] - show or set current blkmap device\n"
+ "blkmap read <addr> <blk#> <cnt>\n"
+ "blkmap write <addr> <blk#> <cnt>\n"
+ "blkmap get <label> dev [<var>] - store device number in variable\n"
+ "blkmap create <label> - create device\n"
+ "blkmap destroy <label> - destroy device\n"
+ "blkmap map <label> <blk#> <cnt> linear <interface> <dev> <blk#> - device mapping\n"
+ "blkmap map <label> <blk#> <cnt> mem <addr> [preserve] - memory mapping\n",
+ U_BOOT_SUBCMD_MKENT(info, 2, 1, do_blkmap_common),
+ U_BOOT_SUBCMD_MKENT(part, 2, 1, do_blkmap_common),
+ U_BOOT_SUBCMD_MKENT(dev, 4, 1, do_blkmap_common),
+ U_BOOT_SUBCMD_MKENT(read, 5, 1, do_blkmap_common),
+ U_BOOT_SUBCMD_MKENT(write, 5, 1, do_blkmap_common),
+ U_BOOT_SUBCMD_MKENT(get, 5, 1, do_blkmap_get),
+ U_BOOT_SUBCMD_MKENT(create, 2, 1, do_blkmap_create),
+ U_BOOT_SUBCMD_MKENT(destroy, 2, 1, do_blkmap_destroy),
+ U_BOOT_SUBCMD_MKENT(map, 32, 1, do_blkmap_map));
diff --git a/cmd/blob.c b/cmd/blob.c
new file mode 100644
index 00000000000..b1c72e3f440
--- /dev/null
+++ b/cmd/blob.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *
+ * Command for encapsulating/decapsulating blob of memory.
+ */
+
+#include <command.h>
+#include <malloc.h>
+#include <vsprintf.h>
+#include <asm/byteorder.h>
+#include <linux/compiler.h>
+#if defined(CONFIG_ARCH_MX6) || defined(CONFIG_ARCH_MX7) || \
+ defined(CONFIG_ARCH_MX7ULP) || defined(CONFIG_ARCH_IMX8M)
+#include <fsl_sec.h>
+#include <asm/arch/clock.h>
+#endif
+
+/**
+ * blob_decap() - Decapsulate the data as a blob
+ * @key_mod: - Pointer to key modifier/key
+ * @src: - Address of data to be decapsulated
+ * @dst: - Address of data to be decapsulated
+ * @len: - Size of data to be decapsulated
+ *
+ * Returns zero on success,and negative on error.
+ */
+__weak int blob_decap(u8 *key_mod, u8 *src, u8 *dst, u32 len)
+{
+ return 0;
+}
+
+/**
+ * blob_encap() - Encapsulate the data as a blob
+ * @key_mod: - Pointer to key modifier/key
+ * @src: - Address of data to be encapsulated
+ * @dst: - Address of data to be encapsulated
+ * @len: - Size of data to be encapsulated
+ *
+ * Returns zero on success,and negative on error.
+ */
+__weak int blob_encap(u8 *key_mod, u8 *src, u8 *dst, u32 len)
+{
+ return 0;
+}
+
+/**
+ * do_blob() - Handle the "blob" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ */
+static int do_blob(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong key_addr, src_addr, dst_addr, len;
+ uint8_t *km_ptr, *src_ptr, *dst_ptr;
+ int enc, ret = 0;
+
+ if (argc != 6)
+ return CMD_RET_USAGE;
+
+ if (!strncmp(argv[1], "enc", 3))
+ enc = 1;
+ else if (!strncmp(argv[1], "dec", 3))
+ enc = 0;
+ else
+ return CMD_RET_USAGE;
+
+ src_addr = hextoul(argv[2], NULL);
+ dst_addr = hextoul(argv[3], NULL);
+ len = hextoul(argv[4], NULL);
+ key_addr = hextoul(argv[5], NULL);
+
+ km_ptr = (uint8_t *)(uintptr_t)key_addr;
+ src_ptr = (uint8_t *)(uintptr_t)src_addr;
+ dst_ptr = (uint8_t *)(uintptr_t)dst_addr;
+
+#if defined(CONFIG_ARCH_MX6) || defined(CONFIG_ARCH_MX7) || \
+ defined(CONFIG_ARCH_MX7ULP) || defined(CONFIG_ARCH_IMX8M)
+
+ hab_caam_clock_enable(1);
+
+ u32 out_jr_size = sec_in32(CFG_SYS_FSL_JR0_ADDR +
+ FSL_CAAM_ORSR_JRa_OFFSET);
+ if (out_jr_size != FSL_CAAM_MAX_JR_SIZE)
+ sec_init();
+#endif
+
+ if (enc)
+ ret = blob_encap(km_ptr, src_ptr, dst_ptr, len);
+ else
+ ret = blob_decap(km_ptr, src_ptr, dst_ptr, len);
+
+ return ret;
+}
+
+/***************************************************/
+U_BOOT_LONGHELP(blob,
+ "enc src dst len km - Encapsulate and create blob of data\n"
+ " $len bytes long at address $src and\n"
+ " store the result at address $dst.\n"
+ " $km is the address where the key\n"
+ " modifier is stored.\n"
+ " The modifier is required for generation\n"
+ " /use as key for cryptographic operation.\n"
+ " Key modifier should be 16 byte long.\n"
+ "blob dec src dst len km - Decapsulate the blob of data at address\n"
+ " $src and store result of $len byte at\n"
+ " addr $dst.\n"
+ " $km is the address where the key\n"
+ " modifier is stored.\n"
+ " The modifier is required for generation\n"
+ " /use as key for cryptographic operation.\n"
+ " Key modifier should be 16 byte long.\n");
+
+U_BOOT_CMD(
+ blob, 6, 1, do_blob,
+ "Blob encapsulation/decryption",
+ blob_help_text
+);
diff --git a/cmd/bloblist.c b/cmd/bloblist.c
new file mode 100644
index 00000000000..333ae558142
--- /dev/null
+++ b/cmd/bloblist.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Command-line access to bloblist features
+ *
+ * Copyright 2020 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <bloblist.h>
+#include <command.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int do_bloblist_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bloblist_show_stats();
+
+ return 0;
+}
+
+static int do_bloblist_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bloblist_show_list();
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(bloblist,
+ "info - show information about the bloblist\n"
+ "bloblist list - list blobs in the bloblist");
+
+U_BOOT_CMD_WITH_SUBCMDS(bloblist, "Bloblists", bloblist_help_text,
+ U_BOOT_SUBCMD_MKENT(info, 1, 1, do_bloblist_info),
+ U_BOOT_SUBCMD_MKENT(list, 1, 1, do_bloblist_list));
diff --git a/cmd/bmp.c b/cmd/bmp.c
new file mode 100644
index 00000000000..3b618448624
--- /dev/null
+++ b/cmd/bmp.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2002
+ * Detlev Zundel, DENX Software Engineering, dzu@denx.de.
+ */
+
+/*
+ * BMP handling routines
+ */
+
+#include <command.h>
+#include <image.h>
+#include <mapmem.h>
+#include <splash.h>
+#include <video.h>
+#include <stdlib.h>
+
+static int do_bmp_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr;
+
+ switch (argc) {
+ case 1: /* use image_load_addr as default address */
+ addr = image_load_addr;
+ break;
+ case 2: /* use argument */
+ addr = hextoul(argv[1], NULL);
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ return (bmp_info(addr));
+}
+
+static int do_bmp_display(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr;
+ int x = 0, y = 0;
+
+ splash_get_pos(&x, &y);
+
+ switch (argc) {
+ case 1: /* use image_load_addr as default address */
+ addr = image_load_addr;
+ break;
+ case 2: /* use argument */
+ addr = hextoul(argv[1], NULL);
+ break;
+ case 4:
+ addr = hextoul(argv[1], NULL);
+ if (!strcmp(argv[2], "m"))
+ x = BMP_ALIGN_CENTER;
+ else
+ x = dectoul(argv[2], NULL);
+ if (!strcmp(argv[3], "m"))
+ y = BMP_ALIGN_CENTER;
+ else
+ y = dectoul(argv[3], NULL);
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ return (bmp_display(addr, x, y));
+}
+
+static struct cmd_tbl cmd_bmp_sub[] = {
+ U_BOOT_CMD_MKENT(info, 3, 0, do_bmp_info, "", ""),
+ U_BOOT_CMD_MKENT(display, 5, 0, do_bmp_display, "", ""),
+};
+
+static int do_bmp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct cmd_tbl *c;
+
+ /* Strip off leading 'bmp' command argument */
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], &cmd_bmp_sub[0], ARRAY_SIZE(cmd_bmp_sub));
+
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+ else
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+ bmp, 5, 1, do_bmp,
+ "manipulate BMP image data",
+ "info <imageAddr> - display image info\n"
+ "bmp display <imageAddr> [x y] - display image at x,y"
+);
diff --git a/cmd/boot.c b/cmd/boot.c
new file mode 100644
index 00000000000..23496cafdf5
--- /dev/null
+++ b/cmd/boot.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000-2003
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+/*
+ * Misc boot support
+ */
+#include <command.h>
+#include <net.h>
+#include <vsprintf.h>
+
+#ifdef CONFIG_CMD_GO
+
+/* Allow ports to override the default behavior */
+__attribute__((weak))
+unsigned long do_go_exec(ulong (*entry)(int, char * const []), int argc,
+ char *const argv[])
+{
+ return entry (argc, argv);
+}
+
+static int do_go(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ ulong addr, rc;
+ int rcode = 0;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ addr = hextoul(argv[1], NULL);
+
+ printf ("## Starting application at 0x%08lX ...\n", addr);
+ flush();
+
+ /*
+ * pass address parameter as argv[0] (aka command name),
+ * and all remaining args
+ */
+ rc = do_go_exec ((void *)addr, argc - 1, argv + 1);
+ if (rc != 0) rcode = 1;
+
+ printf ("## Application terminated, rc = 0x%lX\n", rc);
+ return rcode;
+}
+
+/* -------------------------------------------------------------------- */
+
+U_BOOT_CMD(
+ go, CONFIG_SYS_MAXARGS, 1, do_go,
+ "start application at address 'addr'",
+ "addr [arg ...]\n - start application at address 'addr'\n"
+ " passing 'arg' as arguments"
+);
+
+#endif
+
+U_BOOT_CMD(
+ reset, 2, 0, do_reset,
+ "Perform RESET of the CPU",
+ "- cold boot without level specifier\n"
+ "reset -w - warm reset if implemented"
+);
+
+#ifdef CONFIG_CMD_POWEROFF
+U_BOOT_CMD(
+ poweroff, 1, 0, do_poweroff,
+ "Perform POWEROFF of the device",
+ ""
+);
+#endif
diff --git a/cmd/bootcount.c b/cmd/bootcount.c
new file mode 100644
index 00000000000..5e3b66e676b
--- /dev/null
+++ b/cmd/bootcount.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <command.h>
+#include <bootcount.h>
+
+static int do_bootcount_print(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ printf("%lu\n", bootcount_load());
+ return CMD_RET_SUCCESS;
+}
+
+static int do_bootcount_reset(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ /*
+ * note that we're explicitly not resetting the environment
+ * variable, so you still have the old bootcounter available
+ */
+ bootcount_store(0);
+ return CMD_RET_SUCCESS;
+}
+
+static struct cmd_tbl bootcount_sub[] = {
+ U_BOOT_CMD_MKENT(print, 1, 1, do_bootcount_print, "", ""),
+ U_BOOT_CMD_MKENT(reset, 1, 1, do_bootcount_reset, "", ""),
+};
+
+static int do_bootcount(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* drop initial "bootcount" arg */
+ argc--;
+ argv++;
+
+ cp = find_cmd_tbl(argv[0], bootcount_sub, ARRAY_SIZE(bootcount_sub));
+ if (cp)
+ return cp->cmd(cmdtp, flag, argc, argv);
+
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_LONGHELP(bootcount,
+ "print - print current bootcounter\n"
+ "reset - reset the bootcounter");
+
+U_BOOT_CMD(bootcount, 2, 1, do_bootcount,
+ "bootcount",
+ bootcount_help_text
+);
diff --git a/cmd/bootdev.c b/cmd/bootdev.c
new file mode 100644
index 00000000000..4bc229e809a
--- /dev/null
+++ b/cmd/bootdev.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 'bootdev' command
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootstd.h>
+#include <command.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+
+static int bootdev_check_state(struct bootstd_priv **stdp)
+{
+ struct bootstd_priv *std;
+ int ret;
+
+ ret = bootstd_get_priv(&std);
+ if (ret)
+ return ret;
+ if (!std->cur_bootdev) {
+ printf("Please use 'bootdev select' first\n");
+ return -ENOENT;
+ }
+ *stdp = std;
+
+ return 0;
+}
+
+static int do_bootdev_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool probe;
+
+ probe = argc >= 2 && !strcmp(argv[1], "-p");
+ bootdev_list(probe);
+
+ return 0;
+}
+
+static int do_bootdev_select(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *std;
+ struct udevice *dev;
+ int ret;
+
+ ret = bootstd_get_priv(&std);
+ if (ret)
+ return CMD_RET_FAILURE;
+ if (argc < 2) {
+ std->cur_bootdev = NULL;
+ return 0;
+ }
+ if (bootdev_find_by_any(argv[1], &dev, NULL))
+ return CMD_RET_FAILURE;
+
+ std->cur_bootdev = dev;
+
+ return 0;
+}
+
+static int do_bootdev_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *priv;
+ struct bootflow *bflow;
+ int ret, i, num_valid;
+ struct udevice *dev;
+ bool probe;
+
+ probe = argc >= 2 && !strcmp(argv[1], "-p");
+
+ ret = bootdev_check_state(&priv);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ dev = priv->cur_bootdev;
+
+ /* Count the number of bootflows, including how many are valid */
+ num_valid = 0;
+ for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
+ !ret;
+ ret = bootdev_next_bootflow(&bflow), i++)
+ num_valid += bflow->state == BOOTFLOWST_READY;
+
+ /*
+ * Prove the device, if requested, otherwise assume that there is no
+ * error
+ */
+ ret = 0;
+ if (probe)
+ ret = device_probe(dev);
+
+ printf("Name: %s\n", dev->name);
+ printf("Sequence: %d\n", dev_seq(dev));
+ printf("Status: %s\n", ret ? simple_itoa(-ret) : device_active(dev) ?
+ "Probed" : "OK");
+ printf("Uclass: %s\n", dev_get_uclass_name(dev_get_parent(dev)));
+ printf("Bootflows: %d (%d valid)\n", i, num_valid);
+
+ return 0;
+}
+
+static int do_bootdev_hunt(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *priv;
+ const char *spec = NULL;
+ bool list = false;
+ int ret = 0;
+
+ if (argc >= 2) {
+ if (!strcmp(argv[1], "-l"))
+ list = true;
+ else
+ spec = argv[1];
+ }
+
+ ret = bootstd_get_priv(&priv);
+ if (ret)
+ return ret;
+ if (list) {
+ bootdev_list_hunters(priv);
+ } else {
+ ret = bootdev_hunt(spec, true);
+ if (ret) {
+ printf("Failed (err=%dE)\n", ret);
+
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(bootdev,
+ "list [-p] - list all available bootdevs (-p to probe)\n"
+ "bootdev hunt [-l|<spec>] - use hunt drivers to find bootdevs\n"
+ "bootdev select <bd> - select a bootdev by name | label | seq\n"
+ "bootdev info [-p] - show information about a bootdev (-p to probe)");
+
+U_BOOT_CMD_WITH_SUBCMDS(bootdev, "Boot devices", bootdev_help_text,
+ U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootdev_list),
+ U_BOOT_SUBCMD_MKENT(hunt, 2, 1, do_bootdev_hunt),
+ U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootdev_select),
+ U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootdev_info));
diff --git a/cmd/bootefi.c b/cmd/bootefi.c
new file mode 100644
index 00000000000..b8f5bb35950
--- /dev/null
+++ b/cmd/bootefi.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application loader
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+
+#include <command.h>
+#include <efi.h>
+#include <efi_device_path.h>
+#include <efi_loader.h>
+#include <exports.h>
+#include <log.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <vsprintf.h>
+#include <asm-generic/sections.h>
+#include <asm/global_data.h>
+#include <linux/string.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct efi_device_path *test_image_path;
+static struct efi_device_path *test_device_path;
+
+static efi_status_t bootefi_run_prepare(const char *load_options_path,
+ struct efi_device_path *device_path,
+ struct efi_device_path *image_path,
+ struct efi_loaded_image_obj **image_objp,
+ struct efi_loaded_image **loaded_image_infop)
+{
+ efi_status_t ret;
+ u16 *load_options;
+
+ ret = efi_setup_loaded_image(device_path, image_path, image_objp,
+ loaded_image_infop);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ (*image_objp)->auth_status = EFI_IMAGE_AUTH_PASSED;
+ (*image_objp)->entry = efi_selftest;
+
+ /* Transfer environment variable as load options */
+ return efi_env_set_load_options((efi_handle_t)*image_objp,
+ load_options_path,
+ &load_options);
+}
+
+/**
+ * bootefi_test_prepare() - prepare to run an EFI test
+ *
+ * Prepare to run a test as if it were provided by a loaded image.
+ *
+ * @image_objp: pointer to be set to the loaded image handle
+ * @loaded_image_infop: pointer to be set to the loaded image protocol
+ * @path: dummy file path used to construct the device path
+ * set in the loaded image protocol
+ * @load_options_path: name of a U-Boot environment variable. Its value is
+ * set as load options in the loaded image protocol.
+ * Return: status code
+ */
+static efi_status_t bootefi_test_prepare
+ (struct efi_loaded_image_obj **image_objp,
+ struct efi_loaded_image **loaded_image_infop, const char *path,
+ const char *load_options_path)
+{
+ efi_status_t ret;
+
+ /* Construct a dummy device path */
+ test_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, 0, 0);
+ if (!test_device_path)
+ return EFI_OUT_OF_RESOURCES;
+
+ test_image_path = efi_dp_from_file(NULL, path);
+ if (!test_image_path) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto failure;
+ }
+
+ ret = bootefi_run_prepare(load_options_path, test_device_path,
+ test_image_path, image_objp,
+ loaded_image_infop);
+ if (ret == EFI_SUCCESS)
+ return ret;
+
+failure:
+ efi_free_pool(test_device_path);
+ efi_free_pool(test_image_path);
+ /* TODO: not sure calling clear function is necessary */
+ efi_clear_bootdev();
+ return ret;
+}
+
+/**
+ * do_efi_selftest() - execute EFI selftest
+ *
+ * Return: status code
+ */
+static int do_efi_selftest(void)
+{
+ struct efi_loaded_image_obj *image_obj;
+ struct efi_loaded_image *loaded_image_info;
+ efi_status_t ret;
+
+ ret = bootefi_test_prepare(&image_obj, &loaded_image_info,
+ "\\selftest", "efi_selftest");
+ if (ret != EFI_SUCCESS)
+ return CMD_RET_FAILURE;
+
+ /* Execute the test */
+ ret = do_bootefi_exec(&image_obj->header,
+ loaded_image_info->load_options);
+ efi_free_pool(test_device_path);
+ efi_free_pool(test_image_path);
+ if (ret != EFI_SUCCESS)
+ efi_delete_handle(&image_obj->header);
+ else
+ ret = efi_delete_handle(&image_obj->header);
+
+ return ret != EFI_SUCCESS;
+}
+
+/**
+ * do_bootefi() - execute `bootefi` command
+ *
+ * @cmdtp: table entry describing command
+ * @flag: bitmap indicating how the command was invoked
+ * @argc: number of arguments
+ * @argv: command line arguments
+ * Return: status code
+ */
+static int do_bootefi(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ efi_status_t ret;
+ char *p;
+ void *fdt, *initrd = NULL, *image_buf;
+ unsigned long addr, size, rd_len = 0, fdt_addr = 0;
+ void *image_addr;
+ size_t image_size;
+ int fdt_arg = 2;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (argc > 2) {
+ ulong rd_addr = 0;
+ char *end = strchr(argv[2], ':');
+
+ if (end) {
+ rd_addr = hextoul(argv[2], NULL);
+ if (!rd_addr)
+ return CMD_RET_USAGE;
+
+ rd_len = hextoul(++end, NULL);
+ initrd = map_sysmem(rd_addr, rd_len);
+ ++fdt_arg;
+ }
+ }
+
+ if (argc > fdt_arg + 1)
+ return CMD_RET_USAGE;
+ if (argc == fdt_arg + 1)
+ fdt_addr = hextoul(argv[fdt_arg], NULL);
+
+ if (fdt_addr)
+ fdt = map_sysmem(fdt_addr, 0);
+ else
+ fdt = EFI_FDT_USE_INTERNAL;
+
+ if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) &&
+ !strcmp(argv[1], "bootmgr")) {
+ ret = efi_bootmgr_run(fdt);
+
+ if (ret != EFI_SUCCESS)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+ }
+
+ if (IS_ENABLED(CONFIG_CMD_BOOTEFI_SELFTEST) &&
+ !strcmp(argv[1], "selftest")) {
+ /* Initialize EFI drivers */
+ ret = efi_init_obj_list();
+ if (ret != EFI_SUCCESS) {
+ log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
+ ret & ~EFI_ERROR_MASK);
+ return CMD_RET_FAILURE;
+ }
+
+ ret = efi_install_fdt(fdt);
+ if (ret != EFI_SUCCESS)
+ return CMD_RET_FAILURE;
+
+ return do_efi_selftest();
+ }
+
+ if (!IS_ENABLED(CONFIG_CMD_BOOTEFI_BINARY))
+ return CMD_RET_SUCCESS;
+
+ if (IS_ENABLED(CONFIG_CMD_BOOTEFI_HELLO) &&
+ !strcmp(argv[1], "hello")) {
+ image_buf = __efi_helloworld_begin;
+ size = __efi_helloworld_end - __efi_helloworld_begin;
+ /* TODO: not sure calling clear function is necessary */
+ efi_clear_bootdev();
+ } else {
+ addr = strtoul(argv[1], NULL, 16);
+ /* Check that a numeric value was passed */
+ if (!addr)
+ return CMD_RET_USAGE;
+ image_buf = map_sysmem(addr, 0);
+
+ p = strchr(argv[1], ':');
+ if (p) {
+ size = strtoul(++p, NULL, 16);
+ if (!size)
+ return CMD_RET_USAGE;
+ efi_clear_bootdev();
+ } else {
+ /* Image should be already loaded */
+ efi_get_image_parameters(&image_addr, &image_size);
+
+ if (image_buf != image_addr) {
+ log_err("No UEFI binary known at %s\n",
+ argv[1]);
+ return CMD_RET_FAILURE;
+ }
+ size = image_size;
+ }
+ }
+
+ ret = efi_binary_run(image_buf, size, fdt, initrd, rd_len);
+
+ if (ret != EFI_SUCCESS)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_LONGHELP(bootefi,
+ "<image address>[:<size>] [<initrd address>:<size>] [<fdt address>]\n"
+ " - boot EFI payload\n"
+#ifdef CONFIG_CMD_BOOTEFI_HELLO
+ "bootefi hello\n"
+ " - boot a sample Hello World application stored within U-Boot\n"
+#endif
+#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
+ "bootefi selftest [fdt address]\n"
+ " - boot an EFI selftest application stored within U-Boot\n"
+ " Use environment variable efi_selftest to select a single test.\n"
+ " Use 'setenv efi_selftest list' to enumerate all tests.\n"
+#endif
+#ifdef CONFIG_CMD_BOOTEFI_BOOTMGR
+ "bootefi bootmgr [fdt address]\n"
+ " - load and boot EFI payload based on BootOrder/BootXXXX variables.\n"
+ "\n"
+ " If specified, the device tree located at <fdt address> gets\n"
+ " exposed as EFI configuration table.\n"
+#endif
+ );
+
+U_BOOT_CMD(
+ bootefi, 4, 0, do_bootefi,
+ "Boots an EFI payload from memory",
+ bootefi_help_text
+);
diff --git a/cmd/bootflow.c b/cmd/bootflow.c
new file mode 100644
index 00000000000..551dffbb8b8
--- /dev/null
+++ b/cmd/bootflow.c
@@ -0,0 +1,638 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 'bootflow' command
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootm.h>
+#include <bootstd.h>
+#include <command.h>
+#include <console.h>
+#include <dm.h>
+#include <env.h>
+#include <expo.h>
+#include <log.h>
+#include <mapmem.h>
+
+/**
+ * report_bootflow_err() - Report where a bootflow failed
+ *
+ * When a bootflow does not make it to the 'loaded' state, something went wrong.
+ * Print a helpful message if there is an error
+ *
+ * @bflow: Bootflow to process
+ * @err: Error code (0 if none)
+ */
+static void report_bootflow_err(struct bootflow *bflow, int err)
+{
+ if (!err)
+ return;
+
+ /* Indent out to 'Method' */
+ printf(" ** ");
+
+ switch (bflow->state) {
+ case BOOTFLOWST_BASE:
+ printf("No media/partition found");
+ break;
+ case BOOTFLOWST_MEDIA:
+ printf("No partition found");
+ break;
+ case BOOTFLOWST_PART:
+ printf("No filesystem found");
+ break;
+ case BOOTFLOWST_FS:
+ printf("File not found");
+ break;
+ case BOOTFLOWST_FILE:
+ printf("File cannot be loaded");
+ break;
+ case BOOTFLOWST_READY:
+ printf("Ready");
+ break;
+ case BOOTFLOWST_COUNT:
+ break;
+ }
+
+ printf(", err=%dE\n", err);
+}
+
+/**
+ * show_bootflow() - Show the status of a bootflow
+ *
+ * @seq: Bootflow index
+ * @bflow: Bootflow to show
+ * @errors: True to show the error received, if any
+ */
+static void show_bootflow(int index, struct bootflow *bflow, bool errors)
+{
+ printf("%3x %-11s %-6s %-9.9s %4x %-25.25s %s\n", index,
+ bflow->method->name, bootflow_state_get_name(bflow->state),
+ bflow->dev ? dev_get_uclass_name(dev_get_parent(bflow->dev)) :
+ "(none)", bflow->part, bflow->name, bflow->fname ?: "");
+ if (errors)
+ report_bootflow_err(bflow, bflow->err);
+}
+
+static void show_header(void)
+{
+ printf("Seq Method State Uclass Part Name Filename\n");
+ printf("--- ----------- ------ -------- ---- ------------------------ ----------------\n");
+}
+
+static void show_footer(int count, int num_valid)
+{
+ printf("--- ----------- ------ -------- ---- ------------------------ ----------------\n");
+ printf("(%d bootflow%s, %d valid)\n", count, count != 1 ? "s" : "",
+ num_valid);
+}
+
+/**
+ * bootflow_handle_menu() - Handle running the menu and updating cur bootflow
+ *
+ * This shows the menu, allows the user to select something and then prints
+ * what happened
+ *
+ * @std: bootstd information
+ * @text_mode: true to run the menu in text mode
+ * @bflowp: Returns selected bootflow, on success
+ * Return: 0 on success (a bootflow was selected), -EAGAIN if nothing was
+ * chosen, other -ve value on other error
+ */
+__maybe_unused static int bootflow_handle_menu(struct bootstd_priv *std,
+ bool text_mode,
+ struct bootflow **bflowp)
+{
+ struct expo *exp;
+ struct bootflow *bflow;
+ int ret, seq;
+
+ ret = bootflow_menu_start(std, text_mode, &exp);
+ if (ret)
+ return log_msg_ret("bhs", ret);
+
+ ret = -ERESTART;
+ do {
+ if (ret == -ERESTART) {
+ ret = expo_render(exp);
+ if (ret)
+ return log_msg_ret("bhr", ret);
+ }
+ ret = bootflow_menu_poll(exp, &seq);
+ } while (ret == -EAGAIN || ret == -ERESTART);
+
+ if (ret == -EPIPE) {
+ printf("Nothing chosen\n");
+ std->cur_bootflow = NULL;
+ } else if (ret) {
+ printf("Menu failed (err=%d)\n", ret);
+ } else {
+ bflow = alist_getw(&std->bootflows, seq, struct bootflow);
+ printf("Selected: %s\n", bflow->os_name ? bflow->os_name :
+ bflow->name);
+ std->cur_bootflow = bflow;
+ *bflowp = bflow;
+ }
+ expo_destroy(exp);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *std;
+ struct bootflow_iter iter;
+ struct udevice *dev = NULL;
+ struct bootflow bflow;
+ bool all = false, boot = false, errors = false, no_global = false;
+ bool list = false, no_hunter = false, menu = false, text_mode = false;
+ int num_valid = 0;
+ const char *label = NULL;
+ bool has_args;
+ int ret, i;
+ int flags;
+
+ ret = bootstd_get_priv(&std);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ has_args = argc > 1 && *argv[1] == '-';
+ if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL)) {
+ if (has_args) {
+ all = strchr(argv[1], 'a');
+ boot = strchr(argv[1], 'b');
+ errors = strchr(argv[1], 'e');
+ no_global = strchr(argv[1], 'G');
+ list = strchr(argv[1], 'l');
+ no_hunter = strchr(argv[1], 'H');
+ menu = strchr(argv[1], 'm');
+ text_mode = strchr(argv[1], 't');
+ argc--;
+ argv++;
+ }
+ if (argc > 1)
+ label = argv[1];
+ if (!label)
+ dev = std->cur_bootdev;
+ } else {
+ if (has_args) {
+ printf("Flags not supported: enable CONFIG_BOOTSTD_FULL\n");
+ return CMD_RET_USAGE;
+ }
+ boot = true;
+ }
+
+ std->cur_bootflow = NULL;
+
+ flags = BOOTFLOWIF_ONLY_BOOTABLE;
+ if (list)
+ flags |= BOOTFLOWIF_SHOW;
+ if (all)
+ flags |= BOOTFLOWIF_ALL;
+ if (no_global)
+ flags |= BOOTFLOWIF_SKIP_GLOBAL;
+ if (!no_hunter)
+ flags |= BOOTFLOWIF_HUNT;
+
+ /*
+ * If we have a device, just scan for bootflows attached to that device
+ */
+ if (list) {
+ printf("Scanning for bootflows ");
+ if (dev)
+ printf("in bootdev '%s'\n", dev->name);
+ else if (label)
+ printf("with label '%s'\n", label);
+ else
+ printf("in all bootdevs\n");
+ show_header();
+ }
+ if (dev)
+ bootstd_clear_bootflows_for_bootdev(dev);
+ else
+ bootstd_clear_glob();
+ for (i = 0,
+ ret = bootflow_scan_first(dev, label, &iter, flags, &bflow);
+ i < 1000 && ret != -ENODEV;
+ i++, ret = bootflow_scan_next(&iter, &bflow)) {
+ bflow.err = ret;
+ if (!ret)
+ num_valid++;
+ ret = bootstd_add_bootflow(&bflow);
+ if (ret < 0) {
+ printf("Out of memory\n");
+ return CMD_RET_FAILURE;
+ }
+ if (list)
+ show_bootflow(i, &bflow, errors);
+ if (!menu && boot && !bflow.err)
+ bootflow_run_boot(&iter, &bflow);
+ }
+ bootflow_iter_uninit(&iter);
+ if (list)
+ show_footer(i, num_valid);
+
+ if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL) && IS_ENABLED(CONFIG_EXPO)) {
+ if (!num_valid && !list) {
+ printf("No bootflows found; try again with -l\n");
+ } else if (menu) {
+ struct bootflow *sel_bflow;
+
+ ret = bootflow_handle_menu(std, text_mode, &sel_bflow);
+ if (!ret && boot) {
+ ret = console_clear();
+ if (ret) {
+ log_err("Failed to clear console: %dE\n",
+ ret);
+ return ret;
+ }
+
+ bootflow_run_boot(NULL, sel_bflow);
+ }
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_CMD_BOOTFLOW_FULL
+static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *std;
+ struct udevice *dev;
+ struct bootflow *bflow;
+ int num_valid = 0;
+ bool errors = false;
+ int ret, i;
+
+ if (argc > 1 && *argv[1] == '-')
+ errors = strchr(argv[1], 'e');
+
+ ret = bootstd_get_priv(&std);
+ if (ret)
+ return CMD_RET_FAILURE;
+ dev = std->cur_bootdev;
+
+ /* If we have a device, just list bootflows attached to that device */
+ if (dev) {
+ printf("Showing bootflows for bootdev '%s'\n", dev->name);
+ show_header();
+ for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
+ !ret;
+ ret = bootdev_next_bootflow(&bflow), i++) {
+ num_valid += bflow->state == BOOTFLOWST_READY;
+ show_bootflow(i, bflow, errors);
+ }
+ } else {
+ printf("Showing all bootflows\n");
+ show_header();
+ for (ret = bootflow_first_glob(&bflow), i = 0;
+ !ret;
+ ret = bootflow_next_glob(&bflow), i++) {
+ num_valid += bflow->state == BOOTFLOWST_READY;
+ show_bootflow(i, bflow, errors);
+ }
+ }
+ show_footer(i, num_valid);
+
+ return 0;
+}
+
+static int do_bootflow_select(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *std;
+ struct bootflow *bflow, *found;
+ struct udevice *dev;
+ const char *name;
+ char *endp;
+ int seq, i;
+ int ret;
+
+ ret = bootstd_get_priv(&std);
+ if (ret)
+ return CMD_RET_FAILURE;
+;
+ if (argc < 2) {
+ std->cur_bootflow = NULL;
+ return 0;
+ }
+ dev = std->cur_bootdev;
+
+ name = argv[1];
+ seq = simple_strtol(name, &endp, 16);
+ found = NULL;
+
+ /*
+ * If we have a bootdev device, only allow selection of bootflows
+ * attached to that device
+ */
+ if (dev) {
+ for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
+ !ret;
+ ret = bootdev_next_bootflow(&bflow), i++) {
+ if (*endp ? !strcmp(bflow->name, name) : i == seq) {
+ found = bflow;
+ break;
+ }
+ }
+ } else {
+ for (ret = bootflow_first_glob(&bflow), i = 0;
+ !ret;
+ ret = bootflow_next_glob(&bflow), i++) {
+ if (*endp ? !strcmp(bflow->name, name) : i == seq) {
+ found = bflow;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ printf("Cannot find bootflow '%s' ", name);
+ if (dev)
+ printf("in bootdev '%s' ", dev->name);
+ printf("(err=%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ std->cur_bootflow = found;
+ if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) {
+ if (env_set("bootargs", found->cmdline)) {
+ printf("Cannot set bootargs\n");
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ return 0;
+}
+
+static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *std;
+ struct bootflow *bflow;
+ bool x86_setup = false;
+ bool dump = false;
+ int ret;
+
+ if (argc > 1 && *argv[1] == '-') {
+ dump = strchr(argv[1], 'd');
+ x86_setup = strchr(argv[1], 's');
+ }
+
+ ret = bootstd_get_priv(&std);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ if (!std->cur_bootflow) {
+ printf("No bootflow selected\n");
+ return CMD_RET_FAILURE;
+ }
+ bflow = std->cur_bootflow;
+
+ if (IS_ENABLED(CONFIG_X86) && x86_setup) {
+ zimage_dump(bflow->x86_setup, false);
+
+ return 0;
+ }
+
+ printf("Name: %s\n", bflow->name);
+ printf("Device: %s\n", bflow->dev->name);
+ printf("Block dev: %s\n", bflow->blk ? bflow->blk->name : "(none)");
+ printf("Method: %s\n", bflow->method->name);
+ printf("State: %s\n", bootflow_state_get_name(bflow->state));
+ printf("Partition: %d\n", bflow->part);
+ printf("Subdir: %s\n", bflow->subdir ? bflow->subdir : "(none)");
+ printf("Filename: %s\n", bflow->fname);
+ printf("Buffer: ");
+ if (bflow->buf)
+ printf("%lx\n", (ulong)map_to_sysmem(bflow->buf));
+ else
+ printf("(not loaded)\n");
+ printf("Size: %x (%d bytes)\n", bflow->size, bflow->size);
+ printf("OS: %s\n", bflow->os_name ? bflow->os_name : "(none)");
+ printf("Cmdline: ");
+ if (bflow->cmdline)
+ puts(bflow->cmdline);
+ else
+ puts("(none)");
+ putc('\n');
+ if (bflow->x86_setup)
+ printf("X86 setup: %lx\n",
+ (ulong)map_to_sysmem(bflow->x86_setup));
+ printf("Logo: %s\n", bflow->logo ?
+ simple_xtoa((ulong)map_to_sysmem(bflow->logo)) : "(none)");
+ if (bflow->logo) {
+ printf("Logo size: %x (%d bytes)\n", bflow->logo_size,
+ bflow->logo_size);
+ }
+ printf("FDT: %s\n", bflow->fdt_fname);
+ if (bflow->fdt_fname) {
+ printf("FDT size: %x (%d bytes)\n", bflow->fdt_size,
+ bflow->fdt_size);
+ printf("FDT addr: %lx\n", bflow->fdt_addr);
+ }
+ printf("Error: %d\n", bflow->err);
+ if (dump && bflow->buf) {
+ /* Set some sort of maximum on the size */
+ int size = min(bflow->size, 10 << 10);
+ int i;
+
+ printf("Contents:\n\n");
+ for (i = 0; i < size; i++) {
+ putc(bflow->buf[i]);
+ if (!(i % 128) && ctrlc()) {
+ printf("...interrupted\n");
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int do_bootflow_read(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *std;
+ struct bootflow *bflow;
+ int ret;
+
+ ret = bootstd_get_priv(&std);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ /*
+ * Require a current bootflow. Users can use 'bootflow scan -b' to
+ * automatically scan and boot, if needed.
+ */
+ if (!std->cur_bootflow) {
+ printf("No bootflow selected\n");
+ return CMD_RET_FAILURE;
+ }
+ bflow = std->cur_bootflow;
+ ret = bootflow_read_all(bflow);
+ if (ret) {
+ printf("Failed: err=%dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *std;
+ struct bootflow *bflow;
+ int ret;
+
+ ret = bootstd_get_priv(&std);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ /*
+ * Require a current bootflow. Users can use 'bootflow scan -b' to
+ * automatically scan and boot, if needed.
+ */
+ if (!std->cur_bootflow) {
+ printf("No bootflow selected\n");
+ return CMD_RET_FAILURE;
+ }
+ bflow = std->cur_bootflow;
+ ret = bootflow_run_boot(NULL, bflow);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+static int do_bootflow_menu(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *std;
+ struct bootflow *bflow;
+ bool text_mode = false;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_EXPO)) {
+ printf("Menu not supported\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc > 1 && *argv[1] == '-')
+ text_mode = strchr(argv[1], 't');
+
+ ret = bootstd_get_priv(&std);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ ret = bootflow_handle_menu(std, text_mode, &bflow);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+static int do_bootflow_cmdline(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *std;
+ struct bootflow *bflow;
+ const char *op, *arg, *val = NULL;
+ int ret;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ ret = bootstd_get_priv(&std);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ bflow = std->cur_bootflow;
+ if (!bflow) {
+ printf("No bootflow selected\n");
+ return CMD_RET_FAILURE;
+ }
+
+ op = argv[1];
+ arg = argv[2];
+ if (*op == 's') {
+ val = argv[3] ?: (const char *)BOOTFLOWCL_EMPTY;
+ }
+
+ switch (*op) {
+ case 'c': /* clear */
+ val = "";
+ fallthrough;
+ case 's': /* set */
+ case 'd': /* delete */
+ ret = bootflow_cmdline_set_arg(bflow, arg, val, true);
+ break;
+ case 'g': /* get */
+ ret = bootflow_cmdline_get_arg(bflow, arg, &val);
+ if (ret >= 0)
+ printf("%.*s\n", ret, val);
+ break;
+ case 'a': /* auto */
+ ret = bootflow_cmdline_auto(bflow, arg);
+ break;
+ }
+ switch (ret) {
+ case -E2BIG:
+ printf("Argument too long\n");
+ break;
+ case -ENOENT:
+ printf("Argument not found\n");
+ break;
+ case -EINVAL:
+ printf("Mismatched quotes\n");
+ break;
+ case -EBADF:
+ printf("Value must be quoted\n");
+ break;
+ default:
+ if (ret < 0)
+ printf("Unknown error: %dE\n", ret);
+ }
+ if (ret < 0)
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+#endif /* CONFIG_CMD_BOOTFLOW_FULL */
+
+U_BOOT_LONGHELP(bootflow,
+#ifdef CONFIG_CMD_BOOTFLOW_FULL
+ "scan [-abeGl] [bdev] - scan for valid bootflows (-l list, -a all, -e errors, -b boot, -G no global)\n"
+ "bootflow list [-e] - list scanned bootflows (-e errors)\n"
+ "bootflow select [<num>|<name>] - select a bootflow\n"
+ "bootflow info [-ds] - show info on current bootflow (-d dump bootflow)\n"
+ "bootflow read - read all current-bootflow files\n"
+ "bootflow boot - boot current bootflow\n"
+ "bootflow menu [-t] - show a menu of available bootflows\n"
+ "bootflow cmdline [set|get|clear|delete|auto] <param> [<value>] - update cmdline"
+#else
+ "scan - boot first available bootflow\n"
+#endif
+ );
+
+U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text,
+ U_BOOT_SUBCMD_MKENT(scan, 3, 1, do_bootflow_scan),
+#ifdef CONFIG_CMD_BOOTFLOW_FULL
+ U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootflow_list),
+ U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootflow_select),
+ U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info),
+ U_BOOT_SUBCMD_MKENT(read, 1, 1, do_bootflow_read),
+ U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot),
+ U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu),
+ U_BOOT_SUBCMD_MKENT(cmdline, 4, 1, do_bootflow_cmdline),
+#endif
+);
diff --git a/cmd/booti.c b/cmd/booti.c
new file mode 100644
index 00000000000..e6f67d6e136
--- /dev/null
+++ b/cmd/booti.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000-2009
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <bootm.h>
+#include <command.h>
+#include <env.h>
+#include <image.h>
+#include <irq_func.h>
+#include <lmb.h>
+#include <log.h>
+#include <mapmem.h>
+#include <asm/global_data.h>
+#include <linux/kernel.h>
+#include <linux/sizes.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+/*
+ * Image booting support
+ */
+static int booti_start(struct bootm_info *bmi)
+{
+ struct bootm_headers *images = bmi->images;
+ int ret;
+ ulong ld;
+ ulong relocated_addr;
+ ulong image_size;
+ uint8_t *temp;
+ ulong dest;
+ ulong dest_end;
+ phys_addr_t ep_addr;
+ unsigned long comp_len;
+ unsigned long decomp_len;
+ int ctype;
+
+ ret = bootm_run_states(bmi, BOOTM_STATE_START);
+
+ /* Setup Linux kernel Image entry point */
+ if (!bmi->addr_img) {
+ ld = image_load_addr;
+ debug("* kernel: default image load address = 0x%08lx\n",
+ image_load_addr);
+ } else {
+ ld = hextoul(bmi->addr_img, NULL);
+ debug("* kernel: cmdline image address = 0x%08lx\n", ld);
+ }
+
+ temp = map_sysmem(ld, 0);
+ ctype = image_decomp_type(temp, 2);
+ if (ctype > 0) {
+ dest = env_get_ulong("kernel_comp_addr_r", 16, 0);
+ comp_len = env_get_ulong("kernel_comp_size", 16, 0);
+ if (!dest || !comp_len) {
+ puts("kernel_comp_addr_r or kernel_comp_size is not provided!\n");
+ return -EINVAL;
+ }
+ if (dest < gd->ram_base || dest > gd->ram_top) {
+ puts("kernel_comp_addr_r is outside of DRAM range!\n");
+ return -EINVAL;
+ }
+
+ debug("kernel image compression type %d size = 0x%08lx address = 0x%08lx\n",
+ ctype, comp_len, (ulong)dest);
+ decomp_len = comp_len * 10;
+ ret = image_decomp(ctype, 0, ld, IH_TYPE_KERNEL,
+ (void *)dest, (void *)ld, comp_len,
+ decomp_len, &dest_end);
+ if (ret)
+ return ret;
+ /* dest_end contains the uncompressed Image size */
+ memmove((void *) ld, (void *)dest, dest_end);
+ }
+ unmap_sysmem((void *)ld);
+
+ ret = booti_setup(ld, &relocated_addr, &image_size, false);
+ if (ret)
+ return 1;
+
+ /* Handle BOOTM_STATE_LOADOS */
+ if (relocated_addr != ld) {
+ printf("Moving Image from 0x%lx to 0x%lx, end=0x%lx\n", ld,
+ relocated_addr, relocated_addr + image_size);
+ memmove((void *)relocated_addr, (void *)ld, image_size);
+ }
+
+ images->ep = relocated_addr;
+ images->os.start = relocated_addr;
+ images->os.end = relocated_addr + image_size;
+
+ ep_addr = (phys_addr_t)images->ep;
+ ret = lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &ep_addr,
+ le32_to_cpu(image_size), LMB_NONE);
+ if (ret) {
+ printf("Failed to allocate memory for the image at %#llx\n",
+ (unsigned long long)images->ep);
+ return 1;
+ }
+
+ /*
+ * Handle the BOOTM_STATE_FINDOTHER state ourselves as we do not
+ * have a header that provide this informaiton.
+ */
+ if (bootm_find_images(image_load_addr, bmi->conf_ramdisk, bmi->conf_fdt,
+ relocated_addr, image_size))
+ return 1;
+
+ return 0;
+}
+
+int do_booti(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct bootm_info bmi;
+ int states;
+ int ret;
+
+ /* Consume 'booti' */
+ argc--; argv++;
+
+ bootm_init(&bmi);
+ if (argc)
+ bmi.addr_img = argv[0];
+ if (argc > 1)
+ bmi.conf_ramdisk = argv[1];
+ if (argc > 2)
+ bmi.conf_fdt = argv[2];
+ bmi.boot_progress = true;
+ bmi.cmd_name = "booti";
+ /* do not set up argc and argv[] since nothing uses them */
+
+ if (booti_start(&bmi))
+ return 1;
+
+ /*
+ * We are doing the BOOTM_STATE_LOADOS state ourselves, so must
+ * disable interrupts ourselves
+ */
+ bootm_disable_interrupts();
+
+ images.os.os = IH_OS_LINUX;
+ if (IS_ENABLED(CONFIG_RISCV_SMODE))
+ images.os.arch = IH_ARCH_RISCV;
+ else if (IS_ENABLED(CONFIG_ARM64))
+ images.os.arch = IH_ARCH_ARM64;
+
+ states = BOOTM_STATE_MEASURE | BOOTM_STATE_OS_PREP |
+ BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO;
+ if (IS_ENABLED(CONFIG_SYS_BOOT_RAMDISK_HIGH))
+ states |= BOOTM_STATE_RAMDISK;
+
+ ret = bootm_run_states(&bmi, states);
+
+ return ret;
+}
+
+U_BOOT_LONGHELP(booti,
+ "[addr [initrd[:size]] [fdt]]\n"
+ " - boot Linux flat or compressed 'Image' stored at 'addr'\n"
+ "\tThe argument 'initrd' is optional and specifies the address\n"
+ "\tof an initrd in memory. The optional parameter ':size' allows\n"
+ "\tspecifying the size of a RAW initrd.\n"
+ "\tCurrently only booting from gz, bz2, lzma and lz4 compression\n"
+ "\ttypes are supported. In order to boot from any of these compressed\n"
+ "\timages, user have to set kernel_comp_addr_r and kernel_comp_size environment\n"
+ "\tvariables beforehand.\n"
+#if defined(CONFIG_OF_LIBFDT)
+ "\tSince booting a Linux kernel requires a flat device-tree, a\n"
+ "\tthird argument providing the address of the device-tree blob\n"
+ "\tis required. To boot a kernel with a device-tree blob but\n"
+ "\twithout an initrd image, use a '-' for the initrd argument.\n"
+#endif
+ );
+
+U_BOOT_CMD(
+ booti, CONFIG_SYS_MAXARGS, 1, do_booti,
+ "boot Linux kernel 'Image' format from memory", booti_help_text
+);
diff --git a/cmd/bootm.c b/cmd/bootm.c
new file mode 100644
index 00000000000..2c5aea26d98
--- /dev/null
+++ b/cmd/bootm.c
@@ -0,0 +1,585 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000-2009
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+/*
+ * Boot support
+ */
+#include <bootm.h>
+#include <command.h>
+#include <env.h>
+#include <errno.h>
+#include <image.h>
+#include <malloc.h>
+#include <nand.h>
+#include <asm/byteorder.h>
+#include <asm/global_data.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+#include <u-boot/zlib.h>
+#include <mapmem.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if defined(CONFIG_CMD_IMI)
+static int image_info(unsigned long addr);
+#endif
+
+#if defined(CONFIG_CMD_IMLS)
+#include <flash.h>
+#include <mtd/cfi_flash.h>
+#endif
+
+#if defined(CONFIG_CMD_IMLS) || defined(CONFIG_CMD_IMLS_NAND)
+static int do_imls(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[]);
+#endif
+
+/* we overload the cmd field with our state machine info instead of a
+ * function pointer */
+static struct cmd_tbl cmd_bootm_sub[] = {
+ U_BOOT_CMD_MKENT(start, 0, 1, (void *)BOOTM_STATE_START, "", ""),
+ U_BOOT_CMD_MKENT(loados, 0, 1, (void *)BOOTM_STATE_LOADOS, "", ""),
+#ifdef CONFIG_CMD_BOOTM_PRE_LOAD
+ U_BOOT_CMD_MKENT(preload, 0, 1, (void *)BOOTM_STATE_PRE_LOAD, "", ""),
+#endif
+#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
+ U_BOOT_CMD_MKENT(ramdisk, 0, 1, (void *)BOOTM_STATE_RAMDISK, "", ""),
+#endif
+#ifdef CONFIG_OF_LIBFDT
+ U_BOOT_CMD_MKENT(fdt, 0, 1, (void *)BOOTM_STATE_FDT, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(cmdline, 0, 1, (void *)BOOTM_STATE_OS_CMDLINE, "", ""),
+ U_BOOT_CMD_MKENT(bdt, 0, 1, (void *)BOOTM_STATE_OS_BD_T, "", ""),
+ U_BOOT_CMD_MKENT(prep, 0, 1, (void *)BOOTM_STATE_OS_PREP, "", ""),
+ U_BOOT_CMD_MKENT(fake, 0, 1, (void *)BOOTM_STATE_OS_FAKE_GO, "", ""),
+ U_BOOT_CMD_MKENT(go, 0, 1, (void *)BOOTM_STATE_OS_GO, "", ""),
+};
+
+#if defined(CONFIG_CMD_BOOTM_PRE_LOAD)
+static ulong bootm_get_addr(int argc, char *const argv[])
+{
+ ulong addr;
+
+ if (argc > 0)
+ addr = hextoul(argv[0], NULL);
+ else
+ addr = image_load_addr;
+
+ return addr;
+}
+#endif
+
+static int do_bootm_subcommand(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootm_info bmi;
+ int ret = 0;
+ long state;
+ struct cmd_tbl *c;
+
+ c = find_cmd_tbl(argv[0], &cmd_bootm_sub[0], ARRAY_SIZE(cmd_bootm_sub));
+ argc--; argv++;
+
+ if (c) {
+ state = (long)c->cmd;
+ if (state == BOOTM_STATE_START)
+ state |= BOOTM_STATE_PRE_LOAD | BOOTM_STATE_FINDOS |
+ BOOTM_STATE_FINDOTHER;
+#if defined(CONFIG_CMD_BOOTM_PRE_LOAD)
+ if (state == BOOTM_STATE_PRE_LOAD)
+ state |= BOOTM_STATE_START;
+#endif
+ } else {
+ /* Unrecognized command */
+ return CMD_RET_USAGE;
+ }
+
+ if (((state & BOOTM_STATE_START) != BOOTM_STATE_START) &&
+ images.state >= state) {
+ printf("Trying to execute a command out of order\n");
+ return CMD_RET_USAGE;
+ }
+
+ bootm_init(&bmi);
+ if (argc)
+ bmi.addr_img = argv[0];
+ if (argc > 1)
+ bmi.conf_ramdisk = argv[1];
+ if (argc > 2)
+ bmi.conf_fdt = argv[2];
+ bmi.cmd_name = "bootm";
+ bmi.boot_progress = false;
+
+ /* set up argc and argv[] since some OSes use them */
+ bmi.argc = argc;
+ bmi.argv = argv;
+
+ ret = bootm_run_states(&bmi, state);
+
+#if defined(CONFIG_CMD_BOOTM_PRE_LOAD)
+ if (!ret && (state & BOOTM_STATE_PRE_LOAD))
+ env_set_hex("loadaddr_verified",
+ bootm_get_addr(argc, argv) + image_load_offset);
+#endif
+
+ return ret ? CMD_RET_FAILURE : 0;
+}
+
+/*******************************************************************/
+/* bootm - boot application image from image in memory */
+/*******************************************************************/
+
+int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct bootm_info bmi;
+ int ret;
+
+ /* determine if we have a sub command */
+ argc--; argv++;
+ if (argc > 0) {
+ char *endp;
+
+ hextoul(argv[0], &endp);
+ /* endp pointing to NULL means that argv[0] was just a
+ * valid number, pass it along to the normal bootm processing
+ *
+ * If endp is ':' or '#' assume a FIT identifier so pass
+ * along for normal processing.
+ *
+ * Right now we assume the first arg should never be '-'
+ */
+ if ((*endp != 0) && (*endp != ':') && (*endp != '#'))
+ return do_bootm_subcommand(cmdtp, flag, argc, argv);
+ }
+
+ bootm_init(&bmi);
+ if (argc)
+ bmi.addr_img = argv[0];
+ if (argc > 1)
+ bmi.conf_ramdisk = argv[1];
+ if (argc > 2)
+ bmi.conf_fdt = argv[2];
+
+ /* set up argc and argv[] since some OSes use them */
+ bmi.argc = argc;
+ bmi.argv = argv;
+
+ ret = bootm_run(&bmi);
+
+ return ret ? CMD_RET_FAILURE : 0;
+}
+
+int bootm_maybe_autostart(struct cmd_tbl *cmdtp, const char *cmd)
+{
+ if (env_get_autostart()) {
+ char *local_args[2];
+ local_args[0] = (char *)cmd;
+ local_args[1] = NULL;
+ printf("Automatic boot of image at addr 0x%08lX ...\n",
+ image_load_addr);
+ return do_bootm(cmdtp, 0, 1, local_args);
+ }
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(bootm,
+ "[addr [arg ...]]\n - boot application image stored in memory\n"
+ "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
+ "\t'arg' can be the address of an initrd image\n"
+#if defined(CONFIG_OF_LIBFDT)
+ "\tWhen booting a Linux kernel which requires a flat device-tree\n"
+ "\ta third argument is required which is the address of the\n"
+ "\tdevice-tree blob. To boot that kernel without an initrd image,\n"
+ "\tuse a '-' for the second argument. If you do not pass a third\n"
+ "\ta bd_info struct will be passed instead\n"
+#endif
+#if defined(CONFIG_FIT)
+ "\t\nFor the new multi component uImage format (FIT) addresses\n"
+ "\tmust be extended to include component or configuration unit name:\n"
+ "\taddr:<subimg_uname> - direct component image specification\n"
+ "\taddr#<conf_uname> - configuration specification\n"
+ "\tUse iminfo command to get the list of existing component\n"
+ "\timages and configurations.\n"
+#endif
+ "\nSub-commands to do part of the bootm sequence. The sub-commands "
+ "must be\n"
+ "issued in the order below (it's ok to not issue all sub-commands):\n"
+ "\tstart [addr [arg ...]]\n"
+#if defined(CONFIG_CMD_BOOTM_PRE_LOAD)
+ "\tpreload [addr [arg ..]] - run only the preload stage\n"
+#endif
+ "\tloados - load OS image\n"
+#if defined(CONFIG_SYS_BOOT_RAMDISK_HIGH)
+ "\tramdisk - relocate initrd, set env initrd_start/initrd_end\n"
+#endif
+#if defined(CONFIG_OF_LIBFDT)
+ "\tfdt - relocate flat device tree\n"
+#endif
+ "\tcmdline - OS specific command line processing/setup\n"
+ "\tbdt - OS specific bd_info processing\n"
+ "\tprep - OS specific prep before relocation or go\n"
+#if defined(CONFIG_TRACE)
+ "\tfake - OS specific fake start without go\n"
+#endif
+ "\tgo - start OS");
+
+U_BOOT_CMD(
+ bootm, CONFIG_SYS_MAXARGS, 1, do_bootm,
+ "boot application image from memory", bootm_help_text
+);
+
+/*******************************************************************/
+/* bootd - boot default image */
+/*******************************************************************/
+#if defined(CONFIG_CMD_BOOTD)
+int do_bootd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ return run_command(env_get("bootcmd"), flag);
+}
+
+U_BOOT_CMD(
+ boot, 1, 1, do_bootd,
+ "boot default, i.e., run 'bootcmd'",
+ ""
+);
+
+/* keep old command name "bootd" for backward compatibility */
+U_BOOT_CMD(
+ bootd, 1, 1, do_bootd,
+ "boot default, i.e., run 'bootcmd'",
+ ""
+);
+
+#endif
+
+/*******************************************************************/
+/* iminfo - print header info for a requested image */
+/*******************************************************************/
+#if defined(CONFIG_CMD_IMI)
+static int do_iminfo(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int arg;
+ ulong addr;
+ int rcode = 0;
+
+ if (argc < 2) {
+ return image_info(image_load_addr);
+ }
+
+ for (arg = 1; arg < argc; ++arg) {
+ addr = hextoul(argv[arg], NULL);
+ if (image_info(addr) != 0)
+ rcode = 1;
+ }
+ return rcode;
+}
+
+static int image_info(ulong addr)
+{
+ void *hdr = (void *)map_sysmem(addr, 0);
+
+ printf("\n## Checking Image at %08lx ...\n", addr);
+
+ switch (genimg_get_format(hdr)) {
+#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
+ case IMAGE_FORMAT_LEGACY:
+ puts(" Legacy image found\n");
+ if (!image_check_magic(hdr)) {
+ puts(" Bad Magic Number\n");
+ unmap_sysmem(hdr);
+ return 1;
+ }
+
+ if (!image_check_hcrc(hdr)) {
+ puts(" Bad Header Checksum\n");
+ unmap_sysmem(hdr);
+ return 1;
+ }
+
+ image_print_contents(hdr);
+
+ puts(" Verifying Checksum ... ");
+ if (!image_check_dcrc(hdr)) {
+ puts(" Bad Data CRC\n");
+ unmap_sysmem(hdr);
+ return 1;
+ }
+ puts("OK\n");
+ unmap_sysmem(hdr);
+ return 0;
+#endif
+#if defined(CONFIG_ANDROID_BOOT_IMAGE)
+ case IMAGE_FORMAT_ANDROID:
+ puts(" Android image found\n");
+ android_print_contents(hdr);
+ unmap_sysmem(hdr);
+ return 0;
+#endif
+#if defined(CONFIG_FIT)
+ case IMAGE_FORMAT_FIT:
+ puts(" FIT image found\n");
+
+ if (fit_check_format(hdr, IMAGE_SIZE_INVAL)) {
+ puts("Bad FIT image format!\n");
+ unmap_sysmem(hdr);
+ return 1;
+ }
+
+ fit_print_contents(hdr);
+
+ if (!fit_all_image_verify(hdr)) {
+ puts("Bad hash in FIT image!\n");
+ unmap_sysmem(hdr);
+ return 1;
+ }
+
+ unmap_sysmem(hdr);
+ return 0;
+#endif
+ default:
+ puts("Unknown image format!\n");
+ break;
+ }
+
+ unmap_sysmem(hdr);
+ return 1;
+}
+
+U_BOOT_CMD(
+ iminfo, CONFIG_SYS_MAXARGS, 1, do_iminfo,
+ "print header information for application image",
+ "addr [addr ...]\n"
+ " - print header information for application image starting at\n"
+ " address 'addr' in memory; this includes verification of the\n"
+ " image contents (magic number, header and payload checksums)"
+);
+#endif
+
+/*******************************************************************/
+/* imls - list all images found in flash */
+/*******************************************************************/
+#if defined(CONFIG_CMD_IMLS)
+static int do_imls_nor(void)
+{
+ flash_info_t *info;
+ int i, j;
+ void *hdr;
+
+ for (i = 0, info = &flash_info[0];
+ i < CFI_FLASH_BANKS; ++i, ++info) {
+
+ if (info->flash_id == FLASH_UNKNOWN)
+ goto next_bank;
+ for (j = 0; j < info->sector_count; ++j) {
+
+ hdr = (void *)info->start[j];
+ if (!hdr)
+ goto next_sector;
+
+ switch (genimg_get_format(hdr)) {
+#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
+ case IMAGE_FORMAT_LEGACY:
+ if (!image_check_hcrc(hdr))
+ goto next_sector;
+
+ printf("Legacy Image at %08lX:\n", (ulong)hdr);
+ image_print_contents(hdr);
+
+ puts(" Verifying Checksum ... ");
+ if (!image_check_dcrc(hdr)) {
+ puts("Bad Data CRC\n");
+ } else {
+ puts("OK\n");
+ }
+ break;
+#endif
+#if defined(CONFIG_FIT)
+ case IMAGE_FORMAT_FIT:
+ if (fit_check_format(hdr, IMAGE_SIZE_INVAL))
+ goto next_sector;
+
+ printf("FIT Image at %08lX:\n", (ulong)hdr);
+ fit_print_contents(hdr);
+ break;
+#endif
+ default:
+ goto next_sector;
+ }
+
+next_sector: ;
+ }
+next_bank: ;
+ }
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_CMD_IMLS_NAND)
+static int nand_imls_legacyimage(struct mtd_info *mtd, int nand_dev,
+ loff_t off, size_t len)
+{
+ void *imgdata;
+ int ret;
+
+ imgdata = malloc(len);
+ if (!imgdata) {
+ printf("May be a Legacy Image at NAND device %d offset %08llX:\n",
+ nand_dev, off);
+ printf(" Low memory(cannot allocate memory for image)\n");
+ return -ENOMEM;
+ }
+
+ ret = nand_read_skip_bad(mtd, off, &len, NULL, mtd->size, imgdata);
+ if (ret < 0 && ret != -EUCLEAN) {
+ free(imgdata);
+ return ret;
+ }
+
+ if (!image_check_hcrc(imgdata)) {
+ free(imgdata);
+ return 0;
+ }
+
+ printf("Legacy Image at NAND device %d offset %08llX:\n",
+ nand_dev, off);
+ image_print_contents(imgdata);
+
+ puts(" Verifying Checksum ... ");
+ if (!image_check_dcrc(imgdata))
+ puts("Bad Data CRC\n");
+ else
+ puts("OK\n");
+
+ free(imgdata);
+
+ return 0;
+}
+
+static int nand_imls_fitimage(struct mtd_info *mtd, int nand_dev, loff_t off,
+ size_t len)
+{
+ void *imgdata;
+ int ret;
+
+ imgdata = malloc(len);
+ if (!imgdata) {
+ printf("May be a FIT Image at NAND device %d offset %08llX:\n",
+ nand_dev, off);
+ printf(" Low memory(cannot allocate memory for image)\n");
+ return -ENOMEM;
+ }
+
+ ret = nand_read_skip_bad(mtd, off, &len, NULL, mtd->size, imgdata);
+ if (ret < 0 && ret != -EUCLEAN) {
+ free(imgdata);
+ return ret;
+ }
+
+ if (fit_check_format(imgdata, IMAGE_SIZE_INVAL)) {
+ free(imgdata);
+ return 0;
+ }
+
+ printf("FIT Image at NAND device %d offset %08llX:\n", nand_dev, off);
+
+ fit_print_contents(imgdata);
+ free(imgdata);
+
+ return 0;
+}
+
+static int do_imls_nand(void)
+{
+ struct mtd_info *mtd;
+ int nand_dev = nand_curr_device;
+ size_t len;
+ loff_t off;
+ u32 buffer[16];
+
+ if (nand_dev < 0 || nand_dev >= CONFIG_SYS_MAX_NAND_DEVICE) {
+ puts("\nNo NAND devices available\n");
+ return -ENODEV;
+ }
+
+ printf("\n");
+
+ for (nand_dev = 0; nand_dev < CONFIG_SYS_MAX_NAND_DEVICE; nand_dev++) {
+ mtd = get_nand_dev_by_index(nand_dev);
+ if (!mtd->name || !mtd->size)
+ continue;
+
+ for (off = 0; off < mtd->size; off += mtd->erasesize) {
+ const struct legacy_img_hdr *header;
+ int ret;
+
+ if (nand_block_isbad(mtd, off))
+ continue;
+
+ len = sizeof(buffer);
+
+ ret = nand_read(mtd, off, &len, (u8 *)buffer);
+ if (ret < 0 && ret != -EUCLEAN) {
+ printf("NAND read error %d at offset %08llX\n",
+ ret, off);
+ continue;
+ }
+
+ switch (genimg_get_format(buffer)) {
+#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
+ case IMAGE_FORMAT_LEGACY:
+ header = (const struct legacy_img_hdr *)buffer;
+
+ len = image_get_image_size(header);
+ nand_imls_legacyimage(mtd, nand_dev, off, len);
+ break;
+#endif
+#if defined(CONFIG_FIT)
+ case IMAGE_FORMAT_FIT:
+ len = fit_get_size(buffer);
+ nand_imls_fitimage(mtd, nand_dev, off, len);
+ break;
+#endif
+ }
+ }
+ }
+
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_CMD_IMLS) || defined(CONFIG_CMD_IMLS_NAND)
+static int do_imls(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret_nor = 0, ret_nand = 0;
+
+#if defined(CONFIG_CMD_IMLS)
+ ret_nor = do_imls_nor();
+#endif
+
+#if defined(CONFIG_CMD_IMLS_NAND)
+ ret_nand = do_imls_nand();
+#endif
+
+ if (ret_nor)
+ return ret_nor;
+
+ if (ret_nand)
+ return ret_nand;
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ imls, 1, 1, do_imls,
+ "list all images found in flash",
+ "\n"
+ " - Prints information about all images found at sector/block\n"
+ " boundaries in nor/nand flash."
+);
+#endif
diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
new file mode 100644
index 00000000000..d3108778c6f
--- /dev/null
+++ b/cmd/bootmenu.c
@@ -0,0 +1,717 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2011-2013 Pali Rohár <pali@kernel.org>
+ */
+
+#include <charset.h>
+#include <cli.h>
+#include <command.h>
+#include <ansi.h>
+#include <efi_config.h>
+#include <efi_variable.h>
+#include <env.h>
+#include <log.h>
+#include <menu.h>
+#include <watchdog.h>
+#include <malloc.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+
+/* maximum bootmenu entries */
+#define MAX_COUNT 99
+
+/* maximal size of bootmenu env
+ * 9 = strlen("bootmenu_")
+ * 2 = strlen(MAX_COUNT)
+ * 1 = NULL term
+ */
+#define MAX_ENV_SIZE (9 + 2 + 1)
+
+enum bootmenu_ret {
+ BOOTMENU_RET_SUCCESS = 0,
+ BOOTMENU_RET_FAIL,
+ BOOTMENU_RET_QUIT,
+ BOOTMENU_RET_UPDATED,
+};
+
+enum boot_type {
+ BOOTMENU_TYPE_NONE = 0,
+ BOOTMENU_TYPE_BOOTMENU,
+ BOOTMENU_TYPE_UEFI_BOOT_OPTION,
+};
+
+struct bootmenu_entry {
+ unsigned short int num; /* unique number 0 .. MAX_COUNT */
+ char key[3]; /* key identifier of number */
+ char *title; /* title of entry */
+ char *command; /* hush command of entry */
+ enum boot_type type; /* boot type of entry */
+ u16 bootorder; /* order for each boot type */
+ struct bootmenu_data *menu; /* this bootmenu */
+ struct bootmenu_entry *next; /* next menu entry (num+1) */
+};
+
+static char *bootmenu_getoption(unsigned short int n)
+{
+ char name[MAX_ENV_SIZE];
+
+ if (n > MAX_COUNT)
+ return NULL;
+
+ sprintf(name, "bootmenu_%d", n);
+ return env_get(name);
+}
+
+static void bootmenu_print_entry(void *data)
+{
+ struct bootmenu_entry *entry = data;
+ int reverse = (entry->menu->active == entry->num);
+
+ /*
+ * Move cursor to line where the entry will be drown (entry->num)
+ * First 3 lines contain bootmenu header + 1 empty line
+ */
+ printf(ANSI_CURSOR_POSITION, entry->num + 4, 7);
+
+ if (reverse)
+ puts(ANSI_COLOR_REVERSE);
+
+ printf("%s", entry->title);
+
+ if (reverse)
+ puts(ANSI_COLOR_RESET);
+}
+
+static char *bootmenu_choice_entry(void *data)
+{
+ struct cli_ch_state s_cch, *cch = &s_cch;
+ struct bootmenu_data *menu = data;
+ struct bootmenu_entry *iter;
+ enum bootmenu_key key = BKEY_NONE;
+ int i;
+
+ cli_ch_init(cch);
+
+ while (1) {
+ if (menu->delay >= 0) {
+ /* Autoboot was not stopped */
+ key = bootmenu_autoboot_loop(menu, cch);
+ } else {
+ /* Some key was pressed, so autoboot was stopped */
+ key = bootmenu_loop(menu, cch);
+ }
+
+ switch (key) {
+ case BKEY_UP:
+ menu->last_active = menu->active;
+ if (menu->active > 0)
+ --menu->active;
+ /* no menu key selected, regenerate menu */
+ return NULL;
+ case BKEY_DOWN:
+ menu->last_active = menu->active;
+ if (menu->active < menu->count - 1)
+ ++menu->active;
+ /* no menu key selected, regenerate menu */
+ return NULL;
+ case BKEY_SHORTCUT:
+ /* invalid shortcut, regenerate menu */
+ if (cch->shortcut_key >= menu->count - 1)
+ return NULL;
+ /* shortcut_key value for Exit is is -1 */
+ menu->active = cch->shortcut_key < 0 ? menu->count - 1 :
+ cch->shortcut_key;
+ fallthrough;
+ case BKEY_SELECT:
+ iter = menu->first;
+ for (i = 0; i < menu->active; ++i)
+ iter = iter->next;
+ return iter->key;
+ case BKEY_QUIT:
+ /* Quit by choosing the last entry */
+ iter = menu->first;
+ while (iter->next)
+ iter = iter->next;
+ return iter->key;
+ default:
+ break;
+ }
+ }
+
+ /* never happens */
+ debug("bootmenu: this should not happen");
+ return NULL;
+}
+
+static bool bootmenu_need_reprint(void *data)
+{
+ struct bootmenu_data *menu = data;
+ bool need_reprint;
+
+ need_reprint = menu->last_active != menu->active;
+ menu->last_active = menu->active;
+
+ return need_reprint;
+}
+
+static void bootmenu_destroy(struct bootmenu_data *menu)
+{
+ struct bootmenu_entry *iter = menu->first;
+ struct bootmenu_entry *next;
+
+ while (iter) {
+ next = iter->next;
+ free(iter->title);
+ free(iter->command);
+ free(iter);
+ iter = next;
+ }
+ free(menu);
+}
+
+static char bootmenu_entry_shortcut_key(int index)
+{
+ switch (index) {
+ /* 1-9 shortcut key (0 reserved) */
+ case 0 ... 8:
+ return '1' + index;
+ /* a-z shortcut key */
+ case 9 ... 34:
+ return 'a' + index - 9;
+ /* We support shortcut for up to 34 options (0 reserved) */
+ default:
+ return -ENOENT;
+ }
+}
+
+/**
+ * prepare_bootmenu_entry() - generate the bootmenu_xx entries
+ *
+ * This function read the "bootmenu_x" U-Boot environment variable
+ * and generate the bootmenu entries.
+ *
+ * @menu: pointer to the bootmenu structure
+ * @current: pointer to the last bootmenu entry list
+ * @index: pointer to the index of the last bootmenu entry,
+ * the number of bootmenu entry is added by this function
+ * Return: 1 on success, negative value on error
+ */
+static int prepare_bootmenu_entry(struct bootmenu_data *menu,
+ struct bootmenu_entry **current,
+ unsigned short int *index)
+{
+ char *sep;
+ const char *option;
+ unsigned short int i = *index;
+ struct bootmenu_entry *entry = NULL;
+ struct bootmenu_entry *iter = *current;
+
+ while ((option = bootmenu_getoption(i))) {
+ char shortcut_key;
+ int len;
+
+ /* bootmenu_[num] format is "[title]=[commands]" */
+ sep = strchr(option, '=');
+ if (!sep) {
+ printf("Invalid bootmenu entry: %s\n", option);
+ break;
+ }
+
+ entry = malloc(sizeof(struct bootmenu_entry));
+ if (!entry)
+ return -ENOMEM;
+
+ /* Add shotcut key option: %c. %s\0 */
+ len = sep - option + 4;
+
+ entry->title = malloc(len);
+ if (!entry->title) {
+ free(entry);
+ return -ENOMEM;
+ }
+
+ shortcut_key = bootmenu_entry_shortcut_key(i);
+ /* Use emtpy space if entry doesn't support shortcut key */
+ snprintf(entry->title, len, "%c%c %s",
+ shortcut_key > 0 ? shortcut_key : ' ',
+ shortcut_key > 0 ? '.' : ' ',
+ option);
+
+ entry->command = strdup(sep + 1);
+ if (!entry->command) {
+ free(entry->title);
+ free(entry);
+ return -ENOMEM;
+ }
+
+ sprintf(entry->key, "%d", i);
+
+ entry->num = i;
+ entry->menu = menu;
+ entry->type = BOOTMENU_TYPE_BOOTMENU;
+ entry->bootorder = i;
+ entry->next = NULL;
+
+ if (!iter)
+ menu->first = entry;
+ else
+ iter->next = entry;
+
+ iter = entry;
+ ++i;
+
+ if (i == MAX_COUNT - 1)
+ break;
+ }
+
+ *index = i;
+ *current = iter;
+
+ return 1;
+}
+
+#if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) && (IS_ENABLED(CONFIG_CMD_EFICONFIG))
+/**
+ * prepare_uefi_bootorder_entry() - generate the uefi bootmenu entries
+ *
+ * This function reads the "BootOrder" UEFI variable
+ * and generate the bootmenu entries in the order of "BootOrder".
+ *
+ * @menu: pointer to the bootmenu structure
+ * @current: pointer to the last bootmenu entry list
+ * @index: pointer to the index of the last bootmenu entry,
+ * the number of uefi entry is added by this function
+ * Return: 1 on success, negative value on error
+ */
+static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
+ struct bootmenu_entry **current,
+ unsigned short int *index)
+{
+ u16 *bootorder;
+ efi_status_t ret;
+ unsigned short j;
+ efi_uintn_t num, size;
+ void *load_option;
+ struct efi_load_option lo;
+ u16 varname[] = u"Boot####";
+ unsigned short int i = *index;
+ struct bootmenu_entry *entry = NULL;
+ struct bootmenu_entry *iter = *current;
+
+ bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
+ if (!bootorder)
+ return -ENOENT;
+
+ num = size / sizeof(u16);
+ for (j = 0; j < num; j++) {
+ entry = malloc(sizeof(struct bootmenu_entry));
+ if (!entry)
+ return -ENOMEM;
+
+ efi_create_indexed_name(varname, sizeof(varname),
+ "Boot", bootorder[j]);
+ load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
+ if (!load_option)
+ continue;
+
+ ret = efi_deserialize_load_option(&lo, load_option, &size);
+ if (ret != EFI_SUCCESS) {
+ log_warning("Invalid load option for %ls\n", varname);
+ free(load_option);
+ free(entry);
+ continue;
+ }
+
+ if (lo.attributes & LOAD_OPTION_ACTIVE) {
+ char *buf;
+
+ buf = calloc(1, utf16_utf8_strlen(lo.label) + 1);
+ if (!buf) {
+ free(load_option);
+ free(entry);
+ free(bootorder);
+ return -ENOMEM;
+ }
+ entry->title = buf;
+ utf16_utf8_strncpy(&buf, lo.label, u16_strlen(lo.label));
+ entry->command = strdup("bootefi bootmgr");
+ sprintf(entry->key, "%d", i);
+ entry->num = i;
+ entry->menu = menu;
+ entry->type = BOOTMENU_TYPE_UEFI_BOOT_OPTION;
+ entry->bootorder = bootorder[j];
+ entry->next = NULL;
+
+ if (!iter)
+ menu->first = entry;
+ else
+ iter->next = entry;
+
+ iter = entry;
+ i++;
+ }
+
+ free(load_option);
+
+ if (i == MAX_COUNT - 1)
+ break;
+ }
+
+ free(bootorder);
+ *index = i;
+ *current = iter;
+
+ return 1;
+}
+#endif
+
+/**
+ * bootmenu_create() - create boot menu entries
+ *
+ * @uefi: consider UEFI boot options
+ * @delay: autostart delay in seconds
+ */
+static struct bootmenu_data *bootmenu_create(int uefi, int delay)
+{
+ int ret;
+ unsigned short int i = 0;
+ struct bootmenu_data *menu;
+ struct bootmenu_entry *iter = NULL;
+ struct bootmenu_entry *entry;
+ char *default_str;
+
+ menu = malloc(sizeof(struct bootmenu_data));
+ if (!menu)
+ return NULL;
+
+ menu->delay = delay;
+ menu->active = 0;
+ menu->last_active = -1;
+ menu->first = NULL;
+
+ default_str = env_get("bootmenu_default");
+ if (default_str)
+ menu->active = (int)simple_strtol(default_str, NULL, 10);
+
+ ret = prepare_bootmenu_entry(menu, &iter, &i);
+ if (ret < 0)
+ goto cleanup;
+
+#if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) && (IS_ENABLED(CONFIG_CMD_EFICONFIG))
+ if (uefi && i < MAX_COUNT - 1) {
+ efi_status_t efi_ret;
+
+ /*
+ * UEFI specification requires booting from removal media using
+ * a architecture-specific default image name such as BOOTAA64.EFI.
+ */
+ efi_ret = efi_bootmgr_update_media_device_boot_option();
+ if (efi_ret != EFI_SUCCESS)
+ goto cleanup;
+
+ ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
+ if (ret < 0 && ret != -ENOENT)
+ goto cleanup;
+ }
+#endif
+
+ /* Add Exit entry at the end */
+ if (i <= MAX_COUNT - 1) {
+ entry = malloc(sizeof(struct bootmenu_entry));
+ if (!entry)
+ goto cleanup;
+
+ /* Add Quit entry if exiting bootmenu is disabled */
+ if (!IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE))
+ entry->title = strdup("0. Exit");
+ else
+ entry->title = strdup("0. Quit");
+
+ if (!entry->title) {
+ free(entry);
+ goto cleanup;
+ }
+
+ entry->command = strdup("");
+ if (!entry->command) {
+ free(entry->title);
+ free(entry);
+ goto cleanup;
+ }
+
+ sprintf(entry->key, "%d", i);
+
+ entry->num = i;
+ entry->menu = menu;
+ entry->type = BOOTMENU_TYPE_NONE;
+ entry->next = NULL;
+
+ if (!iter)
+ menu->first = entry;
+ else
+ iter->next = entry;
+
+ iter = entry;
+ ++i;
+ }
+
+ menu->count = i;
+
+ if ((menu->active >= menu->count)||(menu->active < 0)) { //ensure active menuitem is inside menu
+ printf("active menuitem (%d) is outside menu (0..%d)\n",menu->active,menu->count-1);
+ menu->active=0;
+ }
+
+ return menu;
+
+cleanup:
+ bootmenu_destroy(menu);
+ return NULL;
+}
+
+static void menu_display_statusline(struct menu *m)
+{
+ struct bootmenu_entry *entry;
+ struct bootmenu_data *menu;
+
+ if (menu_default_choice(m, (void *)&entry) < 0)
+ return;
+
+ menu = entry->menu;
+
+ printf(ANSI_CURSOR_POSITION, 1, 1);
+ puts(ANSI_CLEAR_LINE);
+ printf(ANSI_CURSOR_POSITION, 2, 3);
+ puts("*** U-Boot Boot Menu ***");
+ puts(ANSI_CLEAR_LINE_TO_END);
+ printf(ANSI_CURSOR_POSITION, 3, 1);
+ puts(ANSI_CLEAR_LINE);
+
+ /* First 3 lines are bootmenu header + 2 empty lines between entries */
+ printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
+ puts(ANSI_CLEAR_LINE);
+ printf(ANSI_CURSOR_POSITION, menu->count + 6, 3);
+ puts("Press UP/DOWN to move, ENTER to select, ESC to quit");
+ puts(ANSI_CLEAR_LINE_TO_END);
+ printf(ANSI_CURSOR_POSITION, menu->count + 7, 1);
+ puts(ANSI_CLEAR_LINE);
+}
+
+static void handle_uefi_bootnext(void)
+{
+ u16 bootnext;
+ efi_status_t ret;
+ efi_uintn_t size;
+
+ /* Initialize EFI drivers */
+ ret = efi_init_obj_list();
+ if (ret != EFI_SUCCESS) {
+ log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
+ ret & ~EFI_ERROR_MASK);
+
+ return;
+ }
+
+ /* If UEFI BootNext variable is set, boot the BootNext load option */
+ size = sizeof(u16);
+ ret = efi_get_variable_int(u"BootNext",
+ &efi_global_variable_guid,
+ NULL, &size, &bootnext, NULL);
+ if (ret == EFI_SUCCESS)
+ /* BootNext does exist here, try to boot */
+ run_command("bootefi bootmgr", 0);
+}
+
+/**
+ * bootmenu_show - display boot menu
+ *
+ * @uefi: generated entries for UEFI boot options
+ * @delay: autoboot delay in seconds
+ */
+static enum bootmenu_ret bootmenu_show(int uefi, int delay)
+{
+ int cmd_ret;
+ int init = 0;
+ void *choice = NULL;
+ char *title = NULL;
+ char *command = NULL;
+ struct menu *menu;
+ struct bootmenu_entry *iter;
+ int ret = BOOTMENU_RET_SUCCESS;
+ struct bootmenu_data *bootmenu;
+ efi_status_t efi_ret = EFI_SUCCESS;
+ char *option, *sep;
+
+ if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && uefi)
+ handle_uefi_bootnext();
+
+ /* If delay is 0 do not create menu, just run first entry */
+ if (delay == 0) {
+ option = bootmenu_getoption(0);
+ if (!option) {
+ puts("bootmenu option 0 was not found\n");
+ return BOOTMENU_RET_FAIL;
+ }
+ sep = strchr(option, '=');
+ if (!sep) {
+ puts("bootmenu option 0 is invalid\n");
+ return BOOTMENU_RET_FAIL;
+ }
+ cmd_ret = run_command(sep + 1, 0);
+ return (cmd_ret == CMD_RET_SUCCESS ? BOOTMENU_RET_SUCCESS : BOOTMENU_RET_FAIL);
+ }
+
+ bootmenu = bootmenu_create(uefi, delay);
+ if (!bootmenu)
+ return BOOTMENU_RET_FAIL;
+
+ menu = menu_create(NULL, bootmenu->delay, 1, menu_display_statusline,
+ bootmenu_print_entry, bootmenu_choice_entry,
+ bootmenu_need_reprint, bootmenu);
+ if (!menu) {
+ bootmenu_destroy(bootmenu);
+ return BOOTMENU_RET_FAIL;
+ }
+
+ for (iter = bootmenu->first; iter; iter = iter->next) {
+ if (menu_item_add(menu, iter->key, iter) != 1)
+ goto cleanup;
+ }
+
+ /* Default menu entry is always first */
+ menu_default_set(menu, "0");
+
+ puts(ANSI_CURSOR_HIDE);
+ puts(ANSI_CLEAR_CONSOLE);
+ printf(ANSI_CURSOR_POSITION, 1, 1);
+
+ init = 1;
+
+ if (menu_get_choice(menu, &choice) == 1) {
+ iter = choice;
+ title = strdup(iter->title);
+ command = strdup(iter->command);
+
+ /* last entry exits bootmenu */
+ if (iter->num == iter->menu->count - 1) {
+ ret = BOOTMENU_RET_QUIT;
+ goto cleanup;
+ }
+ } else {
+ goto cleanup;
+ }
+
+ /*
+ * If the selected entry is UEFI BOOT####, set the BootNext variable.
+ * Then uefi bootmgr is invoked by the preset command in iter->command.
+ */
+ if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
+ if (iter->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION) {
+ /*
+ * UEFI specification requires BootNext variable needs non-volatile
+ * attribute, but this BootNext is only used inside of U-Boot and
+ * removed by efi bootmgr once BootNext is processed.
+ * So this BootNext can be volatile.
+ */
+ efi_ret = efi_set_variable_int(u"BootNext", &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(u16), &iter->bootorder, false);
+ if (efi_ret != EFI_SUCCESS)
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ menu_destroy(menu);
+ bootmenu_destroy(bootmenu);
+
+ if (init) {
+ puts(ANSI_CURSOR_SHOW);
+ puts(ANSI_CLEAR_CONSOLE);
+ printf(ANSI_CURSOR_POSITION, 1, 1);
+ }
+
+ if (title && command) {
+ debug("Starting entry '%s'\n", title);
+ free(title);
+ if (efi_ret == EFI_SUCCESS)
+ cmd_ret = run_command(command, 0);
+ free(command);
+ }
+
+#ifdef CFG_POSTBOOTMENU
+ run_command(CFG_POSTBOOTMENU, 0);
+#endif
+
+ if (efi_ret != EFI_SUCCESS || cmd_ret != CMD_RET_SUCCESS)
+ ret = BOOTMENU_RET_FAIL;
+
+ return ret;
+}
+
+#ifdef CONFIG_AUTOBOOT_MENU_SHOW
+int menu_show(int bootdelay)
+{
+ int ret;
+
+ while (1) {
+ ret = bootmenu_show(1, bootdelay);
+ bootdelay = -1;
+ if (ret == BOOTMENU_RET_UPDATED)
+ continue;
+
+ if (IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE)) {
+ if (ret == BOOTMENU_RET_QUIT) {
+ /* default boot process */
+ if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
+ run_command("bootefi bootmgr", 0);
+
+ run_command("run bootcmd", 0);
+ }
+ } else {
+ break;
+ }
+ }
+
+ return -1; /* -1 - abort boot and run monitor code */
+}
+#endif
+
+int do_bootmenu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char *delay_str = NULL;
+ int delay = 10;
+ int uefi = 0;
+
+#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
+ delay = CONFIG_BOOTDELAY;
+#endif
+
+ if (argc >= 2) {
+ if (!strcmp("-e", argv[1])) {
+ uefi = 1;
+ --argc;
+ ++argv;
+ }
+ }
+ if (argc >= 2)
+ delay_str = argv[1];
+
+ if (!delay_str)
+ delay_str = env_get("bootmenu_delay");
+
+ if (delay_str)
+ delay = (int)simple_strtol(delay_str, NULL, 10);
+
+ bootmenu_show(uefi, delay);
+ return 0;
+}
+
+U_BOOT_CMD(
+ bootmenu, 2, 1, do_bootmenu,
+ "ANSI terminal bootmenu",
+ "[-e] [delay]\n"
+ "-e - show UEFI entries\n"
+ "delay - show ANSI terminal bootmenu with autoboot delay"
+);
diff --git a/cmd/bootmeth.c b/cmd/bootmeth.c
new file mode 100644
index 00000000000..ea4b3f47db8
--- /dev/null
+++ b/cmd/bootmeth.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 'bootmeth' command
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <bootdev.h>
+#include <bootmeth.h>
+#include <bootstd.h>
+#include <command.h>
+#include <dm.h>
+#include <env.h>
+#include <malloc.h>
+#include <dm/uclass-internal.h>
+
+static int do_bootmeth_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *std;
+ struct udevice *dev;
+ bool use_order;
+ bool all = false;
+ int ret;
+ int i;
+
+ if (argc > 1 && *argv[1] == '-') {
+ all = strchr(argv[1], 'a');
+ argc--;
+ argv++;
+ }
+
+ ret = bootstd_get_priv(&std);
+ if (ret) {
+ printf("Cannot get bootstd (err=%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Order Seq Name Description\n");
+ printf("----- --- ------------------ ------------------\n");
+
+ /*
+ * Use the ordering if we have one, so long as we are not trying to list
+ * all bootmethds
+ */
+ use_order = std->bootmeth_count && !all;
+ if (use_order)
+ dev = std->bootmeth_order[0];
+ else
+ ret = uclass_find_first_device(UCLASS_BOOTMETH, &dev);
+
+ for (i = 0; dev;) {
+ struct bootmeth_uc_plat *ucp = dev_get_uclass_plat(dev);
+ int order = i;
+
+ /*
+ * With the -a flag we may list bootdevs that are not in the
+ * ordering. Find their place in the order
+ */
+ if (all && std->bootmeth_count) {
+ int j;
+
+ /* Find the position of this bootmeth in the order */
+ order = -1;
+ for (j = 0; j < std->bootmeth_count; j++) {
+ if (std->bootmeth_order[j] == dev)
+ order = j;
+ }
+ }
+
+ if (ucp->flags & BOOTMETHF_GLOBAL)
+ printf("%5s", "glob");
+ else if (order == -1)
+ printf("%5s", "-");
+ else
+ printf("%5x", order);
+ printf(" %3x %-19.19s %s\n", dev_seq(dev), dev->name,
+ ucp->desc);
+ i++;
+ if (use_order)
+ dev = std->bootmeth_order[i];
+ else
+ uclass_find_next_device(&dev);
+ }
+ printf("----- --- ------------------ ------------------\n");
+ printf("(%d bootmeth%s)\n", i, i != 1 ? "s" : "");
+
+ return 0;
+}
+
+static int do_bootmeth_order(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+
+ ret = bootmeth_set_order(argv[1]);
+ if (ret) {
+ printf("Failed (err=%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ env_set("bootmeths", argv[1]);
+
+ return 0;
+}
+
+static int do_bootmeth_set(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+
+ if (argc < 4) {
+ printf("Required parameters not provided\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = bootmeth_set_property(argv[1], argv[2], argv[3]);
+ if (ret) {
+ printf("Failed (err=%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(bootmeth,
+ "list [-a] - list available bootmeths (-a all)\n"
+ "bootmeth order [<bd> ...] - select bootmeth order / subset to use\n"
+ "bootmeth set <bootmeth> <property> <value> - set optional property");
+
+U_BOOT_CMD_WITH_SUBCMDS(bootmeth, "Boot methods", bootmeth_help_text,
+ U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootmeth_list),
+ U_BOOT_SUBCMD_MKENT(order, CONFIG_SYS_MAXARGS, 1, do_bootmeth_order),
+ U_BOOT_SUBCMD_MKENT(set, CONFIG_SYS_MAXARGS, 1, do_bootmeth_set));
diff --git a/cmd/bootstage.c b/cmd/bootstage.c
new file mode 100644
index 00000000000..5c6d5a3ab45
--- /dev/null
+++ b/cmd/bootstage.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2012, Google Inc. All rights reserved.
+ */
+
+#include <bootstage.h>
+#include <command.h>
+#include <vsprintf.h>
+#include <linux/string.h>
+
+static int do_bootstage_report(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bootstage_report();
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_BOOTSTAGE_STASH)
+static int get_base_size(int argc, char *const argv[], ulong *basep,
+ ulong *sizep)
+{
+ char *endp;
+
+ *basep = CONFIG_BOOTSTAGE_STASH_ADDR;
+ *sizep = CONFIG_BOOTSTAGE_STASH_SIZE;
+ if (argc < 2)
+ return 0;
+ *basep = hextoul(argv[1], &endp);
+ if (*argv[1] == 0 || *endp != 0)
+ return -1;
+ if (argc == 2)
+ return 0;
+ *sizep = hextoul(argv[2], &endp);
+ if (*argv[2] == 0 || *endp != 0)
+ return -1;
+
+ return 0;
+}
+
+static int do_bootstage_stash(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong base, size;
+ int ret;
+
+ if (get_base_size(argc, argv, &base, &size))
+ return CMD_RET_USAGE;
+ if (base == -1UL) {
+ printf("No bootstage stash area defined\n");
+ return 1;
+ }
+
+ if (0 == strcmp(argv[0], "stash"))
+ ret = bootstage_stash((void *)base, size);
+ else
+ ret = bootstage_unstash((void *)base, size);
+ if (ret)
+ return 1;
+
+ return 0;
+}
+#endif
+
+static struct cmd_tbl cmd_bootstage_sub[] = {
+ U_BOOT_CMD_MKENT(report, 2, 1, do_bootstage_report, "", ""),
+#if IS_ENABLED(CONFIG_BOOTSTAGE_STASH)
+ U_BOOT_CMD_MKENT(stash, 4, 0, do_bootstage_stash, "", ""),
+ U_BOOT_CMD_MKENT(unstash, 4, 0, do_bootstage_stash, "", ""),
+#endif
+};
+
+/*
+ * Process a bootstage sub-command
+ */
+static int do_boostage(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *c;
+
+ /* Strip off leading 'bootstage' command argument */
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], cmd_bootstage_sub,
+ ARRAY_SIZE(cmd_bootstage_sub));
+
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+ else
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(bootstage, 4, 1, do_boostage,
+ "Boot stage command",
+ " - check boot progress and timing\n"
+ "report - Print a report\n"
+#if IS_ENABLED(CONFIG_BOOTSTAGE_STASH)
+ "stash [<start> [<size>]] - Stash data into memory\n"
+ "unstash [<start> [<size>]] - Unstash data from memory\n"
+#endif
+);
diff --git a/cmd/bootstd.c b/cmd/bootstd.c
new file mode 100644
index 00000000000..e1d82744eb5
--- /dev/null
+++ b/cmd/bootstd.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 'bootstd' command
+ *
+ * Copyright 2024 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <bootstd.h>
+#include <command.h>
+#include <dm.h>
+#include <malloc.h>
+#include <dm/uclass-internal.h>
+
+static int do_bootstd_images(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const struct bootflow *bflow;
+ struct bootstd_priv *std;
+ int ret, i;
+
+ ret = bootstd_get_priv(&std);
+ if (ret) {
+ printf("Cannot get bootstd (err=%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Seq Bootflow Type At Size Filename\n");
+ printf("--- ------------------- -------------- -------- -------- ----------------\n");
+
+ /*
+ * Use the ordering if we have one, so long as we are not trying to list
+ * all bootmethds
+ */
+ i = 0;
+ alist_for_each(bflow, &std->bootflows) {
+ const struct bootflow_img *img;
+
+ alist_for_each(img, &bflow->images) {
+ printf("%3d %-20.20s %-15.15s ",
+ bootflow_get_seq(bflow), bflow->name,
+ bootflow_img_type_name(img->type));
+ if (img->addr)
+ printf("%8lx", img->addr);
+ else
+ printf("%8s", "-");
+ printf(" %8lx %s\n", img->size, img->fname);
+ i++;
+ }
+ }
+
+ printf("--- ------------------- -------------- -------- -------- ----------------\n");
+ printf("(%d image%s)\n", i, i != 1 ? "s" : "");
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(bootstd,
+ "images - list loaded images");
+
+U_BOOT_CMD_WITH_SUBCMDS(bootstd, "Standard-boot operation", bootstd_help_text,
+ U_BOOT_SUBCMD_MKENT(images, 1, 1, do_bootstd_images));
diff --git a/cmd/bootz.c b/cmd/bootz.c
new file mode 100644
index 00000000000..44af7d012aa
--- /dev/null
+++ b/cmd/bootz.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000-2009
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <bootm.h>
+#include <command.h>
+#include <image.h>
+#include <irq_func.h>
+#include <lmb.h>
+#include <log.h>
+#include <linux/compiler.h>
+
+int __weak bootz_setup(ulong image, ulong *start, ulong *end)
+{
+ /* Please define bootz_setup() for your platform */
+
+ puts("Your platform's zImage format isn't supported yet!\n");
+ return -1;
+}
+
+/*
+ * zImage booting support
+ */
+static int bootz_start(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[], struct bootm_headers *images)
+{
+ ulong zi_start, zi_end;
+ struct bootm_info bmi;
+ phys_addr_t ep_addr;
+ int ret;
+
+ bootm_init(&bmi);
+ if (argc)
+ bmi.addr_img = argv[0];
+ if (argc > 1)
+ bmi.conf_ramdisk = argv[1];
+ if (argc > 2)
+ bmi.conf_fdt = argv[2];
+ /* do not set up argc and argv[] since nothing uses them */
+
+ ret = bootm_run_states(&bmi, BOOTM_STATE_START);
+
+ /* Setup Linux kernel zImage entry point */
+ if (!argc) {
+ images->ep = image_load_addr;
+ debug("* kernel: default image load address = 0x%08lx\n",
+ image_load_addr);
+ } else {
+ images->ep = hextoul(argv[0], NULL);
+ debug("* kernel: cmdline image address = 0x%08lx\n",
+ images->ep);
+ }
+
+ ret = bootz_setup(images->ep, &zi_start, &zi_end);
+ if (ret != 0)
+ return 1;
+
+ ep_addr = (phys_addr_t)images->ep;
+ ret = lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &ep_addr, zi_end - zi_start,
+ LMB_NONE);
+ if (ret) {
+ printf("Failed to allocate memory for the image at %#llx\n",
+ (unsigned long long)images->ep);
+ return 1;
+ }
+
+ /*
+ * Handle the BOOTM_STATE_FINDOTHER state ourselves as we do not
+ * have a header that provide this informaiton.
+ */
+ if (bootm_find_images(image_load_addr, cmd_arg1(argc, argv),
+ cmd_arg2(argc, argv), images->ep,
+ zi_end - zi_start))
+ return 1;
+
+ return 0;
+}
+
+int do_bootz(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct bootm_info bmi;
+ int ret;
+
+ /* Consume 'bootz' */
+ argc--; argv++;
+
+ if (bootz_start(cmdtp, flag, argc, argv, &images))
+ return 1;
+
+ /*
+ * We are doing the BOOTM_STATE_LOADOS state ourselves, so must
+ * disable interrupts ourselves
+ */
+ bootm_disable_interrupts();
+
+ images.os.os = IH_OS_LINUX;
+
+ bootm_init(&bmi);
+ if (argc)
+ bmi.addr_img = argv[0];
+ if (argc > 1)
+ bmi.conf_ramdisk = argv[1];
+ if (argc > 2)
+ bmi.conf_fdt = argv[2];
+ bmi.cmd_name = "bootz";
+
+ ret = bootz_run(&bmi);
+
+ return ret;
+}
+
+U_BOOT_LONGHELP(bootz,
+ "[addr [initrd[:size]] [fdt]]\n"
+ " - boot Linux zImage stored in memory\n"
+ "\tThe argument 'initrd' is optional and specifies the address\n"
+ "\tof the initrd in memory. The optional argument ':size' allows\n"
+ "\tspecifying the size of RAW initrd.\n"
+#if defined(CONFIG_OF_LIBFDT)
+ "\tWhen booting a Linux kernel which requires a flat device-tree\n"
+ "\ta third argument is required which is the address of the\n"
+ "\tdevice-tree blob. To boot that kernel without an initrd image,\n"
+ "\tuse a '-' for the second argument. If you do not pass a third\n"
+ "\ta bd_info struct will be passed instead\n"
+#endif
+ );
+
+U_BOOT_CMD(
+ bootz, CONFIG_SYS_MAXARGS, 1, do_bootz,
+ "boot Linux zImage image from memory", bootz_help_text
+);
diff --git a/cmd/broadcom/Makefile b/cmd/broadcom/Makefile
new file mode 100644
index 00000000000..62268d98d0d
--- /dev/null
+++ b/cmd/broadcom/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2020 Broadcom
+
+obj-y += chimp_boot.o
+obj-y += nitro_image_load.o
+obj-y += chimp_handshake.o
diff --git a/cmd/broadcom/chimp_boot.c b/cmd/broadcom/chimp_boot.c
new file mode 100644
index 00000000000..ae0a81179d0
--- /dev/null
+++ b/cmd/broadcom/chimp_boot.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 Broadcom
+ */
+
+#include <command.h>
+#include <broadcom/chimp.h>
+
+static int do_chimp_fastboot_secure(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 health = 0;
+
+ if (chimp_health_status_optee(&health)) {
+ pr_err("Chimp health command fail\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (health == BCM_CHIMP_RUNNIG_GOOD) {
+ printf("skip fastboot...\n");
+ return CMD_RET_SUCCESS;
+ }
+
+ if (chimp_fastboot_optee()) {
+ pr_err("Failed to load secure ChiMP image\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD
+ (chimp_ld_secure, 1, 0, do_chimp_fastboot_secure,
+ "Invoke chimp fw load via optee",
+ "chimp_ld_secure\n"
+);
diff --git a/cmd/broadcom/chimp_handshake.c b/cmd/broadcom/chimp_handshake.c
new file mode 100644
index 00000000000..e2742671963
--- /dev/null
+++ b/cmd/broadcom/chimp_handshake.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 Broadcom
+ */
+
+#include <command.h>
+#include <broadcom/chimp.h>
+
+/* This command should be called after loading the nitro binaries */
+static int do_chimp_hs(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret = CMD_RET_USAGE;
+ u32 hstatus;
+
+ /* Returns 1, if handshake call is success */
+ if (chimp_handshake_status_optee(0, &hstatus))
+ ret = CMD_RET_SUCCESS;
+
+ if (hstatus == CHIMP_HANDSHAKE_SUCCESS)
+ printf("ChiMP Handshake successful\n");
+ else
+ printf("ERROR: ChiMP Handshake status 0x%x\n", hstatus);
+
+ return ret;
+}
+
+U_BOOT_CMD
+ (chimp_hs, 1, 1, do_chimp_hs,
+ "Verify the Chimp handshake",
+ "chimp_hs\n"
+);
diff --git a/cmd/broadcom/nitro_image_load.c b/cmd/broadcom/nitro_image_load.c
new file mode 100644
index 00000000000..fe08679840e
--- /dev/null
+++ b/cmd/broadcom/nitro_image_load.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 Broadcom
+ */
+
+#include <command.h>
+#include <env.h>
+#include <vsprintf.h>
+
+#define FW_IMAGE_SIG 0xff123456
+#define CFG_IMAGE_SIG 0xcf54321a
+
+/*
+ * structure for bin file
+ * signature: fw itb file
+ * size: fw itb file
+ * signature: NS3 config file
+ * size: NS3 config file
+ * Data: fw itb file
+ * ............................
+ * ............................
+ * Data: NS3 config file
+ * ............................
+ * ............................
+ */
+
+static struct img_header {
+ u32 bin_sig;
+ u32 bin_size;
+ u32 cfg1_sig;
+ u32 cfg1_size;
+} *img_header;
+
+static int env_set_val(const char *varname, ulong val)
+{
+ int ret;
+
+ ret = env_set_hex(varname, val);
+ if (ret)
+ pr_err("Failed to %s env var\n", varname);
+
+ return ret;
+}
+
+static int do_spi_images_addr(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ uintptr_t images_load_addr;
+ uintptr_t spi_load_addr;
+ u32 len;
+ u32 spi_data_offset = sizeof(struct img_header);
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ /* convert command parameter to fastboot address (base 16), i.e. hex */
+ images_load_addr = hextoul(argv[1], NULL);
+ if (!images_load_addr) {
+ pr_err("Invalid load address\n");
+ return CMD_RET_USAGE;
+ }
+
+ spi_load_addr = hextoul(argv[2], NULL);
+ if (!spi_load_addr) {
+ pr_err("Invalid spi load address\n");
+ return CMD_RET_USAGE;
+ }
+
+ img_header = (struct img_header *)images_load_addr;
+
+ if (img_header->bin_sig != FW_IMAGE_SIG) {
+ pr_err("Invalid Nitro bin file\n");
+ goto error;
+ }
+
+ if (env_set_val("spi_nitro_fw_itb_start_addr", 0))
+ goto error;
+
+ if (env_set_val("spi_nitro_fw_itb_len", 0))
+ goto error;
+
+ if (env_set_val("spi_nitro_fw_ns3_cfg_start_addr", 0))
+ goto error;
+
+ if (env_set_val("spi_nitro_fw_ns3_cfg_len", 0))
+ goto error;
+
+ len = img_header->bin_size;
+
+ if (env_set_val("spi_nitro_fw_itb_start_addr",
+ (spi_load_addr + spi_data_offset)))
+ goto error;
+
+ if (env_set_val("spi_nitro_fw_itb_len", img_header->bin_size))
+ goto error;
+
+ spi_data_offset += len;
+
+ if (img_header->cfg1_sig == CFG_IMAGE_SIG) {
+ len = img_header->cfg1_size;
+
+ if (env_set_val("spi_nitro_fw_ns3_cfg_start_addr",
+ (spi_load_addr + spi_data_offset)))
+ goto error;
+
+ if (env_set_val("spi_nitro_fw_ns3_cfg_len", len))
+ goto error;
+
+ spi_data_offset += len;
+ }
+
+ /* disable secure boot */
+ if (env_set_val("nitro_fastboot_secure", 0))
+ goto error;
+
+ return CMD_RET_SUCCESS;
+
+error:
+ return CMD_RET_FAILURE;
+}
+
+U_BOOT_CMD
+ (spi_nitro_images_addr, 3, 1, do_spi_images_addr,
+ "Load the bnxt bin header and sets envs ",
+ "spi_nitro_images_addr <load_addr> <spi_base_addr>\n"
+);
diff --git a/cmd/btrfs.c b/cmd/btrfs.c
new file mode 100644
index 00000000000..69d1b1f830d
--- /dev/null
+++ b/cmd/btrfs.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 2017 by Marek Behún <kabel@kernel.org>
+ */
+
+#include <command.h>
+#include <btrfs.h>
+#include <fs.h>
+
+int do_btrsubvol(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_BTRFS))
+ return 1;
+
+ btrfs_list_subvols();
+ return 0;
+}
+
+U_BOOT_CMD(btrsubvol, 3, 1, do_btrsubvol,
+ "list subvolumes of a BTRFS filesystem",
+ "<interface> <dev[:part]>\n"
+ " - List subvolumes of a BTRFS filesystem."
+);
diff --git a/cmd/button.c b/cmd/button.c
new file mode 100644
index 00000000000..3e6db3f5b8e
--- /dev/null
+++ b/cmd/button.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Philippe Reynes <philippe.reynes@softathome.com>
+ *
+ * Based on led.c
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <button.h>
+#include <dm/uclass-internal.h>
+
+static const char *const state_label[] = {
+ [BUTTON_OFF] = "off",
+ [BUTTON_ON] = "on",
+};
+
+static int show_button_state(struct udevice *dev)
+{
+ int ret;
+
+ ret = button_get_state(dev);
+ if (ret >= BUTTON_COUNT)
+ ret = -EINVAL;
+ if (ret >= 0)
+ printf("%s\n", state_label[ret]);
+
+ return ret;
+}
+
+static int list_buttons(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ for (uclass_find_first_device(UCLASS_BUTTON, &dev);
+ dev;
+ uclass_find_next_device(&dev)) {
+ struct button_uc_plat *plat = dev_get_uclass_plat(dev);
+
+ if (!plat->label)
+ continue;
+ printf("%-15s ", plat->label);
+ if (device_active(dev)) {
+ ret = show_button_state(dev);
+ if (ret < 0)
+ printf("Error %d\n", ret);
+ } else {
+ printf("<inactive>\n");
+ }
+ }
+
+ return 0;
+}
+
+int do_button(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ const char *button_label;
+ struct udevice *dev;
+ int ret;
+
+ /* Validate arguments */
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ button_label = argv[1];
+ if (strncmp(button_label, "list", 4) == 0)
+ return list_buttons();
+
+ ret = button_get_by_label(button_label, &dev);
+ if (ret) {
+ printf("Button '%s' not found (err=%d)\n", button_label, ret);
+ return CMD_RET_FAILURE;
+ }
+
+ ret = show_button_state(dev);
+
+ return !ret;
+}
+
+U_BOOT_CMD(
+ button, 2, 1, do_button,
+ "manage buttons",
+ "<button_label> \tGet button state\n"
+ "button list\t\tShow a list of buttons"
+);
diff --git a/cmd/c5_pl330_dma.c b/cmd/c5_pl330_dma.c
new file mode 100644
index 00000000000..75e8c9b0d92
--- /dev/null
+++ b/cmd/c5_pl330_dma.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Brian Sune <briansune@gmail.com>
+ */
+
+#include <vsprintf.h>
+#include <command.h>
+#include <asm/io.h>
+
+#include <asm/arch/base_addr_ac5.h>
+
+#define RSTMGR_PERMODRST 0x18 /* PERMODRST register offset */
+
+static int do_dmareset(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ u8 val;
+ int i, ch;
+
+ if (argc < 2) {
+ printf("Usage: dmareset <channel 0-7> [<channel 0-7> ...]\n");
+ return CMD_RET_USAGE;
+ }
+
+ /* Read current register value */
+ val = readb(SOCFPGA_RSTMGR_ADDRESS + RSTMGR_PERMODRST);
+
+ /* Iterate over all channels given as arguments */
+ for (i = 1; i < argc; i++) {
+ ch = simple_strtoul(argv[i], NULL, 0);
+ if (ch < 0 || ch > 7) {
+ printf("Error: channel must be 0-7\n");
+ return CMD_RET_USAGE;
+ }
+ val &= ~(1 << ch);
+ printf("PL330 DMA channel %d reset released\n", ch);
+ }
+
+ /* Write back */
+ writeb(val, (SOCFPGA_RSTMGR_ADDRESS + RSTMGR_PERMODRST));
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ dmareset, 8, 0, do_dmareset,
+ "Release PL330 DMA channel reset(s) for SoCFPGA",
+ "dmareset <channel 0-7> [<channel 0-7> ...] - release reset for one or more DMA channels"
+);
diff --git a/cmd/cache.c b/cmd/cache.c
new file mode 100644
index 00000000000..b7007877ab0
--- /dev/null
+++ b/cmd/cache.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+/*
+ * Cache support: switch on or off, get status
+ */
+#include <command.h>
+#include <cpu_func.h>
+#include <linux/compiler.h>
+#include <linux/string.h>
+
+static int parse_argv(const char *);
+
+static int do_icache(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ switch (argc) {
+ case 2: /* on / off / flush */
+ switch (parse_argv(argv[1])) {
+ case 0:
+ icache_disable();
+ break;
+ case 1:
+ icache_enable();
+ break;
+ case 2:
+ invalidate_icache_all();
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ break;
+ case 1: /* get status */
+ printf("Instruction Cache is %s\n",
+ icache_status() ? "ON" : "OFF");
+ return 0;
+ default:
+ return CMD_RET_USAGE;
+ }
+ return 0;
+}
+
+static int do_dcache(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ switch (argc) {
+ case 2: /* on / off / flush */
+ switch (parse_argv(argv[1])) {
+ case 0:
+ dcache_disable();
+ break;
+ case 1:
+ dcache_enable();
+#ifdef CONFIG_SYS_NONCACHED_MEMORY
+ noncached_set_region();
+#endif
+ break;
+ case 2:
+ flush_dcache_all();
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ break;
+ case 1: /* get status */
+ printf("Data (writethrough) Cache is %s\n",
+ dcache_status() ? "ON" : "OFF");
+ return 0;
+ default:
+ return CMD_RET_USAGE;
+ }
+ return 0;
+}
+
+static int parse_argv(const char *s)
+{
+ if (strcmp(s, "flush") == 0)
+ return 2;
+ else if (strcmp(s, "on") == 0)
+ return 1;
+ else if (strcmp(s, "off") == 0)
+ return 0;
+
+ return -1;
+}
+
+U_BOOT_CMD(
+ icache, 2, 1, do_icache,
+ "enable or disable instruction cache",
+ "[on, off, flush]\n"
+ " - enable, disable, or flush instruction cache"
+);
+
+U_BOOT_CMD(
+ dcache, 2, 1, do_dcache,
+ "enable or disable data cache",
+ "[on, off, flush]\n"
+ " - enable, disable, or flush data (writethrough) cache"
+);
diff --git a/cmd/cat.c b/cmd/cat.c
new file mode 100644
index 00000000000..3167cda6032
--- /dev/null
+++ b/cmd/cat.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022
+ * Roger Knecht <rknecht@pm.de>
+ */
+
+#include <command.h>
+#include <fs.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <linux/errno.h>
+
+static int do_cat(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *ifname;
+ char *dev;
+ char *file;
+ char *buffer;
+ ulong file_size;
+ int ret;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ ifname = argv[1];
+ dev = argv[2];
+ file = argv[3];
+
+ ret = fs_load_alloc(ifname, dev, file, 0, 0, (void **)&buffer,
+ &file_size);
+
+ // check file exists
+ switch (ret) {
+ case 0:
+ break;
+ case -ENOMEDIUM:
+ return CMD_RET_FAILURE;
+ case -ENOENT:
+ log_err("File does not exist: ifname=%s dev=%s file=%s\n", ifname, dev, file);
+ return CMD_RET_FAILURE;
+ case -E2BIG:
+ log_err("File is too large: ifname=%s dev=%s file=%s\n", ifname, dev, file);
+ return CMD_RET_FAILURE;
+ case -ENOMEM:
+ log_err("Not enough memory: ifname=%s dev=%s file=%s\n", ifname, dev, file);
+ return CMD_RET_FAILURE;
+ default:
+ case -EIO:
+ log_err("File-read failed: ifname=%s dev=%s file=%s\n", ifname, dev, file);
+ return CMD_RET_FAILURE;
+ }
+
+ // print file content
+ buffer[file_size] = '\0';
+ puts(buffer);
+
+ free(buffer);
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(cat,
+ "<interface> <dev[:part]> <file>\n"
+ " - Print file from 'dev' on 'interface' to standard output\n");
+
+U_BOOT_CMD(cat, 4, 1, do_cat,
+ "Print file to standard output",
+ cat_help_text
+);
diff --git a/cmd/cbfs.c b/cmd/cbfs.c
new file mode 100644
index 00000000000..c1035461df1
--- /dev/null
+++ b/cmd/cbfs.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ */
+
+/*
+ * CBFS commands
+ */
+#include <command.h>
+#include <env.h>
+#include <cbfs.h>
+#include <vsprintf.h>
+
+static int do_cbfs_init(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ uintptr_t end_of_rom = 0xffffffff;
+ char *ep;
+
+ if (argc > 2) {
+ printf("usage: cbfsls [end of rom]>\n");
+ return 0;
+ }
+ if (argc == 2) {
+ end_of_rom = hextoul(argv[1], &ep);
+ if (*ep) {
+ puts("\n** Invalid end of ROM **\n");
+ return 1;
+ }
+ }
+ if (file_cbfs_init(end_of_rom)) {
+ printf("%s.\n", file_cbfs_error());
+ return 1;
+ }
+ return 0;
+}
+
+U_BOOT_CMD(
+ cbfsinit, 2, 0, do_cbfs_init,
+ "initialize the cbfs driver",
+ "[end of rom]\n"
+ " - Initialize the cbfs driver. The optional 'end of rom'\n"
+ " parameter specifies where the end of the ROM is that the\n"
+ " CBFS is in. It defaults to 0xFFFFFFFF\n"
+);
+
+static int do_cbfs_fsload(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const struct cbfs_cachenode *file;
+ unsigned long offset;
+ unsigned long count;
+ long size;
+
+ if (argc < 3) {
+ printf("usage: cbfsload <addr> <filename> [bytes]\n");
+ return 1;
+ }
+
+ /* parse offset and count */
+ offset = hextoul(argv[1], NULL);
+ if (argc == 4)
+ count = hextoul(argv[3], NULL);
+ else
+ count = 0;
+
+ file = file_cbfs_find(argv[2]);
+ if (!file) {
+ if (cbfs_get_result() == CBFS_FILE_NOT_FOUND)
+ printf("%s: %s\n", file_cbfs_error(), argv[2]);
+ else
+ printf("%s.\n", file_cbfs_error());
+ return 1;
+ }
+
+ printf("reading %s\n", file_cbfs_name(file));
+
+ size = file_cbfs_read(file, (void *)offset, count);
+
+ printf("\n%ld bytes read\n", size);
+
+ env_set_hex("filesize", size);
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ cbfsload, 4, 0, do_cbfs_fsload,
+ "load binary file from a cbfs filesystem",
+ "<addr> <filename> [bytes]\n"
+ " - load binary file 'filename' from the cbfs to address 'addr'\n"
+);
+
+static int do_cbfs_ls(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const struct cbfs_cachenode *file = file_cbfs_get_first();
+ int files = 0;
+
+ if (!file) {
+ printf("%s.\n", file_cbfs_error());
+ return 1;
+ }
+
+ printf(" size type name\n");
+ printf("------------------------------------------\n");
+ while (file) {
+ int type = file_cbfs_type(file);
+ char *type_name = NULL;
+ const char *filename = file_cbfs_name(file);
+
+ printf(" %8d", file_cbfs_size(file));
+
+ switch (type) {
+ case CBFS_TYPE_BOOTBLOCK:
+ type_name = "bootblock";
+ break;
+ case CBFS_TYPE_CBFSHEADER:
+ type_name = "cbfs header";
+ break;
+ case CBFS_TYPE_LEGACY_STAGE:
+ type_name = "stage";
+ break;
+ case CBFS_TYPE_PAYLOAD:
+ type_name = "payload";
+ break;
+ case CBFS_TYPE_FIT:
+ type_name = "fit";
+ break;
+ case CBFS_TYPE_OPTIONROM:
+ type_name = "option rom";
+ break;
+ case CBFS_TYPE_BOOTSPLASH:
+ type_name = "boot splash";
+ break;
+ case CBFS_TYPE_RAW:
+ type_name = "raw";
+ break;
+ case CBFS_TYPE_VSA:
+ type_name = "vsa";
+ break;
+ case CBFS_TYPE_MBI:
+ type_name = "mbi";
+ break;
+ case CBFS_TYPE_MICROCODE:
+ type_name = "microcode";
+ break;
+ case CBFS_TYPE_FSP:
+ type_name = "fsp";
+ break;
+ case CBFS_TYPE_MRC:
+ type_name = "mrc";
+ break;
+ case CBFS_TYPE_MMA:
+ type_name = "mma";
+ break;
+ case CBFS_TYPE_EFI:
+ type_name = "efi";
+ break;
+ case CBFS_TYPE_STRUCT:
+ type_name = "struct";
+ break;
+ case CBFS_TYPE_CMOS_DEFAULT:
+ type_name = "cmos default";
+ break;
+ case CBFS_TYPE_SPD:
+ type_name = "spd";
+ break;
+ case CBFS_TYPE_MRC_CACHE:
+ type_name = "mrc cache";
+ break;
+ case CBFS_TYPE_CMOS_LAYOUT:
+ type_name = "cmos layout";
+ break;
+ case -1:
+ case 0:
+ type_name = "null";
+ break;
+ }
+ if (type_name)
+ printf(" %16s", type_name);
+ else
+ printf(" %16d", type);
+
+ if (filename[0])
+ printf(" %s\n", filename);
+ else
+ printf(" %s\n", "(empty)");
+ file_cbfs_get_next(&file);
+ files++;
+ }
+
+ printf("\n%d file(s)\n\n", files);
+ return 0;
+}
+
+U_BOOT_CMD(
+ cbfsls, 1, 1, do_cbfs_ls,
+ "list files",
+ " - list the files in the cbfs\n"
+);
+
+static int do_cbfs_fsinfo(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const struct cbfs_header *header = file_cbfs_get_header();
+
+ if (!header) {
+ printf("%s.\n", file_cbfs_error());
+ return 1;
+ }
+
+ printf("\n");
+ printf("CBFS version: %#x\n", header->version);
+ printf("ROM size: %#x\n", header->rom_size);
+ printf("Boot block size: %#x\n", header->boot_block_size);
+ printf("CBFS size: %#x\n",
+ header->rom_size - header->boot_block_size - header->offset);
+ printf("Alignment: %d\n", header->align);
+ printf("Offset: %#x\n", header->offset);
+ printf("\n");
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ cbfsinfo, 1, 1, do_cbfs_fsinfo,
+ "print information about filesystem",
+ " - print information about the cbfs filesystem\n"
+);
diff --git a/cmd/cedit.c b/cmd/cedit.c
new file mode 100644
index 00000000000..20f48ae0007
--- /dev/null
+++ b/cmd/cedit.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 'cedit' command
+ *
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <abuf.h>
+#include <cedit.h>
+#include <command.h>
+#include <dm.h>
+#include <expo.h>
+#include <fs.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <dm/ofnode.h>
+#include <linux/sizes.h>
+
+struct expo *cur_exp;
+
+static int check_cur_expo(void)
+{
+ if (!cur_exp) {
+ printf("No expo loaded\n");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static int do_cedit_load(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *fname;
+ struct expo *exp;
+ oftree tree;
+ ulong size;
+ void *buf;
+ int ret;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+ fname = argv[3];
+
+ ret = fs_load_alloc(argv[1], argv[2], argv[3], SZ_1M, 0, &buf, &size);
+ if (ret) {
+ printf("File not found\n");
+ return CMD_RET_FAILURE;
+ }
+
+ tree = oftree_from_fdt(buf);
+ if (!oftree_valid(tree)) {
+ printf("Cannot create oftree\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = expo_build(oftree_root(tree), &exp);
+ oftree_dispose(tree);
+ if (ret) {
+ printf("Failed to build expo: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ cur_exp = exp;
+
+ return 0;
+}
+
+#ifdef CONFIG_COREBOOT_SYSINFO
+static int do_cedit_cb_load(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct expo *exp;
+ int ret;
+
+ if (argc > 1)
+ return CMD_RET_USAGE;
+
+ ret = cb_expo_build(&exp);
+ if (ret) {
+ printf("Failed to build expo: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ cur_exp = exp;
+
+ return 0;
+}
+#endif /* CONFIG_COREBOOT_SYSINFO */
+
+static int do_cedit_write_fdt(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *fname;
+ struct abuf buf;
+ loff_t bytes;
+ int ret;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+ fname = argv[3];
+
+ if (check_cur_expo())
+ return CMD_RET_FAILURE;
+
+ ret = cedit_write_settings(cur_exp, &buf);
+ if (ret) {
+ printf("Failed to write settings: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY))
+ return CMD_RET_FAILURE;
+
+ ret = fs_write(fname, map_to_sysmem(abuf_data(&buf)), 0,
+ abuf_size(&buf), &bytes);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+static int do_cedit_read_fdt(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *fname;
+ void *buf;
+ oftree tree;
+ ulong size;
+ int ret;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+ fname = argv[3];
+
+ ret = fs_load_alloc(argv[1], argv[2], argv[3], SZ_1M, 0, &buf, &size);
+ if (ret) {
+ printf("File not found\n");
+ return CMD_RET_FAILURE;
+ }
+
+ tree = oftree_from_fdt(buf);
+ if (!oftree_valid(tree)) {
+ free(buf);
+ printf("Cannot create oftree\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = cedit_read_settings(cur_exp, tree);
+ oftree_dispose(tree);
+ free(buf);
+ if (ret) {
+ printf("Failed to read settings: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_cedit_write_env(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool verbose;
+ int ret;
+
+ if (check_cur_expo())
+ return CMD_RET_FAILURE;
+
+ verbose = argc > 1 && !strcmp(argv[1], "-v");
+
+ ret = cedit_write_settings_env(cur_exp, verbose);
+ if (ret) {
+ printf("Failed to write settings to environment: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_cedit_read_env(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool verbose;
+ int ret;
+
+ if (check_cur_expo())
+ return CMD_RET_FAILURE;
+
+ verbose = argc > 1 && !strcmp(argv[1], "-v");
+
+ ret = cedit_read_settings_env(cur_exp, verbose);
+ if (ret) {
+ printf("Failed to read settings from environment: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_cedit_write_cmos(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ bool verbose = false;
+ int ret;
+
+ if (check_cur_expo())
+ return CMD_RET_FAILURE;
+
+ if (argc > 1 && !strcmp(argv[1], "-v")) {
+ verbose = true;
+ argc--;
+ argv++;
+ }
+
+ if (argc > 1)
+ ret = uclass_get_device_by_name(UCLASS_RTC, argv[1], &dev);
+ else
+ ret = uclass_first_device_err(UCLASS_RTC, &dev);
+ if (ret) {
+ printf("Failed to get RTC device: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ if (cedit_write_settings_cmos(cur_exp, dev, verbose)) {
+ printf("Failed to write settings to CMOS\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_cedit_read_cmos(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ bool verbose = false;
+ int ret;
+
+ if (check_cur_expo())
+ return CMD_RET_FAILURE;
+
+ if (argc > 1 && !strcmp(argv[1], "-v")) {
+ verbose = true;
+ argc--;
+ argv++;
+ }
+
+ if (argc > 1)
+ ret = uclass_get_device_by_name(UCLASS_RTC, argv[1], &dev);
+ else
+ ret = uclass_first_device_err(UCLASS_RTC, &dev);
+ if (ret) {
+ printf("Failed to get RTC device: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ ret = cedit_read_settings_cmos(cur_exp, dev, verbose);
+ if (ret) {
+ printf("Failed to read settings from CMOS: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ofnode node;
+ int ret;
+
+ if (check_cur_expo())
+ return CMD_RET_FAILURE;
+
+ node = ofnode_path("/bootstd/cedit-theme");
+ if (ofnode_valid(node)) {
+ ret = expo_apply_theme(cur_exp, node);
+ if (ret)
+ return CMD_RET_FAILURE;
+ } else {
+ log_warning("No theme found\n");
+ }
+ ret = cedit_run(cur_exp);
+ if (ret) {
+ log_err("Failed (err=%dE)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ expo_destroy(cur_exp);
+ cur_exp = NULL;
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(cedit,
+ "load <interface> <dev[:part]> <filename> - load config editor\n"
+#ifdef CONFIG_COREBOOT_SYSINFO
+ "cb_load - load coreboot CMOS editor\n"
+#endif
+ "cedit read_fdt <i/f> <dev[:part]> <filename> - read settings\n"
+ "cedit write_fdt <i/f> <dev[:part]> <filename> - write settings\n"
+ "cedit read_env [-v] - read settings from env vars\n"
+ "cedit write_env [-v] - write settings to env vars\n"
+ "cedit read_cmos [-v] [dev] - read settings from CMOS RAM\n"
+ "cedit write_cmos [-v] [dev] - write settings to CMOS RAM\n"
+ "cedit run - run config editor");
+
+U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text,
+ U_BOOT_SUBCMD_MKENT(load, 5, 1, do_cedit_load),
+#ifdef CONFIG_COREBOOT_SYSINFO
+ U_BOOT_SUBCMD_MKENT(cb_load, 5, 1, do_cedit_cb_load),
+#endif
+ U_BOOT_SUBCMD_MKENT(read_fdt, 5, 1, do_cedit_read_fdt),
+ U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt),
+ U_BOOT_SUBCMD_MKENT(read_env, 2, 1, do_cedit_read_env),
+ U_BOOT_SUBCMD_MKENT(write_env, 2, 1, do_cedit_write_env),
+ U_BOOT_SUBCMD_MKENT(read_cmos, 2, 1, do_cedit_read_cmos),
+ U_BOOT_SUBCMD_MKENT(write_cmos, 2, 1, do_cedit_write_cmos),
+ U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run),
+);
diff --git a/cmd/cli.c b/cmd/cli.c
new file mode 100644
index 00000000000..e0ddd0a43d0
--- /dev/null
+++ b/cmd/cli.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <cli.h>
+#include <command.h>
+#include <string.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char *gd_flags_to_parser_name(void)
+{
+ if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
+ return "old";
+ if (gd->flags & GD_FLG_HUSH_MODERN_PARSER)
+ return "modern";
+ return NULL;
+}
+
+static int do_cli_get(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *current = gd_flags_to_parser_name();
+
+ if (!current) {
+ printf("current cli value is not valid, this should not happen!\n");
+ return CMD_RET_FAILURE;
+ }
+
+ printf("%s\n", current);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int parser_string_to_gd_flags(const char *parser)
+{
+ if (!strcmp(parser, "old"))
+ return GD_FLG_HUSH_OLD_PARSER;
+ if (!strcmp(parser, "modern"))
+ return GD_FLG_HUSH_MODERN_PARSER;
+ return -1;
+}
+
+static int gd_flags_to_parser_config(int flag)
+{
+ if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
+ return CONFIG_VAL(HUSH_OLD_PARSER);
+ if (gd->flags & GD_FLG_HUSH_MODERN_PARSER)
+ return CONFIG_VAL(HUSH_MODERN_PARSER);
+ return -1;
+}
+
+static void reset_parser_gd_flags(void)
+{
+ gd->flags &= ~GD_FLG_HUSH_OLD_PARSER;
+ gd->flags &= ~GD_FLG_HUSH_MODERN_PARSER;
+}
+
+static int do_cli_set(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *parser_name;
+ int parser_config;
+ int parser_flag;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ parser_name = argv[1];
+
+ parser_flag = parser_string_to_gd_flags(parser_name);
+ if (parser_flag == -1) {
+ printf("Bad value for parser name: %s\n", parser_name);
+ return CMD_RET_USAGE;
+ }
+
+ parser_config = gd_flags_to_parser_config(parser_flag);
+ switch (parser_config) {
+ case -1:
+ printf("Bad value for parser flags: %d\n", parser_flag);
+ return CMD_RET_FAILURE;
+ case 0:
+ printf("Want to set current parser to %s, but its code was not compiled!\n",
+ parser_name);
+ return CMD_RET_FAILURE;
+ }
+
+ reset_parser_gd_flags();
+ gd->flags |= parser_flag;
+
+ cli_init();
+ cli_loop();
+
+ /* cli_loop() should never return. */
+ return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl parser_sub[] = {
+ U_BOOT_CMD_MKENT(get, 1, 1, do_cli_get, "", ""),
+ U_BOOT_CMD_MKENT(set, 2, 1, do_cli_set, "", ""),
+};
+
+static int do_cli(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* drop initial "parser" arg */
+ argc--;
+ argv++;
+
+ cp = find_cmd_tbl(argv[0], parser_sub, ARRAY_SIZE(parser_sub));
+ if (cp)
+ return cp->cmd(cmdtp, flag, argc, argv);
+
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_LONGHELP(cli,
+ "get - print current cli\n"
+ "set - set the current cli, possible value are: old, modern\n");
+
+U_BOOT_CMD(cli, 3, 1, do_cli,
+ "cli",
+ cli_help_text
+);
diff --git a/cmd/clk.c b/cmd/clk.c
new file mode 100644
index 00000000000..2fc834e5549
--- /dev/null
+++ b/cmd/clk.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Xilinx, Inc.
+ */
+#include <command.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device.h>
+#include <dm/root.h>
+#include <dm/device-internal.h>
+#include <linux/clk-provider.h>
+
+static void show_clks(struct udevice *dev, int depth, int last_flag)
+{
+ int i, is_last;
+ struct udevice *child;
+ struct clk *clkp, *parent;
+ u32 rate;
+
+ clkp = dev_get_clk_ptr(dev);
+ if (clkp) {
+ parent = clk_get_parent(clkp);
+ if (!IS_ERR(parent) && depth == -1)
+ return;
+ depth++;
+ rate = clk_get_rate(clkp);
+
+ printf(" %-12u %8d ", rate, clkp->enable_count);
+
+ for (i = depth; i >= 0; i--) {
+ is_last = (last_flag >> i) & 1;
+ if (i) {
+ if (is_last)
+ printf(" ");
+ else
+ printf("| ");
+ } else {
+ if (is_last)
+ printf("`-- ");
+ else
+ printf("|-- ");
+ }
+ }
+
+ printf("%s\n", dev->name);
+ }
+
+ device_foreach_child_probe(child, dev) {
+ if (device_get_uclass_id(child) != UCLASS_CLK)
+ continue;
+ if (child == dev)
+ continue;
+ is_last = list_is_last(&child->sibling_node, &dev->child_head);
+ show_clks(child, depth, (last_flag << 1) | is_last);
+ }
+}
+
+static int soc_clk_dump(void)
+{
+ struct udevice *dev;
+ const struct clk_ops *ops;
+
+ printf(" Rate Usecnt Name\n");
+ printf("------------------------------------------\n");
+
+ uclass_foreach_dev_probe(UCLASS_CLK, dev)
+ show_clks(dev, -1, 0);
+
+ uclass_foreach_dev_probe(UCLASS_CLK, dev) {
+ ops = dev_get_driver_ops(dev);
+ if (ops && ops->dump) {
+ printf("\n%s %s:\n", dev->driver->name, dev->name);
+ ops->dump(dev);
+ }
+ }
+
+ return 0;
+}
+
+static int do_clk_dump(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+
+ ret = soc_clk_dump();
+ if (ret < 0) {
+ printf("Clock dump error %d\n", ret);
+ ret = CMD_RET_FAILURE;
+ }
+
+ return ret;
+}
+
+static int do_clk_setfreq(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct clk *clk = NULL;
+ s32 freq;
+ struct udevice *dev;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ freq = dectoul(argv[2], NULL);
+
+ if (!uclass_get_device_by_name(UCLASS_CLK, argv[1], &dev))
+ clk = dev_get_clk_ptr(dev);
+
+ if (!clk) {
+ printf("clock '%s' not found.\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ freq = clk_set_rate(clk, freq);
+ if (freq < 0) {
+ printf("set_rate failed: %d\n", freq);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("set_rate returns %u\n", freq);
+ return 0;
+}
+
+static struct cmd_tbl cmd_clk_sub[] = {
+ U_BOOT_CMD_MKENT(dump, 1, 1, do_clk_dump, "", ""),
+ U_BOOT_CMD_MKENT(setfreq, 3, 1, do_clk_setfreq, "", ""),
+};
+
+static int do_clk(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *c;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* Strip off leading 'clk' command argument */
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], &cmd_clk_sub[0], ARRAY_SIZE(cmd_clk_sub));
+
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+ else
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_LONGHELP(clk,
+ "dump - Print clock frequencies\n"
+ "clk setfreq [clk] [freq] - Set clock frequency");
+
+U_BOOT_CMD(clk, 4, 1, do_clk, "CLK sub-system", clk_help_text);
diff --git a/cmd/clone.c b/cmd/clone.c
new file mode 100644
index 00000000000..1f3cff1836d
--- /dev/null
+++ b/cmd/clone.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 John Chau <john@harmon.hk>
+ *
+ */
+
+#include <command.h>
+#include <malloc.h>
+#include <part.h>
+#include <blk.h>
+#include <time.h>
+#include <vsprintf.h>
+
+#define BUFSIZE (1 * 1024 * 1024)
+static int do_clone(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ int srcdev, destdev;
+ struct blk_desc *srcdesc, *destdesc;
+ int srcbz, destbz, ret;
+ char *unit, *buf;
+ unsigned long wrcnt, rdcnt, requested, srcblk, destblk;
+ unsigned long timer;
+ const unsigned long buffersize = 1024 * 1024;
+
+ if (argc < 6)
+ return CMD_RET_USAGE;
+
+ srcdev = blk_get_device_by_str(argv[1], argv[2], &srcdesc);
+ destdev = blk_get_device_by_str(argv[3], argv[4], &destdesc);
+ if (srcdev < 0) {
+ printf("Unable to open source device\n");
+ return 1;
+ } else if (destdev < 0) {
+ printf("Unable to open destination device\n");
+ return 1;
+ }
+ requested = dectoul(argv[5], &unit);
+ srcbz = srcdesc->blksz;
+ destbz = destdesc->blksz;
+
+ if ((srcbz * (buffersize / srcbz) != buffersize) ||
+ (destbz * (buffersize / destbz) != buffersize)) {
+ printf("failed: cannot match device block sizes\n");
+ return 1;
+ }
+ if (requested == 0) {
+ unsigned long a = srcdesc->lba * srcdesc->blksz;
+ unsigned long b = destdesc->lba * destdesc->blksz;
+
+ if (a > b)
+ requested = a;
+ else
+ requested = b;
+ } else {
+ switch (unit[0]) {
+ case 'g':
+ case 'G':
+ requested *= 1024 * 1024 * 1024;
+ break;
+ case 'm':
+ case 'M':
+ requested *= 1024 * 1024;
+ break;
+ case 'k':
+ case 'K':
+ requested *= 1024;
+ break;
+ }
+ }
+ printf("Copying %ld bytes from %s:%s to %s:%s\n",
+ requested, argv[1], argv[2], argv[3], argv[4]);
+ wrcnt = 0;
+ rdcnt = 0;
+ buf = (char *)malloc(BUFSIZE);
+ srcblk = 0;
+ destblk = 0;
+ timer = get_timer(0);
+ while (wrcnt < requested) {
+ unsigned long toread = BUFSIZE / srcbz;
+ unsigned long towrite = BUFSIZE / destbz;
+ unsigned long offset = 0;
+
+read:
+ ret = blk_dread(srcdesc, srcblk, toread, buf + offset);
+ if (ret < 0) {
+ printf("Src read error @blk %ld\n", srcblk);
+ goto exit;
+ }
+ rdcnt += ret * srcbz;
+ srcblk += ret;
+ if (ret < toread) {
+ toread -= ret;
+ offset += ret * srcbz;
+ goto read;
+ }
+ offset = 0;
+write:
+ ret = blk_dwrite(destdesc, destblk, towrite, buf + offset);
+ if (ret < 0) {
+ printf("Dest write error @blk %ld\n", srcblk);
+ goto exit;
+ }
+ wrcnt += ret * destbz;
+ destblk += ret;
+ if (ret < towrite) {
+ towrite -= ret;
+ offset += ret * destbz;
+ goto write;
+ }
+ }
+
+exit:
+ timer = get_timer(timer);
+ timer = 1000 * timer / CONFIG_SYS_HZ;
+ printf("%ld read\n", rdcnt);
+ printf("%ld written\n", wrcnt);
+ printf("%ldms, %ldkB/s\n", timer, wrcnt / timer);
+ free(buf);
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ clone, 6, 1, do_clone,
+ "simple storage cloning",
+ "<src interface> <src dev> <dest interface> <dest dev> <size[K/M/G]>\n"
+ "clone storage from 'src dev' on 'src interface' to 'dest dev' on 'dest interface' with maximum 'size' bytes (or 0 for clone to end)"
+);
diff --git a/cmd/cls.c b/cmd/cls.c
new file mode 100644
index 00000000000..b1e0619334b
--- /dev/null
+++ b/cmd/cls.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * DENX Software Engineering, Anatolij Gustschin <agust@denx.de>
+ *
+ * cls - clear screen command
+ */
+#include <command.h>
+#include <console.h>
+#include <dm.h>
+
+static int do_video_clear(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (console_clear())
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(cls, 1, 0, do_video_clear, "clear screen", "");
diff --git a/cmd/config.c b/cmd/config.c
new file mode 100644
index 00000000000..f0d2033c61f
--- /dev/null
+++ b/cmd/config.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Masahiro Yamada <yamada.masahiro@socionext.com>
+ */
+
+#include <command.h>
+#include <gzip.h>
+#include <malloc.h>
+
+#include "config_data_gz.h"
+#include "config_data_size.h"
+
+static int do_config(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *dst;
+ unsigned long len = data_size;
+ int ret = CMD_RET_SUCCESS;
+
+ dst = malloc(data_size + 1);
+ if (!dst)
+ return CMD_RET_FAILURE;
+
+ ret = gunzip(dst, data_size, (unsigned char *)data_gz, &len);
+ if (ret) {
+ printf("failed to uncompress .config data\n");
+ ret = CMD_RET_FAILURE;
+ goto free;
+ }
+
+ dst[data_size] = 0;
+ puts(dst);
+
+free:
+ free(dst);
+
+ return ret;
+}
+
+U_BOOT_CMD(
+ config, 1, 1, do_config,
+ "print .config",
+ ""
+);
diff --git a/cmd/conitrace.c b/cmd/conitrace.c
new file mode 100644
index 00000000000..6cc113328eb
--- /dev/null
+++ b/cmd/conitrace.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The 'conitrace' command prints the codes received from the console input as
+ * hexadecimal numbers.
+ *
+ * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+#include <command.h>
+#include <linux/delay.h>
+
+static int do_conitrace(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool first = true;
+
+ printf("Waiting for your input\n");
+ printf("To terminate type 'x'\n");
+
+ /* Empty input buffer */
+ while (tstc())
+ getchar();
+
+ for (;;) {
+ int c = getchar();
+
+ if (first && (c == 'x' || c == 'X'))
+ break;
+
+ printf("%02x ", c);
+ first = false;
+
+ /* 10 ms delay - serves to detect separate keystrokes */
+ udelay(10000);
+ if (!tstc()) {
+ printf("\n");
+ first = true;
+ }
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_LONGHELP(conitrace, "");
+
+U_BOOT_CMD_COMPLETE(
+ conitrace, 2, 0, do_conitrace,
+ "trace console input",
+ conitrace_help_text, NULL
+);
diff --git a/cmd/console.c b/cmd/console.c
new file mode 100644
index 00000000000..a8133ee3fa3
--- /dev/null
+++ b/cmd/console.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+/*
+ * Boot support
+ */
+#include <command.h>
+#include <iomux.h>
+#include <stdio_dev.h>
+
+extern void _do_coninfo (void);
+static int do_coninfo(struct cmd_tbl *cmd, int flag, int argc,
+ char *const argv[])
+{
+ int l;
+ struct list_head *list = stdio_get_list();
+ struct list_head *pos;
+ struct stdio_dev *dev;
+
+ /* Scan for valid output and input devices */
+
+ puts("List of available devices\n");
+
+ list_for_each(pos, list) {
+ dev = list_entry(pos, struct stdio_dev, list);
+
+ printf("|-- %s (%s%s)\n",
+ dev->name,
+ (dev->flags & DEV_FLAGS_INPUT) ? "I" : "",
+ (dev->flags & DEV_FLAGS_OUTPUT) ? "O" : "");
+
+ for (l = 0; l < MAX_FILES; l++) {
+ if (CONFIG_IS_ENABLED(CONSOLE_MUX)) {
+ if (iomux_match_device(console_devices[l],
+ cd_count[l], dev) >= 0)
+ printf("| |-- %s\n", stdio_names[l]);
+ } else {
+ if (stdio_devices[l] == dev)
+ printf("| |-- %s\n", stdio_names[l]);
+ }
+
+ }
+ }
+ return 0;
+}
+
+/***************************************************/
+
+U_BOOT_CMD(
+ coninfo, 3, 1, do_coninfo,
+ "print console devices and information",
+ ""
+);
diff --git a/cmd/cpu.c b/cmd/cpu.c
new file mode 100644
index 00000000000..27552507564
--- /dev/null
+++ b/cmd/cpu.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ * Copyright (c) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
+ * Copyright 2024 NXP
+ */
+
+#include <command.h>
+#include <cpu.h>
+#include <display_options.h>
+#include <dm.h>
+#include <errno.h>
+
+static const char *cpu_feature_name[CPU_FEAT_COUNT] = {
+ "L1 cache",
+ "MMU",
+ "Microcode",
+ "Device ID",
+};
+
+static struct udevice *cpu_find_device(unsigned long cpu_id)
+{
+ struct udevice *dev;
+
+ for (uclass_first_device(UCLASS_CPU, &dev); dev;
+ uclass_next_device(&dev)) {
+ if (cpu_id == dev_seq(dev))
+ return dev;
+ }
+
+ return NULL;
+}
+
+static int print_cpu_list(bool detail)
+{
+ struct udevice *dev;
+ char buf[100];
+
+ for (uclass_first_device(UCLASS_CPU, &dev);
+ dev;
+ uclass_next_device(&dev)) {
+ struct cpu_plat *plat = dev_get_parent_plat(dev);
+ struct cpu_info info;
+ bool first = true;
+ int ret, i;
+
+ ret = cpu_get_desc(dev, buf, sizeof(buf));
+ printf("%3d: %-10s %s\n", dev_seq(dev), dev->name,
+ ret ? "<no description>" : buf);
+ if (!detail)
+ continue;
+ ret = cpu_get_info(dev, &info);
+ if (ret) {
+ printf("\t(no detail available");
+ if (ret != -ENOSYS)
+ printf(": err=%d", ret);
+ printf(")\n");
+ continue;
+ }
+ printf("\tID = %d, freq = ", plat->cpu_id);
+ print_freq(info.cpu_freq, "");
+ for (i = 0; i < CPU_FEAT_COUNT; i++) {
+ if (info.features & (1 << i)) {
+ printf("%s%s", first ? ": " : ", ",
+ cpu_feature_name[i]);
+ first = false;
+ }
+ }
+ printf("\n");
+ if (info.features & (1 << CPU_FEAT_UCODE))
+ printf("\tMicrocode version %#x\n",
+ plat->ucode_version);
+ if (info.features & (1 << CPU_FEAT_DEVICE_ID))
+ printf("\tDevice ID %#lx\n", plat->device_id);
+ }
+
+ return 0;
+}
+
+static int do_cpu_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (print_cpu_list(false))
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+static int do_cpu_detail(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (print_cpu_list(true))
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+static int do_cpu_release(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ unsigned long cpu_id;
+ unsigned long long boot_addr;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ cpu_id = dectoul(argv[1], NULL);
+ dev = cpu_find_device(cpu_id);
+ if (!dev)
+ return CMD_RET_FAILURE;
+
+ boot_addr = simple_strtoull(argv[2], NULL, 16);
+
+ if (cpu_release_core(dev, boot_addr))
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(cpu,
+ "list - list available CPUs\n"
+ "cpu detail - show CPU detail\n"
+ "cpu release <core ID> <addr> - Release CPU <core ID> at <addr>\n"
+ " <core ID>: the sequence number in list subcommand outputs");
+
+U_BOOT_CMD_WITH_SUBCMDS(cpu, "display information about CPUs", cpu_help_text,
+ U_BOOT_SUBCMD_MKENT(list, 1, 1, do_cpu_list),
+ U_BOOT_SUBCMD_MKENT(detail, 1, 0, do_cpu_detail),
+ U_BOOT_SUBCMD_MKENT(release, 3, 0, do_cpu_release));
diff --git a/cmd/cramfs.c b/cmd/cramfs.c
new file mode 100644
index 00000000000..baff50d1bd6
--- /dev/null
+++ b/cmd/cramfs.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *
+ * based on: cmd_jffs2.c
+ *
+ * Add support for a CRAMFS located in RAM
+ */
+
+/*
+ * CRAMFS support
+ */
+#include <command.h>
+#include <env.h>
+#include <image.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <jffs2/jffs2.h>
+#include <jffs2/load_kernel.h>
+#include <cramfs/cramfs_fs.h>
+#include <asm/io.h>
+
+/* enable/disable debugging messages */
+#define DEBUG_CRAMFS
+#undef DEBUG_CRAMFS
+
+#ifdef DEBUG_CRAMFS
+# define DEBUGF(fmt, args...) printf(fmt ,##args)
+#else
+# define DEBUGF(fmt, args...)
+#endif
+
+#ifndef CONFIG_MTD_NOR_FLASH
+# define OFFSET_ADJUSTMENT 0
+#else
+#include <flash.h>
+# define OFFSET_ADJUSTMENT (flash_info[id.num].start[0])
+#endif
+
+#ifndef CONFIG_FS_JFFS2
+#include <linux/stat.h>
+char *mkmodestr(unsigned long mode, char *str)
+{
+ static const char *l = "xwr";
+ int mask = 1, i;
+ char c;
+
+ switch (mode & S_IFMT) {
+ case S_IFDIR: str[0] = 'd'; break;
+ case S_IFBLK: str[0] = 'b'; break;
+ case S_IFCHR: str[0] = 'c'; break;
+ case S_IFIFO: str[0] = 'f'; break;
+ case S_IFLNK: str[0] = 'l'; break;
+ case S_IFSOCK: str[0] = 's'; break;
+ case S_IFREG: str[0] = '-'; break;
+ default: str[0] = '?';
+ }
+
+ for(i = 0; i < 9; i++) {
+ c = l[i%3];
+ str[9-i] = (mode & mask)?c:'-';
+ mask = mask<<1;
+ }
+
+ if(mode & S_ISUID) str[3] = (mode & S_IXUSR)?'s':'S';
+ if(mode & S_ISGID) str[6] = (mode & S_IXGRP)?'s':'S';
+ if(mode & S_ISVTX) str[9] = (mode & S_IXOTH)?'t':'T';
+ str[10] = '\0';
+ return str;
+}
+#endif /* CONFIG_FS_JFFS2 */
+
+extern int cramfs_check (struct part_info *info);
+extern int cramfs_load (char *loadoffset, struct part_info *info, char *filename);
+extern int cramfs_ls (struct part_info *info, char *filename);
+extern int cramfs_info (struct part_info *info);
+
+/***************************************************/
+/* U-Boot commands */
+/***************************************************/
+
+/**
+ * Routine implementing fsload u-boot command. This routine tries to load
+ * a requested file from cramfs filesystem at location 'cramfsaddr'.
+ * cramfsaddr is an evironment variable.
+ *
+ * @param cmdtp command internal data
+ * @param flag command flag
+ * @param argc number of arguments supplied to the command
+ * @param argv arguments list
+ * Return: 0 on success, 1 otherwise
+ */
+int do_cramfs_load(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *filename;
+ int size;
+ ulong offset = image_load_addr;
+ char *offset_virt;
+
+ struct part_info part;
+ struct mtd_device dev;
+ struct mtdids id;
+
+ ulong addr;
+ addr = hextoul(env_get("cramfsaddr"), NULL);
+
+ /* hack! */
+ /* cramfs_* only supports NOR flash chips */
+ /* fake the device type */
+ id.type = MTD_DEV_TYPE_NOR;
+ id.num = 0;
+ dev.id = &id;
+ part.dev = &dev;
+ /* fake the address offset */
+ part.offset = (u64)(uintptr_t) map_sysmem(addr - OFFSET_ADJUSTMENT, 0);
+
+ /* pre-set Boot file name */
+ filename = env_get("bootfile");
+ if (!filename)
+ filename = "uImage";
+
+ if (argc == 2) {
+ filename = argv[1];
+ }
+ if (argc == 3) {
+ offset = simple_strtoul(argv[1], NULL, 0);
+ image_load_addr = offset;
+ filename = argv[2];
+ }
+
+ offset_virt = map_sysmem(offset, 0);
+ size = 0;
+ if (cramfs_check(&part))
+ size = cramfs_load (offset_virt, &part, filename);
+
+ if (size > 0) {
+ printf("### CRAMFS load complete: %d bytes loaded to 0x%lx\n",
+ size, offset);
+ env_set_hex("filesize", size);
+ } else {
+ printf("### CRAMFS LOAD ERROR<%x> for %s!\n", size, filename);
+ }
+
+ unmap_sysmem(offset_virt);
+ unmap_sysmem((void *)(uintptr_t)part.offset);
+
+ return !(size > 0);
+}
+
+/**
+ * Routine implementing u-boot ls command which lists content of a given
+ * directory at location 'cramfsaddr'.
+ * cramfsaddr is an evironment variable.
+ *
+ * @param cmdtp command internal data
+ * @param flag command flag
+ * @param argc number of arguments supplied to the command
+ * @param argv arguments list
+ * Return: 0 on success, 1 otherwise
+ */
+int do_cramfs_ls(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char *filename = "/";
+ int ret;
+ struct part_info part;
+ struct mtd_device dev;
+ struct mtdids id;
+
+ ulong addr;
+ addr = hextoul(env_get("cramfsaddr"), NULL);
+
+ /* hack! */
+ /* cramfs_* only supports NOR flash chips */
+ /* fake the device type */
+ id.type = MTD_DEV_TYPE_NOR;
+ id.num = 0;
+ dev.id = &id;
+ part.dev = &dev;
+ /* fake the address offset */
+ part.offset = (u64)(uintptr_t) map_sysmem(addr - OFFSET_ADJUSTMENT, 0);
+
+ if (argc == 2)
+ filename = argv[1];
+
+ ret = 0;
+ if (cramfs_check(&part))
+ ret = cramfs_ls (&part, filename);
+ unmap_sysmem((void *)(uintptr_t)part.offset);
+
+ return ret ? 0 : 1;
+}
+
+/* command line only */
+
+/***************************************************/
+U_BOOT_CMD(
+ cramfsload, 3, 0, do_cramfs_load,
+ "load binary file from a filesystem image",
+ "[ off ] [ filename ]\n"
+ " - load binary file from address 'cramfsaddr'\n"
+ " with offset 'off'\n"
+);
+U_BOOT_CMD(
+ cramfsls, 2, 1, do_cramfs_ls,
+ "list files in a directory (default /)",
+ "[ directory ]\n"
+ " - list files in a directory.\n"
+);
diff --git a/cmd/cros_ec.c b/cmd/cros_ec.c
new file mode 100644
index 00000000000..7b60e415b6c
--- /dev/null
+++ b/cmd/cros_ec.c
@@ -0,0 +1,568 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Chromium OS cros_ec driver
+ *
+ * Copyright (c) 2016 The Chromium OS Authors.
+ * Copyright (c) 2016 National Instruments Corp
+ */
+
+#include <command.h>
+#include <cros_ec.h>
+#include <dm.h>
+#include <log.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+
+/* Note: depends on enum ec_current_image */
+static const char * const ec_current_image_name[] = {"unknown", "RO", "RW"};
+
+/**
+ * Decode a flash region parameter
+ *
+ * @param argc Number of params remaining
+ * @param argv List of remaining parameters
+ * Return: flash region (EC_FLASH_REGION_...) or -1 on error
+ */
+static int cros_ec_decode_region(int argc, char *const argv[])
+{
+ if (argc > 0) {
+ if (0 == strcmp(*argv, "rw"))
+ return EC_FLASH_REGION_ACTIVE;
+ else if (0 == strcmp(*argv, "ro"))
+ return EC_FLASH_REGION_RO;
+
+ debug("%s: Invalid region '%s'\n", __func__, *argv);
+ } else {
+ debug("%s: Missing region parameter\n", __func__);
+ }
+
+ return -1;
+}
+
+/**
+ * Perform a flash read or write command
+ *
+ * @param dev CROS-EC device to read/write
+ * @param is_write 1 do to a write, 0 to do a read
+ * @param argc Number of arguments
+ * @param argv Arguments (2 is region, 3 is address)
+ * Return: 0 for ok, 1 for a usage error or -ve for ec command error
+ * (negative EC_RES_...)
+ */
+static int do_read_write(struct udevice *dev, int is_write, int argc,
+ char *const argv[])
+{
+ uint32_t offset, size = -1U, region_size;
+ unsigned long addr;
+ char *endp;
+ int region;
+ int ret;
+
+ region = cros_ec_decode_region(argc - 2, argv + 2);
+ if (region == -1)
+ return 1;
+ if (argc < 4)
+ return 1;
+ addr = hextoul(argv[3], &endp);
+ if (*argv[3] == 0 || *endp != 0)
+ return 1;
+ if (argc > 4) {
+ size = hextoul(argv[4], &endp);
+ if (*argv[4] == 0 || *endp != 0)
+ return 1;
+ }
+
+ ret = cros_ec_flash_offset(dev, region, &offset, &region_size);
+ if (ret) {
+ debug("%s: Could not read region info\n", __func__);
+ return ret;
+ }
+ if (size == -1U)
+ size = region_size;
+
+ ret = is_write ?
+ cros_ec_flash_write(dev, (uint8_t *)addr, offset, size) :
+ cros_ec_flash_read(dev, (uint8_t *)addr, offset, size);
+ if (ret) {
+ debug("%s: Could not %s region\n", __func__,
+ is_write ? "write" : "read");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const char *const feat_name[64] = {
+ "limited",
+ "flash",
+ "pwm_fan",
+ "pwm_keyb",
+ "lightbar",
+ "led",
+ "motion_sense",
+ "keyb",
+ "pstore",
+ "port80",
+ "thermal",
+ "bklight_switch",
+ "wifi_switch",
+ "host_events",
+ "gpio",
+ "i2c",
+ "charger",
+ "battery",
+ "smart_battery",
+ "hang_detect",
+ "pmu",
+ "sub_mcu",
+ "usb_pd",
+ "usb_mux",
+ "motion_sense_fifo",
+ "vstore",
+ "usbc_ss_mux_virtual",
+ "rtc",
+ "fingerprint",
+ "touchpad",
+ "rwsig",
+ "device_event",
+ "unified_wake_masks",
+ "host_event64",
+ "exec_in_ram",
+ "cec",
+ "motion_sense_tight_timestamps",
+ "refined_tablet_mode_hysteresis",
+ "efs2",
+ "scp",
+ "ish",
+ "typec_cmd",
+ "typec_require_ap_mode_entry",
+ "typec_mux_require_ap_ack",
+};
+
+static int do_show_features(struct udevice *dev)
+{
+ u64 feat;
+ int ret;
+ uint i;
+
+ ret = cros_ec_get_features(dev, &feat);
+ if (ret)
+ return ret;
+ for (i = 0; i < ARRAY_SIZE(feat_name); i++) {
+ if (feat & (1ULL << i)) {
+ if (feat_name[i])
+ printf("%s\n", feat_name[i]);
+ else
+ printf("unknown %d\n", i);
+ }
+ }
+
+ return 0;
+}
+
+static const char *const switch_name[8] = {
+ "lid open",
+ "power button pressed",
+ "write-protect disabled",
+ NULL,
+ "dedicated recovery",
+ NULL,
+ NULL,
+ NULL,
+};
+
+static int do_show_switches(struct udevice *dev)
+{
+ uint switches;
+ int ret;
+ uint i;
+
+ ret = cros_ec_get_switches(dev);
+ if (ret < 0)
+ return log_msg_ret("get", ret);
+ switches = ret;
+ for (i = 0; i < ARRAY_SIZE(switch_name); i++) {
+ uint mask = 1 << i;
+
+ if (switches & mask) {
+ if (switch_name[i])
+ printf("%s\n", switch_name[i]);
+ else
+ printf("unknown %02x\n", mask);
+ }
+ }
+
+ return 0;
+}
+
+static const char *const event_name[] = {
+ "lid_closed",
+ "lid_open",
+ "power_button",
+ "ac_connected",
+ "ac_disconnected",
+ "battery_low",
+ "battery_critical",
+ "battery",
+ "thermal_threshold",
+ "device",
+ "thermal",
+ "usb_charger",
+ "key_pressed",
+ "interface_ready",
+ "keyboard_recovery",
+ "thermal_shutdown",
+ "battery_shutdown",
+ "throttle_start",
+ "throttle_stop",
+ "hang_detect",
+ "hang_reboot",
+ "pd_mcu",
+ "battery_status",
+ "panic",
+ "keyboard_fastboot",
+ "rtc",
+ "mkbp",
+ "usb_mux",
+ "mode_change",
+ "keyboard_recovery_hw_reinit",
+ "extended",
+ "invalid",
+};
+
+static int do_show_events(struct udevice *dev)
+{
+ u32 events;
+ int ret;
+ uint i;
+
+ ret = cros_ec_get_host_events(dev, &events);
+ if (ret)
+ return ret;
+ printf("%08x\n", events);
+ for (i = 0; i < ARRAY_SIZE(event_name); i++) {
+ enum host_event_code code = i + 1;
+ u64 mask = EC_HOST_EVENT_MASK(code);
+
+ if (events & mask) {
+ if (event_name[i])
+ printf("%s\n", event_name[i]);
+ else
+ printf("unknown code %#x\n", code);
+ }
+ }
+
+ return 0;
+}
+
+static int do_cros_ec(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ const char *cmd;
+ int ret = 0;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ cmd = argv[1];
+ if (0 == strcmp("init", cmd)) {
+ /* Remove any existing device */
+ ret = uclass_find_device(UCLASS_CROS_EC, 0, &dev);
+ if (!ret)
+ device_remove(dev, DM_REMOVE_NORMAL);
+ ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev);
+ if (ret) {
+ printf("Could not init cros_ec device (err %d)\n", ret);
+ return 1;
+ }
+ return 0;
+ }
+
+ ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev);
+ if (ret) {
+ printf("Cannot get cros-ec device (err=%d)\n", ret);
+ return 1;
+ }
+ if (0 == strcmp("id", cmd)) {
+ char id[MSG_BYTES];
+
+ if (cros_ec_read_id(dev, id, sizeof(id))) {
+ debug("%s: Could not read KBC ID\n", __func__);
+ return 1;
+ }
+ printf("%s\n", id);
+ } else if (0 == strcmp("info", cmd)) {
+ struct ec_response_mkbp_info info;
+
+ if (cros_ec_info(dev, &info)) {
+ debug("%s: Could not read KBC info\n", __func__);
+ return 1;
+ }
+ printf("rows = %u\n", info.rows);
+ printf("cols = %u\n", info.cols);
+ } else if (!strcmp("features", cmd)) {
+ ret = do_show_features(dev);
+
+ if (ret)
+ printf("Error: %d\n", ret);
+ } else if (!strcmp("switches", cmd)) {
+ ret = do_show_switches(dev);
+
+ if (ret)
+ printf("Error: %d\n", ret);
+ } else if (0 == strcmp("curimage", cmd)) {
+ enum ec_current_image image;
+
+ if (cros_ec_read_current_image(dev, &image)) {
+ debug("%s: Could not read KBC image\n", __func__);
+ return 1;
+ }
+ printf("%d\n", image);
+ } else if (0 == strcmp("hash", cmd)) {
+ struct ec_response_vboot_hash hash;
+ int i;
+
+ if (cros_ec_read_hash(dev, EC_VBOOT_HASH_OFFSET_ACTIVE, &hash)) {
+ debug("%s: Could not read KBC hash\n", __func__);
+ return 1;
+ }
+
+ if (hash.hash_type == EC_VBOOT_HASH_TYPE_SHA256)
+ printf("type: SHA-256\n");
+ else
+ printf("type: %d\n", hash.hash_type);
+
+ printf("offset: 0x%08x\n", hash.offset);
+ printf("size: 0x%08x\n", hash.size);
+
+ printf("digest: ");
+ for (i = 0; i < hash.digest_size; i++)
+ printf("%02x", hash.hash_digest[i]);
+ printf("\n");
+ } else if (0 == strcmp("reboot", cmd)) {
+ int region;
+ enum ec_reboot_cmd cmd;
+
+ if (argc >= 3 && !strcmp(argv[2], "cold")) {
+ cmd = EC_REBOOT_COLD;
+ } else {
+ region = cros_ec_decode_region(argc - 2, argv + 2);
+ if (region == EC_FLASH_REGION_RO)
+ cmd = EC_REBOOT_JUMP_RO;
+ else if (region == EC_FLASH_REGION_ACTIVE)
+ cmd = EC_REBOOT_JUMP_RW;
+ else
+ return CMD_RET_USAGE;
+ }
+
+ if (cros_ec_reboot(dev, cmd, 0)) {
+ debug("%s: Could not reboot KBC\n", __func__);
+ return 1;
+ }
+ } else if (0 == strcmp("events", cmd)) {
+ ret = do_show_events(dev);
+
+ if (ret)
+ printf("Error: %d\n", ret);
+ } else if (0 == strcmp("clrevents", cmd)) {
+ uint32_t events = 0x7fffffff;
+
+ if (argc >= 3)
+ events = simple_strtol(argv[2], NULL, 0);
+
+ if (cros_ec_clear_host_events(dev, events)) {
+ debug("%s: Could not clear host events\n", __func__);
+ return 1;
+ }
+ } else if (0 == strcmp("read", cmd)) {
+ ret = do_read_write(dev, 0, argc, argv);
+ if (ret > 0)
+ return CMD_RET_USAGE;
+ } else if (0 == strcmp("write", cmd)) {
+ ret = do_read_write(dev, 1, argc, argv);
+ if (ret > 0)
+ return CMD_RET_USAGE;
+ } else if (0 == strcmp("erase", cmd)) {
+ int region = cros_ec_decode_region(argc - 2, argv + 2);
+ uint32_t offset, size;
+
+ if (region == -1)
+ return CMD_RET_USAGE;
+ if (cros_ec_flash_offset(dev, region, &offset, &size)) {
+ debug("%s: Could not read region info\n", __func__);
+ ret = -1;
+ } else {
+ ret = cros_ec_flash_erase(dev, offset, size);
+ if (ret) {
+ debug("%s: Could not erase region\n",
+ __func__);
+ }
+ }
+ } else if (0 == strcmp("regioninfo", cmd)) {
+ int region = cros_ec_decode_region(argc - 2, argv + 2);
+ uint32_t offset, size;
+
+ if (region == -1)
+ return CMD_RET_USAGE;
+ ret = cros_ec_flash_offset(dev, region, &offset, &size);
+ if (ret) {
+ debug("%s: Could not read region info\n", __func__);
+ } else {
+ printf("Region: %s\n", region == EC_FLASH_REGION_RO ?
+ "RO" : "RW");
+ printf("Offset: %x\n", offset);
+ printf("Size: %x\n", size);
+ }
+ } else if (0 == strcmp("flashinfo", cmd)) {
+ struct ec_response_flash_info p;
+
+ ret = cros_ec_read_flashinfo(dev, &p);
+ if (!ret) {
+ printf("Flash size: %u\n", p.flash_size);
+ printf("Write block size: %u\n", p.write_block_size);
+ printf("Erase block size: %u\n", p.erase_block_size);
+ }
+ } else if (0 == strcmp("vbnvcontext", cmd)) {
+ uint8_t block[EC_VBNV_BLOCK_SIZE];
+ char buf[3];
+ int i, len;
+ unsigned long result;
+
+ if (argc <= 2) {
+ ret = cros_ec_read_nvdata(dev, block,
+ EC_VBNV_BLOCK_SIZE);
+ if (!ret) {
+ printf("vbnv_block: ");
+ for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++)
+ printf("%02x", block[i]);
+ putc('\n');
+ }
+ } else {
+ /*
+ * TODO(clchiou): Move this to a utility function as
+ * cmd_spi might want to call it.
+ */
+ memset(block, 0, EC_VBNV_BLOCK_SIZE);
+ len = strlen(argv[2]);
+ buf[2] = '\0';
+ for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) {
+ if (i * 2 >= len)
+ break;
+ buf[0] = argv[2][i * 2];
+ if (i * 2 + 1 >= len)
+ buf[1] = '0';
+ else
+ buf[1] = argv[2][i * 2 + 1];
+ strict_strtoul(buf, 16, &result);
+ block[i] = result;
+ }
+ ret = cros_ec_write_nvdata(dev, block,
+ EC_VBNV_BLOCK_SIZE);
+ }
+ if (ret) {
+ debug("%s: Could not %s VbNvContext\n", __func__,
+ argc <= 2 ? "read" : "write");
+ }
+ } else if (0 == strcmp("test", cmd)) {
+ int result = cros_ec_test(dev);
+
+ if (result)
+ printf("Test failed with error %d\n", result);
+ else
+ puts("Test passed\n");
+ } else if (0 == strcmp("version", cmd)) {
+ struct ec_response_get_version *p;
+ char *build_string;
+
+ ret = cros_ec_read_version(dev, &p);
+ if (!ret) {
+ /* Print versions */
+ printf("RO version: %1.*s\n",
+ (int)sizeof(p->version_string_ro),
+ p->version_string_ro);
+ printf("RW version: %1.*s\n",
+ (int)sizeof(p->version_string_rw),
+ p->version_string_rw);
+ printf("Firmware copy: %s\n",
+ (p->current_image <
+ ARRAY_SIZE(ec_current_image_name) ?
+ ec_current_image_name[p->current_image] :
+ "?"));
+ ret = cros_ec_read_build_info(dev, &build_string);
+ if (!ret)
+ printf("Build info: %s\n", build_string);
+ }
+ } else if (0 == strcmp("ldo", cmd)) {
+ uint8_t index, state;
+ char *endp;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+ index = dectoul(argv[2], &endp);
+ if (*argv[2] == 0 || *endp != 0)
+ return CMD_RET_USAGE;
+ if (argc > 3) {
+ state = dectoul(argv[3], &endp);
+ if (*argv[3] == 0 || *endp != 0)
+ return CMD_RET_USAGE;
+ ret = cros_ec_set_ldo(dev, index, state);
+ } else {
+ ret = cros_ec_get_ldo(dev, index, &state);
+ if (!ret) {
+ printf("LDO%d: %s\n", index,
+ state == EC_LDO_STATE_ON ?
+ "on" : "off");
+ }
+ }
+
+ if (ret) {
+ debug("%s: Could not access LDO%d\n", __func__, index);
+ return ret;
+ }
+ } else if (!strcmp("sku", cmd)) {
+ ret = cros_ec_get_sku_id(dev);
+
+ if (ret >= 0) {
+ printf("%d\n", ret);
+ ret = 0;
+ } else {
+ printf("Error: %d\n", ret);
+ }
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ if (ret < 0) {
+ printf("Error: CROS-EC command failed (error %d)\n", ret);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+U_BOOT_CMD(
+ crosec, 6, 1, do_cros_ec,
+ "CROS-EC utility command",
+ "init Re-init CROS-EC (done on startup automatically)\n"
+ "crosec id Read CROS-EC ID\n"
+ "crosec info Read CROS-EC info\n"
+ "crosec features Read CROS-EC features\n"
+ "crosec switches Read CROS-EC switches\n"
+ "crosec curimage Read CROS-EC current image\n"
+ "crosec hash Read CROS-EC hash\n"
+ "crosec reboot [rw | ro | cold] Reboot CROS-EC\n"
+ "crosec events Read CROS-EC host events\n"
+ "crosec eventsb Read CROS-EC host events_b\n"
+ "crosec clrevents [mask] Clear CROS-EC host events\n"
+ "crosec regioninfo <ro|rw> Read image info\n"
+ "crosec flashinfo Read flash info\n"
+ "crosec erase <ro|rw> Erase EC image\n"
+ "crosec read <ro|rw> <addr> [<size>] Read EC image\n"
+ "crosec write <ro|rw> <addr> [<size>] Write EC image\n"
+ "crosec vbnvcontext [hexstring] Read [write] VbNvContext from EC\n"
+ "crosec ldo <idx> [<state>] Switch/Read LDO state\n"
+ "crosec sku Read board SKU ID\n"
+ "crosec test run tests on cros_ec\n"
+ "crosec version Read CROS-EC version"
+);
diff --git a/cmd/cyclic.c b/cmd/cyclic.c
new file mode 100644
index 00000000000..339dd4a7bce
--- /dev/null
+++ b/cmd/cyclic.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * A general-purpose cyclic execution infrastructure, to allow "small"
+ * (run-time wise) functions to be executed at a specified frequency.
+ * Things like LED blinking or watchdog triggering are examples for such
+ * tasks.
+ *
+ * Copyright (C) 2022 Stefan Roese <sr@denx.de>
+ */
+
+#include <command.h>
+#include <cyclic.h>
+#include <div64.h>
+#include <malloc.h>
+#include <time.h>
+#include <vsprintf.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+
+struct cyclic_demo_info {
+ struct cyclic_info cyclic;
+ uint delay_us;
+};
+
+static void cyclic_demo(struct cyclic_info *c)
+{
+ struct cyclic_demo_info *info = container_of(c, struct cyclic_demo_info, cyclic);
+
+ /* Just a small dummy delay here */
+ udelay(info->delay_us);
+}
+
+static int do_cyclic_demo(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cyclic_demo_info *info;
+ uint time_ms;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ info = malloc(sizeof(struct cyclic_demo_info));
+ if (!info) {
+ printf("out of memory\n");
+ return CMD_RET_FAILURE;
+ }
+
+ time_ms = simple_strtoul(argv[1], NULL, 0);
+ info->delay_us = simple_strtoul(argv[2], NULL, 0);
+
+ /* Register demo cyclic function */
+ cyclic_register(&info->cyclic, cyclic_demo, time_ms * 1000, "cyclic_demo");
+
+ printf("Registered function \"%s\" to be executed all %dms\n",
+ "cyclic_demo", time_ms);
+
+ return 0;
+}
+
+static int do_cyclic_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cyclic_info *cyclic;
+ struct hlist_node *tmp;
+ u64 cnt, freq;
+
+ hlist_for_each_entry_safe(cyclic, tmp, cyclic_get_list(), list) {
+ cnt = cyclic->run_cnt * 1000000ULL * 100ULL;
+ freq = lldiv(cnt, timer_get_us() - cyclic->start_time_us);
+ printf("function: %s, cpu-time: %lld us, frequency: %lld.%02d times/s\n",
+ cyclic->name, cyclic->cpu_time_us,
+ lldiv(freq, 100), do_div(freq, 100));
+ }
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(cyclic,
+ "demo <cycletime_ms> <delay_us> - register cyclic demo function\n"
+ "cyclic list - list cyclic functions\n");
+
+U_BOOT_CMD_WITH_SUBCMDS(cyclic, "Cyclic", cyclic_help_text,
+ U_BOOT_SUBCMD_MKENT(demo, 3, 1, do_cyclic_demo),
+ U_BOOT_SUBCMD_MKENT(list, 1, 1, do_cyclic_list));
diff --git a/cmd/date.c b/cmd/date.c
new file mode 100644
index 00000000000..8614f022761
--- /dev/null
+++ b/cmd/date.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2001
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+/*
+ * RTC, Date & Time support: get and set date & time
+ */
+#include <command.h>
+#include <dm.h>
+#include <rtc.h>
+#include <i2c.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char * const weekdays[] = {
+ "Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur",
+};
+
+int mk_date (const char *, struct rtc_time *);
+
+static struct rtc_time default_tm = { 0, 0, 0, 1, 1, 2000, 6, 0, 0 };
+
+static int do_date(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct rtc_time tm;
+ int rcode = 0;
+ int old_bus __maybe_unused;
+
+ /* switch to correct I2C bus */
+ struct udevice *dev;
+
+ rcode = uclass_get_device_by_seq(UCLASS_RTC, 0, &dev);
+ if (rcode) {
+ rcode = uclass_get_device(UCLASS_RTC, 0, &dev);
+ if (rcode) {
+ printf("Cannot find RTC: err=%d\n", rcode);
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ switch (argc) {
+ case 2: /* set date & time */
+ if (strcmp(argv[1],"reset") == 0) {
+ puts ("Reset RTC...\n");
+ rcode = dm_rtc_reset(dev);
+ if (!rcode)
+ rcode = dm_rtc_set(dev, &default_tm);
+ if (rcode)
+ puts("## Failed to set date after RTC reset\n");
+ } else {
+ /* initialize tm with current time */
+ rcode = dm_rtc_get(dev, &tm);
+ if (!rcode) {
+ /* insert new date & time */
+ if (mk_date(argv[1], &tm) != 0) {
+ puts ("## Bad date format\n");
+ break;
+ }
+ /* and write to RTC */
+ rcode = dm_rtc_set(dev, &tm);
+ if (rcode) {
+ printf("## Set date failed: err=%d\n",
+ rcode);
+ }
+ } else {
+ puts("## Get date failed\n");
+ }
+ }
+ fallthrough;
+ case 1: /* get date & time */
+ rcode = dm_rtc_get(dev, &tm);
+ if (rcode) {
+ puts("## Get date failed\n");
+ break;
+ }
+
+ printf ("Date: %4d-%02d-%02d (%sday) Time: %2d:%02d:%02d\n",
+ tm.tm_year, tm.tm_mon, tm.tm_mday,
+ (tm.tm_wday<0 || tm.tm_wday>6) ?
+ "unknown " : weekdays[tm.tm_wday],
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+ break;
+ default:
+ rcode = CMD_RET_USAGE;
+ }
+
+ return rcode ? CMD_RET_FAILURE : 0;
+}
+
+/*
+ * simple conversion of two-digit string with error checking
+ */
+static int cnvrt2 (const char *str, int *valp)
+{
+ int val;
+
+ if ((*str < '0') || (*str > '9'))
+ return (-1);
+
+ val = *str - '0';
+
+ ++str;
+
+ if ((*str < '0') || (*str > '9'))
+ return (-1);
+
+ *valp = 10 * val + (*str - '0');
+
+ return (0);
+}
+
+/*
+ * Convert date string: MMDDhhmm[[CC]YY][.ss]
+ *
+ * Some basic checking for valid values is done, but this will not catch
+ * all possible error conditions.
+ */
+int mk_date (const char *datestr, struct rtc_time *tmp)
+{
+ int len, val;
+ char *ptr;
+
+ ptr = strchr(datestr, '.');
+ len = strlen(datestr);
+
+ /* Set seconds */
+ if (ptr) {
+ int sec;
+
+ ptr++;
+ if ((len - (ptr - datestr)) != 2)
+ return (-1);
+
+ len -= 3;
+
+ if (cnvrt2 (ptr, &sec))
+ return (-1);
+
+ tmp->tm_sec = sec;
+ } else {
+ tmp->tm_sec = 0;
+ }
+
+ if (len == 12) { /* MMDDhhmmCCYY */
+ int year, century;
+
+ if (cnvrt2 (datestr+ 8, &century) ||
+ cnvrt2 (datestr+10, &year) ) {
+ return (-1);
+ }
+ tmp->tm_year = 100 * century + year;
+ } else if (len == 10) { /* MMDDhhmmYY */
+ int year, century;
+
+ century = tmp->tm_year / 100;
+ if (cnvrt2 (datestr+ 8, &year))
+ return (-1);
+ tmp->tm_year = 100 * century + year;
+ }
+
+ switch (len) {
+ case 8: /* MMDDhhmm */
+ /* fall thru */
+ case 10: /* MMDDhhmmYY */
+ /* fall thru */
+ case 12: /* MMDDhhmmCCYY */
+ if (cnvrt2 (datestr+0, &val) ||
+ val > 12) {
+ break;
+ }
+ tmp->tm_mon = val;
+ if (cnvrt2 (datestr+2, &val) ||
+ val > ((tmp->tm_mon==2) ? 29 : 31)) {
+ break;
+ }
+ tmp->tm_mday = val;
+
+ if (cnvrt2 (datestr+4, &val) ||
+ val > 23) {
+ break;
+ }
+ tmp->tm_hour = val;
+
+ if (cnvrt2 (datestr+6, &val) ||
+ val > 59) {
+ break;
+ }
+ tmp->tm_min = val;
+
+ /* calculate day of week */
+ rtc_calc_weekday(tmp);
+
+ return (0);
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+/***************************************************/
+
+U_BOOT_CMD(
+ date, 2, 1, do_date,
+ "get/set/reset date & time",
+ "[MMDDhhmm[[CC]YY][.ss]]\ndate reset\n"
+ " - without arguments: print date & time\n"
+ " - with numeric argument: set the system date & time\n"
+ " - with 'reset' argument: reset the RTC"
+);
diff --git a/cmd/demo.c b/cmd/demo.c
new file mode 100644
index 00000000000..5c422ac165b
--- /dev/null
+++ b/cmd/demo.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <dm-demo.h>
+#include <mapmem.h>
+#include <asm/io.h>
+
+struct udevice *demo_dev;
+
+static int do_demo_hello(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ch = 0;
+
+ if (argc)
+ ch = *argv[0];
+
+ return demo_hello(demo_dev, ch);
+}
+
+static int do_demo_status(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int status;
+ int ret;
+
+ ret = demo_status(demo_dev, &status);
+ if (ret)
+ return ret;
+
+ printf("Status: %d\n", status);
+
+ return 0;
+}
+
+static int do_demo_light(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int light;
+ int ret;
+
+ if (argc) {
+ light = hextoul(argv[0], NULL);
+ ret = demo_set_light(demo_dev, light);
+ } else {
+ ret = demo_get_light(demo_dev);
+ if (ret >= 0) {
+ printf("Light: %x\n", ret);
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+int do_demo_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct udevice *dev;
+ int i, ret, err = 0;
+
+ puts("Demo uclass entries:\n");
+
+ for (i = 0, ret = uclass_first_device_check(UCLASS_DEMO, &dev);
+ dev;
+ ret = uclass_next_device_check(&dev)) {
+ printf("entry %d - instance %08x, ops %08x, plat %08x, status %i\n",
+ i++, (uint)map_to_sysmem(dev),
+ (uint)map_to_sysmem(dev->driver->ops),
+ (uint)map_to_sysmem(dev_get_plat(dev)),
+ ret);
+ if (ret)
+ err = ret;
+ }
+
+ return cmd_process_error(cmdtp, err);
+}
+
+static struct cmd_tbl demo_commands[] = {
+ U_BOOT_CMD_MKENT(list, 0, 1, do_demo_list, "", ""),
+ U_BOOT_CMD_MKENT(hello, 2, 1, do_demo_hello, "", ""),
+ U_BOOT_CMD_MKENT(light, 2, 1, do_demo_light, "", ""),
+ U_BOOT_CMD_MKENT(status, 1, 1, do_demo_status, "", ""),
+};
+
+static int do_demo(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *demo_cmd;
+ int devnum = 0;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ demo_cmd = find_cmd_tbl(argv[1], demo_commands,
+ ARRAY_SIZE(demo_commands));
+ argc -= 2;
+ argv += 2;
+
+ if ((!demo_cmd || argc > demo_cmd->maxargs) ||
+ ((demo_cmd->name[0] != 'l') && (argc < 1)))
+ return CMD_RET_USAGE;
+
+ if (argc) {
+ devnum = dectoul(argv[0], NULL);
+ ret = uclass_get_device(UCLASS_DEMO, devnum, &demo_dev);
+ if (ret)
+ return cmd_process_error(cmdtp, ret);
+ argc--;
+ argv++;
+ } else {
+ demo_dev = NULL;
+ if (demo_cmd->cmd != do_demo_list)
+ return CMD_RET_USAGE;
+ }
+
+ ret = demo_cmd->cmd(demo_cmd, flag, argc, argv);
+
+ return cmd_process_error(demo_cmd, ret);
+}
+
+U_BOOT_CMD(
+ demo, 4, 1, do_demo,
+ "Driver model (dm) demo operations",
+ "list List available demo devices\n"
+ "demo hello <num> [<char>] Say hello\n"
+ "demo light [<num>] Set or get the lights\n"
+ "demo status <num> Get demo device status\n"
+);
diff --git a/cmd/dfu.c b/cmd/dfu.c
new file mode 100644
index 00000000000..46f0190588e
--- /dev/null
+++ b/cmd/dfu.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * cmd_dfu.c -- dfu command
+ *
+ * Copyright (C) 2015
+ * Lukasz Majewski <l.majewski@majess.pl>
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * authors: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ * Lukasz Majewski <l.majewski@samsung.com>
+ */
+
+#include <command.h>
+#include <watchdog.h>
+#include <dfu.h>
+#include <console.h>
+#include <g_dnl.h>
+#include <usb.h>
+#include <net.h>
+
+static int do_dfu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+#ifdef CONFIG_DFU_OVER_USB
+ char *usb_controller = argv[1];
+#endif
+ char *interface = NULL;
+ char *devstring = NULL;
+#if defined(CONFIG_DFU_TIMEOUT) || defined(CONFIG_DFU_OVER_TFTP)
+ unsigned long value = 0;
+#endif
+ if (argc >= 4) {
+ interface = argv[2];
+ devstring = argv[3];
+ }
+
+#if defined(CONFIG_DFU_TIMEOUT) || defined(CONFIG_DFU_OVER_TFTP)
+ if (argc == 5 || argc == 3)
+ value = simple_strtoul(argv[argc - 1], NULL, 0);
+#endif
+
+ int ret = 0;
+#ifdef CONFIG_DFU_OVER_TFTP
+ if (!strcmp(argv[1], "tftp"))
+ return update_tftp(value, interface, devstring);
+#endif
+ ret = dfu_init_env_entities(interface, devstring);
+ if (ret)
+ goto done;
+
+#ifdef CONFIG_DFU_TIMEOUT
+ dfu_set_timeout(value * 1000);
+#endif
+
+ ret = CMD_RET_SUCCESS;
+ if (strcmp(argv[argc - 1], "list") == 0) {
+ dfu_show_entities();
+ goto done;
+ }
+
+#ifdef CONFIG_DFU_OVER_USB
+ int controller_index = simple_strtoul(usb_controller, NULL, 0);
+ bool retry = false;
+ do {
+ ret = run_usb_dnl_gadget(controller_index, "usb_dnl_dfu");
+
+ if (dfu_reinit_needed) {
+ dfu_free_entities();
+ ret = dfu_init_env_entities(interface, devstring);
+ if (ret)
+ goto done;
+ retry = true;
+ }
+ } while (retry);
+
+#endif
+done:
+ dfu_free_entities();
+ return ret;
+}
+
+U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu,
+ "Device Firmware Upgrade",
+ ""
+#ifdef CONFIG_DFU_OVER_USB
+#ifdef CONFIG_DFU_TIMEOUT
+ "<USB_controller> [<interface> <dev>] [<timeout>] [list]\n"
+#else
+ "<USB_controller> [<interface> <dev>] [list]\n"
+#endif
+ " - device firmware upgrade via <USB_controller>\n"
+ " on device <dev>, attached to interface\n"
+ " <interface>\n"
+#ifdef CONFIG_DFU_TIMEOUT
+ " [<timeout>] - specify inactivity timeout in seconds\n"
+#endif
+#endif
+ " [list] - list available alt settings\n"
+#ifdef CONFIG_DFU_OVER_TFTP
+#ifdef CONFIG_DFU_OVER_USB
+ "dfu "
+#endif
+ "tftp [<interface> <dev>] [<addr>]\n"
+ " - device firmware upgrade via TFTP\n"
+ " on device <dev>, attached to interface\n"
+ " <interface>\n"
+ " [<addr>] - address where FIT image has been stored\n"
+#endif
+);
diff --git a/cmd/diag.c b/cmd/diag.c
new file mode 100644
index 00000000000..4a88ab00a07
--- /dev/null
+++ b/cmd/diag.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+/*
+ * Diagnostics support
+ */
+#include <command.h>
+#include <post.h>
+#include <linux/string.h>
+
+int do_diag(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ unsigned int i;
+
+ if (argc == 1 || strcmp (argv[1], "run") != 0) {
+ /* List test info */
+ if (argc == 1) {
+ puts ("Available hardware tests:\n");
+ post_info (NULL);
+ puts ("Use 'diag [<test1> [<test2> ...]]'"
+ " to get more info.\n");
+ puts ("Use 'diag run [<test1> [<test2> ...]]'"
+ " to run tests.\n");
+ } else {
+ for (i = 1; i < argc; i++) {
+ if (post_info (argv[i]) != 0)
+ printf ("%s - no such test\n", argv[i]);
+ }
+ }
+ } else {
+ /* Run tests */
+ if (argc == 2) {
+ post_run (NULL, POST_RAM | POST_MANUAL);
+ } else {
+ for (i = 2; i < argc; i++) {
+ if (post_run (argv[i], POST_RAM | POST_MANUAL) != 0)
+ printf ("%s - unable to execute the test\n",
+ argv[i]);
+ }
+ }
+ }
+
+ return 0;
+}
+/***************************************************/
+
+U_BOOT_CMD(
+ diag, CONFIG_SYS_MAXARGS, 0, do_diag,
+ "perform board diagnostics",
+ " - print list of available tests\n"
+ "diag [test1 [test2]]\n"
+ " - print information about specified tests\n"
+ "diag run - run all available tests\n"
+ "diag run [test1 [test2]]\n"
+ " - run specified tests"
+);
diff --git a/cmd/disk.c b/cmd/disk.c
new file mode 100644
index 00000000000..2efc3ca4b1a
--- /dev/null
+++ b/cmd/disk.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000-2011
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+#include <bootstage.h>
+#include <command.h>
+#include <cpu_func.h>
+#include <image.h>
+#include <log.h>
+#include <part.h>
+
+int common_diskboot(struct cmd_tbl *cmdtp, const char *intf, int argc,
+ char *const argv[])
+{
+ __maybe_unused int dev;
+ int part;
+ ulong addr = CONFIG_SYS_LOAD_ADDR;
+ ulong cnt;
+ struct disk_partition info;
+#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
+ struct legacy_img_hdr *hdr;
+#endif
+ struct blk_desc *dev_desc;
+
+#if CONFIG_IS_ENABLED(FIT)
+ const void *fit_hdr = NULL;
+#endif
+
+ bootstage_mark(BOOTSTAGE_ID_IDE_START);
+ if (argc > 3) {
+ bootstage_error(BOOTSTAGE_ID_IDE_ADDR);
+ return CMD_RET_USAGE;
+ }
+ bootstage_mark(BOOTSTAGE_ID_IDE_ADDR);
+
+ if (argc > 1)
+ addr = hextoul(argv[1], NULL);
+
+ bootstage_mark(BOOTSTAGE_ID_IDE_BOOT_DEVICE);
+
+ part = blk_get_device_part_str(intf, cmd_arg2(argc, argv),
+ &dev_desc, &info, 1);
+ if (part < 0) {
+ bootstage_error(BOOTSTAGE_ID_IDE_TYPE);
+ return 1;
+ }
+
+ dev = dev_desc->devnum;
+ bootstage_mark(BOOTSTAGE_ID_IDE_TYPE);
+
+ printf("\nLoading from %s device %d, partition %d: "
+ "Name: %.32s Type: %.32s\n", intf, dev, part, info.name,
+ info.type);
+
+ debug("First Block: " LBAFU ", # of blocks: " LBAFU
+ ", Block Size: %ld\n",
+ info.start, info.size, info.blksz);
+
+ if (blk_dread(dev_desc, info.start, 1, (ulong *)addr) != 1) {
+ printf("** Read error on %d:%d\n", dev, part);
+ bootstage_error(BOOTSTAGE_ID_IDE_PART_READ);
+ return 1;
+ }
+ bootstage_mark(BOOTSTAGE_ID_IDE_PART_READ);
+
+ switch (genimg_get_format((void *) addr)) {
+#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
+ case IMAGE_FORMAT_LEGACY:
+ hdr = (struct legacy_img_hdr *)addr;
+
+ bootstage_mark(BOOTSTAGE_ID_IDE_FORMAT);
+
+ if (!image_check_hcrc(hdr)) {
+ puts("\n** Bad Header Checksum **\n");
+ bootstage_error(BOOTSTAGE_ID_IDE_CHECKSUM);
+ return 1;
+ }
+ bootstage_mark(BOOTSTAGE_ID_IDE_CHECKSUM);
+
+ image_print_contents(hdr);
+
+ cnt = image_get_image_size(hdr);
+ break;
+#endif
+#if CONFIG_IS_ENABLED(FIT)
+ case IMAGE_FORMAT_FIT:
+ fit_hdr = (const void *) addr;
+ puts("Fit image detected...\n");
+
+ cnt = fit_get_size(fit_hdr);
+ break;
+#endif
+ default:
+ bootstage_error(BOOTSTAGE_ID_IDE_FORMAT);
+ puts("** Unknown image type\n");
+ return 1;
+ }
+
+ cnt += info.blksz - 1;
+ cnt /= info.blksz;
+ cnt -= 1;
+
+ if (blk_dread(dev_desc, info.start + 1, cnt,
+ (ulong *)(addr + info.blksz)) != cnt) {
+ printf("** Read error on %d:%d\n", dev, part);
+ bootstage_error(BOOTSTAGE_ID_IDE_READ);
+ return 1;
+ }
+ bootstage_mark(BOOTSTAGE_ID_IDE_READ);
+
+#if CONFIG_IS_ENABLED(FIT)
+ /* This cannot be done earlier,
+ * we need complete FIT image in RAM first */
+ if (genimg_get_format((void *) addr) == IMAGE_FORMAT_FIT) {
+ if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) {
+ bootstage_error(BOOTSTAGE_ID_IDE_FIT_READ);
+ puts("** Bad FIT image format\n");
+ return 1;
+ }
+ bootstage_mark(BOOTSTAGE_ID_IDE_FIT_READ_OK);
+ }
+#endif
+
+ flush_cache(addr, (cnt+1)*info.blksz);
+
+ /* Loading ok, update default load address */
+ image_load_addr = addr;
+
+ return bootm_maybe_autostart(cmdtp, argv[0]);
+}
diff --git a/cmd/dm.c b/cmd/dm.c
new file mode 100644
index 00000000000..1f212c0f030
--- /dev/null
+++ b/cmd/dm.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Marek Vasut <marex@denx.de>
+ */
+
+#include <command.h>
+#include <dm/root.h>
+#include <dm/util.h>
+#include <linux/string.h>
+
+static int do_dm_dump_driver_compat(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ dm_dump_driver_compat();
+
+ return 0;
+}
+
+static int do_dm_dump_devres(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ dm_dump_devres();
+
+ return 0;
+}
+
+static int do_dm_dump_drivers(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ dm_dump_drivers();
+
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(DM_STATS)
+static int do_dm_dump_mem(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct dm_stats mem;
+
+ dm_get_mem(&mem);
+ dm_dump_mem(&mem);
+
+ return 0;
+}
+#endif /* DM_STATS */
+
+static int do_dm_dump_static_driver_info(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ dm_dump_static_driver_info();
+
+ return 0;
+}
+
+static int do_dm_dump_tree(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool extended = false, sort = false;
+ char *device = NULL;
+
+ for (; argc > 1; argc--, argv++) {
+ if (argv[1][0] != '-')
+ break;
+
+ if (!strcmp(argv[1], "-e")) {
+ extended = true;
+ } else if (!strcmp(argv[1], "-s")) {
+ sort = true;
+ } else {
+ printf("Unknown parameter: %s\n", argv[1]);
+ return 0;
+ }
+ }
+ if (argc > 1)
+ device = argv[1];
+
+ dm_dump_tree(device, extended, sort);
+
+ return 0;
+}
+
+static int do_dm_dump_uclass(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool extended = false;
+ char *uclass = NULL;
+
+ if (argc > 1) {
+ if (!strcmp(argv[1], "-e")) {
+ extended = true;
+ argc--;
+ argv++;
+ }
+ if (argc > 1)
+ uclass = argv[1];
+ }
+
+ dm_dump_uclass(uclass, extended);
+
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(DM_STATS)
+#define DM_MEM_HELP "dm mem Provide a summary of memory usage\n"
+#define DM_MEM U_BOOT_SUBCMD_MKENT(mem, 1, 1, do_dm_dump_mem),
+#else
+#define DM_MEM_HELP
+#define DM_MEM
+#endif
+
+U_BOOT_LONGHELP(dm,
+ "compat Dump list of drivers with compatibility strings\n"
+ "dm devres Dump list of device resources for each device\n"
+ "dm drivers Dump list of drivers with uclass and instances\n"
+ DM_MEM_HELP
+ "dm static Dump list of drivers with static platform data\n"
+ "dm tree [-s][-e][name] Dump tree of driver model devices (-s=sort)\n"
+ "dm uclass [-e][name] Dump list of instances for each uclass");
+
+U_BOOT_CMD_WITH_SUBCMDS(dm, "Driver model low level access", dm_help_text,
+ U_BOOT_SUBCMD_MKENT(compat, 1, 1, do_dm_dump_driver_compat),
+ U_BOOT_SUBCMD_MKENT(devres, 1, 1, do_dm_dump_devres),
+ U_BOOT_SUBCMD_MKENT(drivers, 1, 1, do_dm_dump_drivers),
+ DM_MEM
+ U_BOOT_SUBCMD_MKENT(static, 1, 1, do_dm_dump_static_driver_info),
+ U_BOOT_SUBCMD_MKENT(tree, 4, 1, do_dm_dump_tree),
+ U_BOOT_SUBCMD_MKENT(uclass, 3, 1, do_dm_dump_uclass));
diff --git a/cmd/echo.c b/cmd/echo.c
new file mode 100644
index 00000000000..d1346504cfb
--- /dev/null
+++ b/cmd/echo.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2000-2009
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <linux/string.h>
+
+static int do_echo(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int i = 1;
+ bool space = false;
+ bool newline = true;
+
+ if (argc > 1) {
+ if (!strcmp(argv[1], "-n")) {
+ newline = false;
+ ++i;
+ }
+ }
+
+ for (; i < argc; ++i) {
+ if (space) {
+ putc(' ');
+ }
+ puts(argv[i]);
+ space = true;
+ }
+
+ if (newline)
+ putc('\n');
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ echo, CONFIG_SYS_MAXARGS, 1, do_echo,
+ "echo args to console",
+ "[-n] [args..]\n"
+ " - echo args to console; -n suppresses newline"
+);
diff --git a/cmd/eeprom.c b/cmd/eeprom.c
new file mode 100644
index 00000000000..cf89cfce3e4
--- /dev/null
+++ b/cmd/eeprom.c
@@ -0,0 +1,577 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000, 2001
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+/*
+ * Support for read and write access to EEPROM like memory devices. This
+ * includes regular EEPROM as well as FRAM (ferroelectic nonvolaile RAM).
+ * FRAM devices read and write data at bus speed. In particular, there is no
+ * write delay. Also, there is no limit imposed on the number of bytes that can
+ * be transferred with a single read or write.
+ *
+ * Use the following configuration options to ensure no unneeded performance
+ * degradation (typical for EEPROM) is incured for FRAM memory:
+ *
+ * #define CONFIG_SYS_I2C_FRAM
+ * Set CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS to 0
+ *
+ */
+
+#include <config.h>
+#include <command.h>
+#include <dm.h>
+#include <eeprom.h>
+#include <i2c.h>
+#include <i2c_eeprom.h>
+#include <eeprom_layout.h>
+#include <vsprintf.h>
+#include <linux/delay.h>
+
+#ifndef I2C_RXTX_LEN
+#define I2C_RXTX_LEN 128
+#endif
+
+#define EEPROM_PAGE_SIZE (1 << CONFIG_SYS_EEPROM_PAGE_WRITE_BITS)
+#define EEPROM_PAGE_OFFSET(x) ((x) & (EEPROM_PAGE_SIZE - 1))
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+static int eeprom_i2c_bus;
+#endif
+
+__weak int eeprom_write_enable(unsigned dev_addr, int state)
+{
+ return 0;
+}
+
+void eeprom_init(int bus)
+{
+ /* I2C EEPROM */
+#if CONFIG_IS_ENABLED(DM_I2C)
+ eeprom_i2c_bus = bus;
+#elif CONFIG_IS_ENABLED(SYS_I2C_LEGACY)
+ if (bus >= 0)
+ i2c_set_bus_num(bus);
+ i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+#endif
+}
+
+/*
+ * for CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 2 (16-bit EEPROM address) offset is
+ * 0x000nxxxx for EEPROM address selectors at n, offset xxxx in EEPROM.
+ *
+ * for CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 1 (8-bit EEPROM page address) offset is
+ * 0x00000nxx for EEPROM address selectors and page number at n.
+ */
+static int eeprom_addr(unsigned dev_addr, unsigned offset, uchar *addr)
+{
+ unsigned blk_off;
+ int alen;
+
+ blk_off = offset & 0xff; /* block offset */
+#if CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 1
+ addr[0] = offset >> 8; /* block number */
+ addr[1] = blk_off; /* block offset */
+ alen = 2;
+#else
+ addr[0] = offset >> 16; /* block number */
+ addr[1] = offset >> 8; /* upper address octet */
+ addr[2] = blk_off; /* lower address octet */
+ alen = 3;
+#endif /* CONFIG_SYS_I2C_EEPROM_ADDR_LEN */
+
+ addr[0] |= dev_addr; /* insert device address */
+
+ return alen;
+}
+
+static int eeprom_len(unsigned offset, unsigned end)
+{
+ unsigned len = end - offset;
+
+ /*
+ * For a FRAM device there is no limit on the number of the
+ * bytes that can be accessed with the single read or write
+ * operation.
+ */
+#if !defined(CONFIG_SYS_I2C_FRAM)
+ unsigned blk_off = offset & 0xff;
+ unsigned maxlen = EEPROM_PAGE_SIZE - EEPROM_PAGE_OFFSET(blk_off);
+
+ if (maxlen > I2C_RXTX_LEN)
+ maxlen = I2C_RXTX_LEN;
+
+ if (len > maxlen)
+ len = maxlen;
+#endif
+
+ return len;
+}
+
+static int eeprom_rw_block(unsigned offset, uchar *addr, unsigned alen,
+ uchar *buffer, unsigned len, bool read)
+{
+ int ret = 0;
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *dev;
+
+ ret = i2c_get_chip_for_busnum(eeprom_i2c_bus, addr[0],
+ alen - 1, &dev);
+ if (ret) {
+ printf("%s: Cannot find udev for a bus %d\n", __func__,
+ eeprom_i2c_bus);
+ return CMD_RET_FAILURE;
+ }
+
+ if (read)
+ ret = dm_i2c_read(dev, offset, buffer, len);
+ else
+ ret = dm_i2c_write(dev, offset, buffer, len);
+
+#else /* Non DM I2C support - will be removed */
+
+ if (read)
+ ret = i2c_read(addr[0], offset, alen - 1, buffer, len);
+ else
+ ret = i2c_write(addr[0], offset, alen - 1, buffer, len);
+#endif /* CONFIG_DM_I2C */
+ if (ret)
+ ret = CMD_RET_FAILURE;
+
+ return ret;
+}
+
+static int eeprom_rw(unsigned dev_addr, unsigned offset, uchar *buffer,
+ unsigned cnt, bool read)
+{
+ unsigned end = offset + cnt;
+ unsigned alen, len;
+ int rcode = 0;
+ uchar addr[3];
+
+#if !CONFIG_IS_ENABLED(DM_I2C) && defined(CONFIG_SYS_I2C_EEPROM_BUS)
+ eeprom_init(CONFIG_SYS_I2C_EEPROM_BUS);
+#endif
+
+ while (offset < end) {
+ alen = eeprom_addr(dev_addr, offset, addr);
+
+ len = eeprom_len(offset, end);
+
+ rcode = eeprom_rw_block(offset, addr, alen, buffer, len, read);
+
+ buffer += len;
+ offset += len;
+
+#if CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS > 0
+ if (!read)
+ udelay(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS * 1000);
+#endif
+ }
+
+ return rcode;
+}
+
+int eeprom_read(unsigned dev_addr, unsigned offset, uchar *buffer, unsigned cnt)
+{
+ /*
+ * Read data until done or would cross a page boundary.
+ * We must write the address again when changing pages
+ * because the next page may be in a different device.
+ */
+ return eeprom_rw(dev_addr, offset, buffer, cnt, 1);
+}
+
+int eeprom_write(unsigned dev_addr, unsigned offset,
+ uchar *buffer, unsigned cnt)
+{
+ int ret;
+
+ eeprom_write_enable(dev_addr, 1);
+
+ /*
+ * Write data until done or would cross a write page boundary.
+ * We must write the address again when changing pages
+ * because the address counter only increments within a page.
+ */
+ ret = eeprom_rw(dev_addr, offset, buffer, cnt, 0);
+
+ eeprom_write_enable(dev_addr, 0);
+ return ret;
+}
+
+static long parse_numeric_param(char *str)
+{
+ char *endptr;
+ long value = simple_strtol(str, &endptr, 16);
+
+ return (*endptr != '\0') ? -1 : value;
+}
+
+struct eeprom_dev_spec {
+#if CONFIG_IS_ENABLED(I2C_EEPROM)
+ struct udevice *dev;
+#endif
+ int i2c_bus;
+ ulong i2c_addr;
+};
+
+static void eeprom_dev_spec_init(struct eeprom_dev_spec *dev)
+{
+#if CONFIG_IS_ENABLED(I2C_EEPROM)
+ if (!dev->dev)
+#endif
+ eeprom_init(dev->i2c_bus);
+}
+
+static int eeprom_dev_spec_read(struct eeprom_dev_spec *dev,
+ unsigned offset, uchar *buffer, unsigned cnt)
+{
+#if CONFIG_IS_ENABLED(I2C_EEPROM)
+ if (dev->dev)
+ return i2c_eeprom_read(dev->dev, offset, buffer, cnt);
+#endif
+ return eeprom_read(dev->i2c_addr, offset, buffer, cnt);
+}
+
+static int eeprom_dev_spec_write(struct eeprom_dev_spec *dev,
+ unsigned offset, uchar *buffer, unsigned cnt)
+{
+#if CONFIG_IS_ENABLED(I2C_EEPROM)
+ if (dev->dev)
+ return i2c_eeprom_write(dev->dev, offset, buffer, cnt);
+#endif
+ return eeprom_write(dev->i2c_addr, offset, buffer, cnt);
+}
+
+/**
+ * parse_eeprom_dev_spec - parse the eeprom device specifier
+ *
+ * @dev: pointer to eeprom device specifier
+ * @argc: count of command line arguments that can be used to parse
+ * the device specifier
+ * @argv: command line arguments left to parse
+ *
+ * @returns: number of arguments parsed or CMD_RET_USAGE if error
+ */
+static int parse_eeprom_dev_spec(struct eeprom_dev_spec *dev, int argc,
+ char *const argv[])
+{
+#if CONFIG_IS_ENABLED(I2C_EEPROM)
+ if (argc == 0) {
+ if (!uclass_first_device_err(UCLASS_I2C_EEPROM, &dev->dev))
+ return 0;
+ }
+
+ if (argc == 1) {
+ if (!uclass_get_device_by_name(UCLASS_I2C_EEPROM, argv[0],
+ &dev->dev))
+ return 1;
+
+ /*
+ * If we could not find the device by name and the parameter is
+ * not numeric (and so won't be handled later), fail.
+ */
+ if (parse_numeric_param(argv[0]) == -1) {
+ printf("Can't get eeprom device: %s\n", argv[0]);
+ return CMD_RET_USAGE;
+ }
+ }
+#endif
+
+#ifdef CONFIG_SYS_I2C_EEPROM_ADDR
+ if (argc == 0) {
+ dev->i2c_bus = -1;
+ dev->i2c_addr = CONFIG_SYS_I2C_EEPROM_ADDR;
+
+ return 0;
+ }
+#endif
+ if (argc == 1) {
+ dev->i2c_bus = -1;
+ dev->i2c_addr = parse_numeric_param(argv[0]);
+
+ return 1;
+ }
+
+ if (argc == 2) {
+ dev->i2c_bus = parse_numeric_param(argv[0]);
+ dev->i2c_addr = parse_numeric_param(argv[1]);
+
+ return 2;
+ }
+
+ return CMD_RET_USAGE;
+}
+
+#ifdef CONFIG_CMD_EEPROM_LAYOUT
+
+#ifdef CONFIG_EEPROM_LAYOUT_VERSIONS
+__weak int eeprom_parse_layout_version(char *str)
+{
+ return LAYOUT_VERSION_UNRECOGNIZED;
+}
+#endif
+
+static unsigned char eeprom_buf[CONFIG_SYS_EEPROM_SIZE];
+
+#endif
+
+enum eeprom_action {
+ EEPROM_LIST,
+ EEPROM_READ,
+ EEPROM_WRITE,
+ EEPROM_PRINT,
+ EEPROM_UPDATE,
+ EEPROM_ACTION_INVALID,
+};
+
+static enum eeprom_action parse_action(char *cmd)
+{
+#if CONFIG_IS_ENABLED(I2C_EEPROM)
+ if (!strncmp(cmd, "list", 4))
+ return EEPROM_LIST;
+#endif
+ if (!strncmp(cmd, "read", 4))
+ return EEPROM_READ;
+ if (!strncmp(cmd, "write", 5))
+ return EEPROM_WRITE;
+#ifdef CONFIG_CMD_EEPROM_LAYOUT
+ if (!strncmp(cmd, "print", 5))
+ return EEPROM_PRINT;
+ if (!strncmp(cmd, "update", 6))
+ return EEPROM_UPDATE;
+#endif
+
+ return EEPROM_ACTION_INVALID;
+}
+
+#if CONFIG_IS_ENABLED(I2C_EEPROM)
+static int do_eeprom_list(void)
+{
+ struct udevice *dev;
+ struct uclass *uc;
+ int err;
+
+ err = uclass_get(UCLASS_I2C_EEPROM, &uc);
+ if (err)
+ return CMD_RET_FAILURE;
+
+ uclass_foreach_dev(dev, uc)
+ printf("%s (%s)\n", dev->name, dev->driver->name);
+
+ return CMD_RET_SUCCESS;
+}
+#endif
+
+static int do_eeprom_rw(struct eeprom_dev_spec *dev, bool read,
+ ulong addr, ulong off, ulong cnt)
+{
+ const char *const fmt =
+ "\nEEPROM @0x%lX %s: addr 0x%08lx off 0x%04lx count %ld ... ";
+ uchar *memloc = (uchar *)addr;
+ int ret;
+
+ printf(fmt, dev->i2c_addr, read ? "read" : "write", addr, off, cnt);
+ if (read)
+ ret = eeprom_dev_spec_read(dev, off, memloc, cnt);
+ else
+ ret = eeprom_dev_spec_write(dev, off, memloc, cnt);
+ puts("done\n");
+
+ return ret;
+}
+
+#ifdef CONFIG_CMD_EEPROM_LAYOUT
+
+static int do_eeprom_layout(struct eeprom_dev_spec *dev, int layout_ver,
+ struct eeprom_layout *layout)
+{
+ eeprom_layout_setup(layout, eeprom_buf, CONFIG_SYS_EEPROM_SIZE,
+ layout_ver);
+
+ return eeprom_dev_spec_read(dev, 0, eeprom_buf, layout->data_size);
+}
+
+static int do_eeprom_print(struct eeprom_dev_spec *dev, int layout_ver)
+{
+ struct eeprom_layout layout;
+ int ret;
+
+ ret = do_eeprom_layout(dev, layout_ver, &layout);
+ if (ret)
+ return ret;
+
+ layout.print(&layout);
+
+ return 0;
+}
+
+static int do_eeprom_update(struct eeprom_dev_spec *dev, int layout_ver,
+ char *key, char *value)
+{
+ struct eeprom_layout layout;
+ int ret;
+
+ ret = do_eeprom_layout(dev, layout_ver, &layout);
+ if (ret)
+ return ret;
+
+ ret = layout.update(&layout, key, value);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ return eeprom_dev_spec_write(dev, 0, layout.data, layout.data_size);
+}
+
+#endif
+
+static int eeprom_action_expected_argc(enum eeprom_action action)
+{
+ switch (action) {
+ case EEPROM_LIST:
+ return 0;
+ case EEPROM_READ:
+ case EEPROM_WRITE:
+ return 3;
+ case EEPROM_PRINT:
+ return 0;
+ case EEPROM_UPDATE:
+ return 2;
+ default:
+ return CMD_RET_USAGE;
+ }
+}
+
+#define NEXT_PARAM(argc, index) { (argc)--; (index)++; }
+int do_eeprom(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ enum eeprom_action action = EEPROM_ACTION_INVALID;
+ struct eeprom_dev_spec dev;
+ ulong addr = 0, cnt = 0, off = 0;
+ int ret, index = 0;
+#ifdef CONFIG_CMD_EEPROM_LAYOUT
+ char *field_name = "";
+ char *field_value = "";
+ int layout_ver = LAYOUT_VERSION_AUTODETECT;
+#endif
+
+ if (argc <= 1)
+ return CMD_RET_USAGE;
+
+ NEXT_PARAM(argc, index); /* Skip program name */
+
+ action = parse_action(argv[index]);
+ NEXT_PARAM(argc, index);
+
+ if (action == EEPROM_ACTION_INVALID)
+ return CMD_RET_USAGE;
+
+#if CONFIG_IS_ENABLED(I2C_EEPROM)
+ if (action == EEPROM_LIST)
+ return do_eeprom_list();
+#endif
+
+#ifdef CONFIG_EEPROM_LAYOUT_VERSIONS
+ if (action == EEPROM_PRINT || action == EEPROM_UPDATE) {
+ if (!strcmp(argv[index], "-l")) {
+ NEXT_PARAM(argc, index);
+ layout_ver = eeprom_parse_layout_version(argv[index]);
+ NEXT_PARAM(argc, index);
+ }
+ }
+#endif
+
+ ret = parse_eeprom_dev_spec(&dev,
+ argc - eeprom_action_expected_argc(action),
+ argv + index);
+ if (ret == CMD_RET_USAGE)
+ return ret;
+
+ while (ret--)
+ NEXT_PARAM(argc, index);
+
+ if (action == EEPROM_READ || action == EEPROM_WRITE) {
+ addr = parse_numeric_param(argv[index]);
+ NEXT_PARAM(argc, index);
+ off = parse_numeric_param(argv[index]);
+ NEXT_PARAM(argc, index);
+ cnt = parse_numeric_param(argv[index]);
+ }
+
+#ifdef CONFIG_CMD_EEPROM_LAYOUT
+ if (action == EEPROM_UPDATE) {
+ field_name = argv[index];
+ NEXT_PARAM(argc, index);
+ field_value = argv[index];
+ NEXT_PARAM(argc, index);
+ }
+#endif
+
+ eeprom_dev_spec_init(&dev);
+
+ switch (action) {
+ case EEPROM_READ:
+ case EEPROM_WRITE:
+ return do_eeprom_rw(&dev, action == EEPROM_READ,
+ addr, off, cnt);
+#ifdef CONFIG_CMD_EEPROM_LAYOUT
+ case EEPROM_PRINT:
+ return do_eeprom_print(&dev, layout_ver);
+ case EEPROM_UPDATE:
+ return do_eeprom_update(&dev, layout_ver,
+ field_name, field_value);
+#endif
+ default:
+ return CMD_RET_USAGE;
+ }
+}
+
+#ifdef CONFIG_EEPROM_LAYOUT_VERSIONS
+#define EEPROM_LAYOUT_SPEC "[-l <layout_version>] "
+#else
+#define EEPROM_LAYOUT_SPEC ""
+#endif
+
+#if CONFIG_IS_ENABLED(I2C_EEPROM)
+# define EEPROM_DEV_SPEC "[device_specifier]"
+#else
+# define EEPROM_DEV_SPEC "[[bus] devaddr]"
+#endif
+
+U_BOOT_CMD(
+ eeprom, 8, 1, do_eeprom,
+ "EEPROM sub-system",
+#if CONFIG_IS_ENABLED(I2C_EEPROM)
+ "list\n"
+ "eeprom "
+#endif
+ "read " EEPROM_DEV_SPEC " addr off cnt\n"
+ "eeprom write " EEPROM_DEV_SPEC " addr off cnt\n"
+ " - read/write `cnt' bytes from `devaddr` EEPROM at offset `off'"
+#ifdef CONFIG_CMD_EEPROM_LAYOUT
+ "\n"
+ "eeprom print " EEPROM_LAYOUT_SPEC EEPROM_DEV_SPEC "\n"
+ " - Print layout fields and their data in human readable format\n"
+ "eeprom update " EEPROM_LAYOUT_SPEC EEPROM_DEV_SPEC " field_name field_value\n"
+ " - Update a specific eeprom field with new data.\n"
+ " The new data must be written in the same human readable format as shown by the print command."
+#endif
+#if CONFIG_IS_ENABLED(I2C_EEPROM)
+ "\n\n"
+ "DEVICE SPECIFIER - the eeprom device can be specified\n"
+ " [dev_name] - by device name (devices can listed with the eeprom list command)\n"
+ " [[bus] devaddr] - or by I2C bus and I2C device address\n"
+ "If no device specifier is given, the first driver-model found device is used."
+#endif
+#ifdef CONFIG_EEPROM_LAYOUT_VERSIONS
+ "\n\n"
+ "LAYOUT VERSIONS\n"
+ "The -l option can be used to force the command to interpret the EEPROM data using the chosen layout.\n"
+ "If the -l option is omitted, the command will auto detect the layout based on the data in the EEPROM.\n"
+ "The values which can be provided with the -l option are:\n"
+ CONFIG_EEPROM_LAYOUT_HELP_STRING"\n"
+#endif
+);
diff --git a/cmd/efi.c b/cmd/efi.c
new file mode 100644
index 00000000000..687ccb52042
--- /dev/null
+++ b/cmd/efi.c
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <command.h>
+#include <efi.h>
+#include <efi_api.h>
+#include <errno.h>
+#include <log.h>
+#include <malloc.h>
+#include <sort.h>
+#include <u-boot/uuid.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char *const type_name[] = {
+ "reserved",
+ "loader_code",
+ "loader_data",
+ "bs_code",
+ "bs_data",
+ "rt_code",
+ "rt_data",
+ "conv",
+ "unusable",
+ "acpi_reclaim",
+ "acpi_nvs",
+ "io",
+ "io_port",
+ "pal_code",
+};
+
+static struct attr_info {
+ u64 val;
+ const char *name;
+} mem_attr[] = {
+ { EFI_MEMORY_UC, "uncached" },
+ { EFI_MEMORY_WC, "write-coalescing" },
+ { EFI_MEMORY_WT, "write-through" },
+ { EFI_MEMORY_WB, "write-back" },
+ { EFI_MEMORY_UCE, "uncached & exported" },
+ { EFI_MEMORY_WP, "write-protect" },
+ { EFI_MEMORY_RP, "read-protect" },
+ { EFI_MEMORY_XP, "execute-protect" },
+ { EFI_MEMORY_NV, "non-volatile" },
+ { EFI_MEMORY_MORE_RELIABLE, "higher reliability" },
+ { EFI_MEMORY_RO, "read-only" },
+ { EFI_MEMORY_SP, "specific purpose" },
+ { EFI_MEMORY_RUNTIME, "needs runtime mapping" }
+};
+
+/* Maximum different attribute values we can track */
+#define ATTR_SEEN_MAX 30
+
+static inline bool is_boot_services(int type)
+{
+ return type == EFI_LOADER_CODE || type == EFI_LOADER_DATA ||
+ type == EFI_BOOT_SERVICES_CODE ||
+ type == EFI_BOOT_SERVICES_DATA;
+}
+
+static int h_cmp_entry(const void *v1, const void *v2)
+{
+ const struct efi_mem_desc *desc1 = v1;
+ const struct efi_mem_desc *desc2 = v2;
+ int64_t diff = desc1->physical_start - desc2->physical_start;
+
+ /*
+ * Manually calculate the difference to avoid sign loss in the 64-bit
+ * to 32-bit conversion
+ */
+ return diff < 0 ? -1 : diff > 0 ? 1 : 0;
+}
+
+/**
+ * efi_build_mem_table() - make a sorted copy of the memory table
+ *
+ * @desc_base: Pointer to EFI memory map table
+ * @size: Size of table in bytes
+ * @desc_size: Size of each @desc_base record
+ * @skip_bs: True to skip boot-time memory and merge it with conventional
+ * memory. This will significantly reduce the number of table
+ * entries.
+ * Return: pointer to the new table. It should be freed with free() by the
+ * caller.
+ */
+static void *efi_build_mem_table(struct efi_mem_desc *desc_base, int size,
+ int desc_size, bool skip_bs)
+{
+ struct efi_mem_desc *desc, *end, *base, *dest, *prev;
+ int count;
+ u64 addr;
+
+ base = malloc(size + sizeof(*desc));
+ if (!base) {
+ debug("%s: Cannot allocate %#x bytes\n", __func__, size);
+ return NULL;
+ }
+ end = (void *)desc_base + size;
+ count = ((ulong)end - (ulong)desc_base) / desc_size;
+ memcpy(base, desc_base, (ulong)end - (ulong)desc_base);
+ qsort(base, count, desc_size, h_cmp_entry);
+ prev = NULL;
+ addr = 0;
+ dest = base;
+ end = (struct efi_mem_desc *)((ulong)base + count * desc_size);
+ for (desc = base; desc < end;
+ desc = efi_get_next_mem_desc(desc, desc_size)) {
+ bool merge = true;
+ u32 type = desc->type;
+
+ if (type >= EFI_MAX_MEMORY_TYPE) {
+ printf("Memory map contains invalid entry type %u\n",
+ type);
+ continue;
+ }
+
+ if (skip_bs && is_boot_services(desc->type))
+ type = EFI_CONVENTIONAL_MEMORY;
+
+ memcpy(dest, desc, desc_size);
+ dest->type = type;
+ if (!skip_bs || !prev)
+ merge = false;
+ else if (desc->physical_start != addr)
+ merge = false;
+ else if (type != EFI_CONVENTIONAL_MEMORY)
+ merge = false;
+ else if (prev->type != EFI_CONVENTIONAL_MEMORY)
+ merge = false;
+
+ if (merge) {
+ prev->num_pages += desc->num_pages;
+ } else {
+ prev = dest;
+ dest = efi_get_next_mem_desc(dest, desc_size);
+ }
+ addr = desc->physical_start + (desc->num_pages <<
+ EFI_PAGE_SHIFT);
+ }
+
+ /* Mark the end */
+ dest->type = EFI_MAX_MEMORY_TYPE;
+
+ return base;
+}
+
+static void efi_print_mem_table(struct efi_mem_desc *desc, int desc_size,
+ bool skip_bs)
+{
+ u64 attr_seen[ATTR_SEEN_MAX];
+ int attr_seen_count;
+ int upto, i;
+ u64 addr;
+
+ printf(" # %-14s %10s %10s %10s %s\n", "Type", "Physical",
+ "Virtual", "Size", "Attributes");
+
+ /* Keep track of all the different attributes we have seen */
+ attr_seen_count = 0;
+ addr = 0;
+ for (upto = 0; desc->type != EFI_MAX_MEMORY_TYPE;
+ upto++, desc = efi_get_next_mem_desc(desc, desc_size)) {
+ const char *name;
+ u64 size;
+
+ if (skip_bs && is_boot_services(desc->type))
+ continue;
+ if (desc->physical_start != addr) {
+ printf(" %-14s %010llx %10s %010llx\n", "<gap>",
+ addr, "", desc->physical_start - addr);
+ }
+ size = desc->num_pages << EFI_PAGE_SHIFT;
+
+ name = desc->type < ARRAY_SIZE(type_name) ?
+ type_name[desc->type] : "<invalid>";
+ printf("%2d %x:%-12s %010llx %010llx %010llx ", upto,
+ desc->type, name, desc->physical_start,
+ desc->virtual_start, size);
+ if (desc->attribute & EFI_MEMORY_RUNTIME)
+ putc('r');
+ printf("%llx", desc->attribute & ~EFI_MEMORY_RUNTIME);
+ putc('\n');
+
+ for (i = 0; i < attr_seen_count; i++) {
+ if (attr_seen[i] == desc->attribute)
+ break;
+ }
+ if (i == attr_seen_count && i < ATTR_SEEN_MAX)
+ attr_seen[attr_seen_count++] = desc->attribute;
+ addr = desc->physical_start + size;
+ }
+
+ printf("\nAttributes key:\n");
+ for (i = 0; i < attr_seen_count; i++) {
+ u64 attr = attr_seen[i];
+ bool first;
+ int j;
+
+ printf("%c%llx: ", (attr & EFI_MEMORY_RUNTIME) ? 'r' : ' ',
+ attr & ~EFI_MEMORY_RUNTIME);
+ for (j = 0, first = true; j < ARRAY_SIZE(mem_attr); j++) {
+ if (attr & mem_attr[j].val) {
+ if (first)
+ first = false;
+ else
+ printf(", ");
+ printf("%s", mem_attr[j].name);
+ }
+ }
+ putc('\n');
+ }
+ if (skip_bs)
+ printf("*Some areas are merged (use 'all' to see)\n");
+}
+
+static int do_efi_mem(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct efi_mem_desc *orig, *desc;
+ uint version, key;
+ int desc_size;
+ int size, ret;
+ bool skip_bs;
+
+ skip_bs = !argc || *argv[0] != 'a';
+ if (IS_ENABLED(CONFIG_EFI_APP)) {
+ ret = efi_get_mmap(&orig, &size, &key, &desc_size, &version);
+ if (ret) {
+ printf("Cannot read memory map (err=%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ } else {
+ struct efi_entry_memmap *map;
+
+ ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size);
+ switch (ret) {
+ case -ENOENT:
+ printf("No EFI table available\n");
+ goto done;
+ case -EPROTONOSUPPORT:
+ printf("Incorrect EFI table version\n");
+ goto done;
+ }
+ orig = map->desc;
+ desc_size = map->desc_size;
+ version = map->version;
+ }
+ printf("EFI table at %lx, memory map %p, size %x, key %x, version %x, descr. size %#x\n",
+ gd->arch.table, orig, size, key, version, desc_size);
+ if (version != EFI_MEM_DESC_VERSION) {
+ printf("Incorrect memory map version\n");
+ ret = -EPROTONOSUPPORT;
+ goto done;
+ }
+
+ desc = efi_build_mem_table(orig, size, desc_size, skip_bs);
+ if (!desc) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ efi_print_mem_table(desc, desc_size, skip_bs);
+ free(desc);
+ if (IS_ENABLED(CONFIG_EFI_APP))
+ free(orig);
+done:
+ if (ret)
+ printf("Error: %d\n", ret);
+
+ return ret ? CMD_RET_FAILURE : 0;
+}
+
+static int do_efi_tables(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct efi_system_table *systab;
+
+ if (IS_ENABLED(CONFIG_EFI_APP)) {
+ systab = efi_get_sys_table();
+ if (!systab) {
+ printf("Cannot read system table\n");
+ return CMD_RET_FAILURE;
+ }
+ } else {
+ int size;
+ int ret;
+
+ ret = efi_info_get(EFIET_SYS_TABLE, (void **)&systab, &size);
+ if (ret) /* this should not happen */
+ return CMD_RET_FAILURE;
+ }
+
+ efi_show_tables(systab);
+
+ return 0;
+}
+
+static struct cmd_tbl efi_commands[] = {
+ U_BOOT_CMD_MKENT(mem, 1, 1, do_efi_mem, "", ""),
+ U_BOOT_CMD_MKENT(tables, 1, 1, do_efi_tables, "", ""),
+};
+
+static int do_efi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct cmd_tbl *efi_cmd;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ efi_cmd = find_cmd_tbl(argv[1], efi_commands, ARRAY_SIZE(efi_commands));
+ argc -= 2;
+ argv += 2;
+ if (!efi_cmd || argc > efi_cmd->maxargs)
+ return CMD_RET_USAGE;
+
+ ret = efi_cmd->cmd(efi_cmd, flag, argc, argv);
+
+ return cmd_process_error(efi_cmd, ret);
+}
+
+U_BOOT_CMD(
+ efi, 3, 1, do_efi,
+ "EFI access",
+ "mem [all] Dump memory information [include boot services]\n"
+ "tables Dump tables"
+);
diff --git a/cmd/efi_common.c b/cmd/efi_common.c
new file mode 100644
index 00000000000..d2f2b59e9e3
--- /dev/null
+++ b/cmd/efi_common.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Common code for EFI commands
+ *
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <efi.h>
+#include <efi_api.h>
+#include <u-boot/uuid.h>
+
+void efi_show_tables(struct efi_system_table *systab)
+{
+ int i;
+
+ for (i = 0; i < systab->nr_tables; i++) {
+ struct efi_configuration_table *tab = &systab->tables[i];
+
+ printf("%p %pUl %s\n", tab->table, tab->guid.b,
+ uuid_guid_get_str(tab->guid.b) ?: "(unknown)");
+ }
+}
diff --git a/cmd/eficonfig.c b/cmd/eficonfig.c
new file mode 100644
index 00000000000..8ac0fb98e02
--- /dev/null
+++ b/cmd/eficonfig.c
@@ -0,0 +1,2507 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Menu-driven UEFI Variable maintenance
+ *
+ * Copyright (c) 2022 Masahisa Kojima, Linaro Limited
+ */
+
+#include <ansi.h>
+#include <cli.h>
+#include <charset.h>
+#include <efi_device_path.h>
+#include <efi_loader.h>
+#include <efi_load_initrd.h>
+#include <efi_config.h>
+#include <efi_variable.h>
+#include <log.h>
+#include <malloc.h>
+#include <menu.h>
+#include <sort.h>
+#include <watchdog.h>
+#include <asm/unaligned.h>
+#include <linux/delay.h>
+
+static struct efi_simple_text_input_protocol *cin;
+const char *eficonfig_menu_desc =
+ " Press UP/DOWN to move, ENTER to select, ESC to quit";
+
+static const char *eficonfig_change_boot_order_desc =
+ " Press UP/DOWN to move, +/- to change orde\n"
+ " Press SPACE to activate or deactivate the entry\n"
+ " CTRL+S to save, ESC to quit";
+
+static struct efi_simple_text_output_protocol *cout;
+static int avail_row;
+
+#define EFICONFIG_DESCRIPTION_MAX 32
+#define EFICONFIG_OPTIONAL_DATA_MAX 64
+#define EFICONFIG_URI_MAX 512
+#define EFICONFIG_MENU_HEADER_ROW_NUM 3
+#define EFICONFIG_MENU_DESC_ROW_NUM 5
+
+/**
+ * struct eficonfig_filepath_info - structure to be used to store file path
+ *
+ * @name: file or directory name
+ * @list: list structure
+ */
+struct eficonfig_filepath_info {
+ char *name;
+ struct list_head list;
+};
+
+/**
+ * struct eficonfig_boot_option - structure to be used for updating UEFI boot option
+ *
+ * @file_info: user selected file info
+ * @initrd_info: user selected initrd file info
+ * @boot_index: index of the boot option
+ * @description: pointer to the description string
+ * @optional_data: pointer to the optional_data
+ * @edit_completed: flag indicates edit complete
+ */
+struct eficonfig_boot_option {
+ struct eficonfig_select_file_info file_info;
+ struct eficonfig_select_file_info initrd_info;
+ struct eficonfig_select_file_info fdt_info;
+ unsigned int boot_index;
+ u16 *description;
+ u16 *optional_data;
+ bool edit_completed;
+};
+
+/**
+ * struct eficonfig_volume_entry_data - structure to be used to store volume info
+ *
+ * @file_info: pointer to file info structure
+ * @v: pointer to the protocol interface
+ * @dp: pointer to the device path
+ */
+struct eficonfig_volume_entry_data {
+ struct eficonfig_select_file_info *file_info;
+ struct efi_simple_file_system_protocol *v;
+ struct efi_device_path *dp;
+};
+
+/**
+ * struct eficonfig_file_entry_data - structure to be used to store file info
+ *
+ * @file_info: pointer to file info structure
+ * @is_directory: flag to identify the directory or file
+ * @file_name: name of directory or file
+ */
+struct eficonfig_file_entry_data {
+ struct eficonfig_select_file_info *file_info;
+ bool is_directory;
+ char *file_name;
+};
+
+/**
+ * struct eficonfig_boot_selection_data - structure to be used to select the boot option entry
+ *
+ * @boot_index: index of the boot option
+ * @selected: pointer to store the selected index in the BootOrder variable
+ */
+struct eficonfig_boot_selection_data {
+ u16 boot_index;
+ int *selected;
+};
+
+/**
+ * struct eficonfig_boot_order_data - structure to be used to update BootOrder variable
+ *
+ * @boot_index: boot option index
+ * @active: flag to include the boot option into BootOrder variable
+ */
+struct eficonfig_boot_order_data {
+ u32 boot_index;
+ bool active;
+};
+
+/**
+ * struct eficonfig_save_boot_order_data - structure to be used to change boot order
+ *
+ * @efi_menu: pointer to efimenu structure
+ * @selected: flag to indicate user selects "Save" entry
+ */
+struct eficonfig_save_boot_order_data {
+ struct efimenu *efi_menu;
+ bool selected;
+};
+
+/**
+ * struct eficonfig_menu_adjust - update start and end entry index
+ *
+ * @efi_menu: pointer to efimenu structure
+ * @add: flag to add or substract the index
+ */
+static void eficonfig_menu_adjust(struct efimenu *efi_menu, bool add)
+{
+ if (add)
+ ++efi_menu->active;
+ else
+ --efi_menu->active;
+
+ if (add && efi_menu->end < efi_menu->active) {
+ efi_menu->start++;
+ efi_menu->end++;
+ } else if (!add && efi_menu->start > efi_menu->active) {
+ efi_menu->start--;
+ efi_menu->end--;
+ }
+}
+#define eficonfig_menu_up(_a) eficonfig_menu_adjust(_a, false)
+#define eficonfig_menu_down(_a) eficonfig_menu_adjust(_a, true)
+
+/**
+ * eficonfig_print_msg() - print message
+ *
+ * display the message to the user, user proceeds the screen
+ * with any key press.
+ *
+ * @items: pointer to the structure of each menu entry
+ * @count: the number of menu entry
+ * @menu_header: pointer to the menu header string
+ * Return: status code
+ */
+void eficonfig_print_msg(char *msg)
+{
+ /* Flush input */
+ while (tstc())
+ getchar();
+
+ printf(ANSI_CURSOR_HIDE
+ ANSI_CLEAR_CONSOLE
+ ANSI_CURSOR_POSITION
+ "%s\n\n Press any key to continue", 3, 4, msg);
+
+ getchar();
+}
+
+/**
+ * eficonfig_print_entry() - print each menu entry
+ *
+ * @data: pointer to the data associated with each menu entry
+ */
+void eficonfig_print_entry(void *data)
+{
+ struct eficonfig_entry *entry = data;
+ bool reverse = (entry->efi_menu->active == entry->num);
+
+ if (entry->efi_menu->start > entry->num || entry->efi_menu->end < entry->num)
+ return;
+
+ printf(ANSI_CURSOR_POSITION, (entry->num - entry->efi_menu->start) +
+ EFICONFIG_MENU_HEADER_ROW_NUM + 1, 7);
+
+ if (reverse)
+ puts(ANSI_COLOR_REVERSE);
+
+ printf(ANSI_CLEAR_LINE "%s", entry->title);
+
+ if (reverse)
+ puts(ANSI_COLOR_RESET);
+}
+
+/**
+ * eficonfig_display_statusline() - print status line
+ *
+ * @m: pointer to the menu structure
+ */
+void eficonfig_display_statusline(struct menu *m)
+{
+ struct eficonfig_entry *entry;
+
+ if (menu_default_choice(m, (void *)&entry) < 0)
+ return;
+
+ printf(ANSI_CURSOR_POSITION
+ "\n%s\n"
+ ANSI_CURSOR_POSITION ANSI_CLEAR_LINE ANSI_CURSOR_POSITION
+ "%s"
+ ANSI_CLEAR_LINE_TO_END,
+ 1, 1, entry->efi_menu->menu_header, avail_row + 4, 1,
+ avail_row + 5, 1, entry->efi_menu->menu_desc);
+}
+
+/**
+ * eficonfig_choice_entry() - user key input handler
+ *
+ * @data: pointer to the efimenu structure
+ * Return: key string to identify the selected entry
+ */
+char *eficonfig_choice_entry(void *data)
+{
+ struct cli_ch_state s_cch, *cch = &s_cch;
+ struct list_head *pos, *n;
+ struct eficonfig_entry *entry;
+ enum bootmenu_key key = BKEY_NONE;
+ struct efimenu *efi_menu = data;
+
+ cli_ch_init(cch);
+
+ while (1) {
+ key = bootmenu_loop((struct bootmenu_data *)efi_menu, cch);
+
+ switch (key) {
+ case BKEY_UP:
+ if (efi_menu->active > 0)
+ eficonfig_menu_up(efi_menu);
+
+ /* no menu key selected, regenerate menu */
+ return NULL;
+ case BKEY_DOWN:
+ if (efi_menu->active < efi_menu->count - 1)
+ eficonfig_menu_down(efi_menu);
+
+ /* no menu key selected, regenerate menu */
+ return NULL;
+ case BKEY_SELECT:
+ list_for_each_safe(pos, n, &efi_menu->list) {
+ entry = list_entry(pos, struct eficonfig_entry, list);
+ if (entry->num == efi_menu->active)
+ return entry->key;
+ }
+ break;
+ case BKEY_QUIT:
+ /* Quit by choosing the last entry */
+ entry = list_last_entry(&efi_menu->list, struct eficonfig_entry, list);
+ return entry->key;
+ default:
+ /* Pressed key is not valid, no need to regenerate the menu */
+ break;
+ }
+ }
+}
+
+/**
+ * eficonfig_destroy() - destroy efimenu
+ *
+ * @efi_menu: pointer to the efimenu structure
+ */
+void eficonfig_destroy(struct efimenu *efi_menu)
+{
+ struct list_head *pos, *n;
+ struct eficonfig_entry *entry;
+
+ if (!efi_menu)
+ return;
+
+ list_for_each_safe(pos, n, &efi_menu->list) {
+ entry = list_entry(pos, struct eficonfig_entry, list);
+ free(entry->title);
+ list_del(&entry->list);
+ free(entry);
+ }
+ free(efi_menu->menu_header);
+ free(efi_menu);
+}
+
+/**
+ * eficonfig_process_quit() - callback function for "Quit" entry
+ *
+ * @data: pointer to the data
+ * Return: status code
+ */
+efi_status_t eficonfig_process_quit(void *data)
+{
+ return EFI_ABORTED;
+}
+
+/**
+ * eficonfig_append_menu_entry() - append menu item
+ *
+ * @efi_menu: pointer to the efimenu structure
+ * @title: pointer to the entry title
+ * @func: callback of each entry
+ * @data: pointer to the data to be passed to each entry callback
+ * Return: status code
+ */
+efi_status_t eficonfig_append_menu_entry(struct efimenu *efi_menu,
+ char *title, eficonfig_entry_func func,
+ void *data)
+{
+ struct eficonfig_entry *entry;
+
+ if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX)
+ return EFI_OUT_OF_RESOURCES;
+
+ entry = calloc(1, sizeof(struct eficonfig_entry));
+ if (!entry)
+ return EFI_OUT_OF_RESOURCES;
+
+ entry->title = title;
+ sprintf(entry->key, "%d", efi_menu->count);
+ entry->efi_menu = efi_menu;
+ entry->func = func;
+ entry->data = data;
+ entry->num = efi_menu->count++;
+ list_add_tail(&entry->list, &efi_menu->list);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * eficonfig_append_quit_entry() - append quit entry
+ *
+ * @efi_menu: pointer to the efimenu structure
+ * Return: status code
+ */
+efi_status_t eficonfig_append_quit_entry(struct efimenu *efi_menu)
+{
+ char *title;
+ efi_status_t ret;
+
+ title = strdup("Quit");
+ if (!title)
+ return EFI_OUT_OF_RESOURCES;
+
+ ret = eficonfig_append_menu_entry(efi_menu, title, eficonfig_process_quit, NULL);
+ if (ret != EFI_SUCCESS)
+ free(title);
+
+ return ret;
+}
+
+/**
+ * eficonfig_create_fixed_menu() - create fixed entry menu structure
+ *
+ * @items: pointer to the menu entry item
+ * @count: the number of menu entry
+ * Return: pointer to the efimenu structure
+ */
+void *eficonfig_create_fixed_menu(const struct eficonfig_item *items, int count)
+{
+ u32 i;
+ char *title;
+ efi_status_t ret;
+ struct efimenu *efi_menu;
+ const struct eficonfig_item *iter = items;
+
+ efi_menu = calloc(1, sizeof(struct efimenu));
+ if (!efi_menu)
+ return NULL;
+
+ INIT_LIST_HEAD(&efi_menu->list);
+ for (i = 0; i < count; i++, iter++) {
+ title = strdup(iter->title);
+ if (!title)
+ goto out;
+
+ ret = eficonfig_append_menu_entry(efi_menu, title, iter->func, iter->data);
+ if (ret != EFI_SUCCESS) {
+ free(title);
+ goto out;
+ }
+ }
+
+ return efi_menu;
+out:
+ eficonfig_destroy(efi_menu);
+
+ return NULL;
+}
+
+/**
+ * eficonfig_process_common() - main handler for UEFI menu
+ *
+ * Construct the structures required to show the menu, then handle
+ * the user input interacting with u-boot menu functions.
+ *
+ * @efi_menu: pointer to the efimenu structure
+ * @menu_header: pointer to the menu header string
+ * @menu_desc: pointer to the menu description
+ * @display_statusline: function pointer to draw statusline
+ * @item_data_print: function pointer to draw the menu item
+ * @item_choice: function pointer to handle the key press
+ * Return: status code
+ */
+efi_status_t eficonfig_process_common(struct efimenu *efi_menu,
+ char *menu_header, const char *menu_desc,
+ void (*display_statusline)(struct menu *),
+ void (*item_data_print)(void *),
+ char *(*item_choice)(void *))
+{
+ struct menu *menu;
+ void *choice = NULL;
+ struct list_head *pos, *n;
+ struct eficonfig_entry *entry;
+ efi_status_t ret = EFI_SUCCESS;
+
+ if (efi_menu->count > EFICONFIG_ENTRY_NUM_MAX)
+ return EFI_OUT_OF_RESOURCES;
+
+ efi_menu->delay = -1;
+ efi_menu->active = 0;
+ efi_menu->start = 0;
+ efi_menu->end = avail_row - 1;
+
+ if (menu_header) {
+ efi_menu->menu_header = strdup(menu_header);
+ if (!efi_menu->menu_header)
+ return EFI_OUT_OF_RESOURCES;
+ }
+ if (menu_desc)
+ efi_menu->menu_desc = menu_desc;
+
+ menu = menu_create(NULL, 0, 1, display_statusline, item_data_print,
+ item_choice, NULL, efi_menu);
+ if (!menu)
+ return EFI_INVALID_PARAMETER;
+
+ list_for_each_safe(pos, n, &efi_menu->list) {
+ entry = list_entry(pos, struct eficonfig_entry, list);
+ if (!menu_item_add(menu, entry->key, entry)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+
+ entry = list_first_entry_or_null(&efi_menu->list, struct eficonfig_entry, list);
+ if (entry)
+ menu_default_set(menu, entry->key);
+
+ printf(ANSI_CURSOR_HIDE
+ ANSI_CLEAR_CONSOLE
+ ANSI_CURSOR_POSITION, 1, 1);
+
+ if (menu_get_choice(menu, &choice)) {
+ entry = choice;
+ if (entry->func)
+ ret = entry->func(entry->data);
+ }
+out:
+ menu_destroy(menu);
+
+ printf(ANSI_CLEAR_CONSOLE
+ ANSI_CURSOR_POSITION
+ ANSI_CURSOR_SHOW, 1, 1);
+
+ return ret;
+}
+
+/**
+ * eficonfig_volume_selected() - handler of volume selection
+ *
+ * @data: pointer to the data of selected entry
+ * Return: status code
+ */
+static efi_status_t eficonfig_volume_selected(void *data)
+{
+ struct eficonfig_volume_entry_data *info = data;
+
+ if (info) {
+ info->file_info->current_volume = info->v;
+ info->file_info->dp_volume = info->dp;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * eficonfig_create_device_path() - create device path
+ *
+ * @dp_volume: pointer to the volume
+ * @current_path: pointer to the file path u16 string
+ * Return:
+ * device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *eficonfig_create_device_path(struct efi_device_path *dp_volume,
+ u16 *current_path)
+{
+ char *p;
+ void *buf;
+ efi_uintn_t fp_size;
+ struct efi_device_path *dp;
+ struct efi_device_path_file_path *fp;
+
+ fp_size = sizeof(struct efi_device_path) + u16_strsize(current_path);
+ buf = calloc(1, fp_size + sizeof(EFI_DP_END));
+ if (!buf)
+ return NULL;
+
+ fp = buf;
+ fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
+ fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
+ fp->dp.length = (u16)fp_size;
+ u16_strcpy(fp->str, current_path);
+
+ p = buf;
+ p += fp_size;
+ *((struct efi_device_path *)p) = EFI_DP_END;
+
+ dp = efi_dp_shorten(dp_volume);
+ if (!dp)
+ dp = dp_volume;
+ dp = efi_dp_concat(dp, &fp->dp, 0);
+ free(buf);
+
+ return dp;
+}
+
+/**
+ * eficonfig_create_uri_device_path() - Create an URI based device path
+ * @uri_str: URI string to be added to the device path
+ *
+ * Take the u16 string, convert it to a u8 string, and create a URI
+ * device path. This will be used for the EFI HTTP boot.
+ *
+ * Return: pointer to the URI device path on success, NULL on failure
+ */
+static struct efi_device_path *eficonfig_create_uri_device_path(u16 *uri_str)
+{
+ char *pos, *p;
+ u32 len = 0;
+ efi_uintn_t uridp_len;
+ struct efi_device_path_uri *uridp;
+
+ len = utf16_utf8_strlen(uri_str);
+
+ uridp_len = sizeof(struct efi_device_path) + len + 1;
+ uridp = efi_alloc(uridp_len + sizeof(EFI_DP_END));
+ if (!uridp)
+ return NULL;
+
+ uridp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ uridp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_URI;
+ uridp->dp.length = uridp_len;
+ p = (char *)&uridp->uri;
+ utf16_utf8_strcpy(&p, uri_str);
+ pos = (char *)uridp + uridp_len;
+ memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END));
+
+ return &uridp->dp;
+}
+
+/**
+ * eficonfig_file_selected() - handler of file selection
+ *
+ * @data: pointer to the data of selected entry
+ * Return: status code
+ */
+static efi_status_t eficonfig_file_selected(void *data)
+{
+ u16 *tmp;
+ struct eficonfig_file_entry_data *info = data;
+
+ if (!info)
+ return EFI_INVALID_PARAMETER;
+
+ if (!strcmp(info->file_name, "..\\")) {
+ struct eficonfig_filepath_info *iter;
+ struct list_head *pos, *n;
+ int is_last;
+ char *filepath;
+ tmp = info->file_info->current_path;
+
+ memset(info->file_info->current_path, 0, EFICONFIG_FILE_PATH_BUF_SIZE);
+ filepath = calloc(1, EFICONFIG_FILE_PATH_MAX);
+ if (!filepath)
+ return EFI_OUT_OF_RESOURCES;
+
+ list_for_each_safe(pos, n, &info->file_info->filepath_list) {
+ iter = list_entry(pos, struct eficonfig_filepath_info, list);
+
+ is_last = list_is_last(&iter->list, &info->file_info->filepath_list);
+ if (is_last) {
+ list_del(&iter->list);
+ free(iter->name);
+ free(iter);
+ break;
+ }
+ strlcat(filepath, iter->name, EFICONFIG_FILE_PATH_MAX);
+ }
+ utf8_utf16_strcpy(&tmp, filepath);
+ } else {
+ size_t new_len;
+ struct eficonfig_filepath_info *filepath_info;
+
+ new_len = u16_strlen(info->file_info->current_path) +
+ strlen(info->file_name);
+ if (new_len >= EFICONFIG_FILE_PATH_MAX) {
+ eficonfig_print_msg("File path is too long!");
+ return EFI_INVALID_PARAMETER;
+ }
+ tmp = &info->file_info->current_path[u16_strlen(info->file_info->current_path)];
+ utf8_utf16_strcpy(&tmp, info->file_name);
+
+ filepath_info = calloc(1, sizeof(struct eficonfig_filepath_info));
+ if (!filepath_info)
+ return EFI_OUT_OF_RESOURCES;
+
+ filepath_info->name = strdup(info->file_name);
+ if (!filepath_info->name) {
+ free(filepath_info);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ list_add_tail(&filepath_info->list, &info->file_info->filepath_list);
+
+ if (!info->is_directory)
+ info->file_info->file_selected = true;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * eficonfig_select_volume() - construct the volume selection menu
+ *
+ * @file_info: pointer to the file selection structure
+ * Return: status code
+ */
+static efi_status_t eficonfig_select_volume(struct eficonfig_select_file_info *file_info)
+{
+ u32 i;
+ efi_status_t ret;
+ efi_uintn_t count;
+ struct efimenu *efi_menu;
+ struct list_head *pos, *n;
+ struct efi_handler *handler;
+ struct eficonfig_entry *entry;
+ struct efi_device_path *device_path;
+ efi_handle_t *volume_handles = NULL;
+ struct efi_simple_file_system_protocol *v;
+
+ ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid,
+ NULL, &count, (efi_handle_t **)&volume_handles);
+ if (ret != EFI_SUCCESS) {
+ eficonfig_print_msg("No block device found!");
+ return ret;
+ }
+
+ efi_menu = calloc(1, sizeof(struct efimenu));
+ if (!efi_menu)
+ return EFI_OUT_OF_RESOURCES;
+
+ INIT_LIST_HEAD(&efi_menu->list);
+ for (i = 0; i < count; i++) {
+ char *devname;
+ struct efi_block_io *block_io;
+ struct eficonfig_volume_entry_data *info;
+
+ if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
+ break;
+
+ ret = efi_search_protocol(volume_handles[i],
+ &efi_simple_file_system_protocol_guid, &handler);
+ if (ret != EFI_SUCCESS)
+ continue;
+ ret = efi_protocol_open(handler, (void **)&v, efi_root, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (ret != EFI_SUCCESS)
+ continue;
+
+ ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler);
+ if (ret != EFI_SUCCESS)
+ continue;
+ ret = efi_protocol_open(handler, (void **)&device_path,
+ efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (ret != EFI_SUCCESS)
+ continue;
+
+ ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler);
+ if (ret != EFI_SUCCESS)
+ continue;
+ ret = efi_protocol_open(handler, (void **)&block_io,
+ efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (ret != EFI_SUCCESS)
+ continue;
+
+ info = calloc(1, sizeof(struct eficonfig_volume_entry_data));
+ if (!info) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ devname = calloc(1, BOOTMENU_DEVICE_NAME_MAX);
+ if (!devname) {
+ free(info);
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ ret = efi_disk_get_device_name(volume_handles[i], devname,
+ BOOTMENU_DEVICE_NAME_MAX);
+ if (ret != EFI_SUCCESS) {
+ free(info);
+ goto out;
+ }
+
+ info->v = v;
+ info->dp = device_path;
+ info->file_info = file_info;
+ ret = eficonfig_append_menu_entry(efi_menu, devname, eficonfig_volume_selected,
+ info);
+ if (ret != EFI_SUCCESS) {
+ free(info);
+ goto out;
+ }
+ }
+
+ ret = eficonfig_append_quit_entry(efi_menu);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = eficonfig_process_common(efi_menu, " ** Select Volume **",
+ eficonfig_menu_desc,
+ eficonfig_display_statusline,
+ eficonfig_print_entry,
+ eficonfig_choice_entry);
+
+out:
+ efi_free_pool(volume_handles);
+ list_for_each_safe(pos, n, &efi_menu->list) {
+ entry = list_entry(pos, struct eficonfig_entry, list);
+ free(entry->data);
+ }
+ eficonfig_destroy(efi_menu);
+
+ return ret;
+}
+
+/**
+ * sort_file() - sort the file name in ascii order
+ *
+ * @data1: pointer to the file entry data
+ * @data2: pointer to the file entry data
+ * Return: -1 if the data1 file name is less than data2 file name,
+ * 0 if both file name match,
+ * 1 if the data1 file name is greater thant data2 file name.
+ */
+static int sort_file(const void *arg1, const void *arg2)
+{
+ const struct eficonfig_file_entry_data *data1, *data2;
+
+ data1 = *((const struct eficonfig_file_entry_data **)arg1);
+ data2 = *((const struct eficonfig_file_entry_data **)arg2);
+
+ return strcasecmp(data1->file_name, data2->file_name);
+}
+
+/**
+ * eficonfig_create_file_entry() - construct the file menu entry
+ *
+ * @efi_menu: pointer to the efimenu structure
+ * @count: number of the directory and file
+ * @tmp_infos: pointer to the entry data array
+ * @f: pointer to the file handle
+ * @buf: pointer to the buffer to store the directory information
+ * @file_info: pointer to the file selection structure
+ * Return: status code
+ */
+static efi_status_t
+eficonfig_create_file_entry(struct efimenu *efi_menu, u32 count,
+ struct eficonfig_file_entry_data **tmp_infos,
+ struct efi_file_handle *f, struct efi_file_info *buf,
+ struct eficonfig_select_file_info *file_info)
+{
+ char *name, *p;
+ efi_uintn_t len;
+ efi_status_t ret;
+ u32 i, entry_num = 0;
+ struct eficonfig_file_entry_data *info;
+
+ EFI_CALL(f->setpos(f, 0));
+ /* Read directory and construct menu structure */
+ for (i = 0; i < count; i++) {
+ if (entry_num >= EFICONFIG_ENTRY_NUM_MAX - 1)
+ break;
+
+ len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE;
+ ret = EFI_CALL(f->read(f, &len, buf));
+ if (ret != EFI_SUCCESS || len == 0)
+ break;
+
+ info = calloc(1, sizeof(struct eficonfig_file_entry_data));
+ if (!info) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ /* append '\\' at the end of directory name */
+ name = calloc(1, utf16_utf8_strlen(buf->file_name) + 2);
+ if (!name) {
+ ret = EFI_OUT_OF_RESOURCES;
+ free(info);
+ goto out;
+ }
+ p = name;
+ utf16_utf8_strcpy(&p, buf->file_name);
+ if (buf->attribute & EFI_FILE_DIRECTORY) {
+ /* filter out u'.' */
+ if (!u16_strcmp(buf->file_name, u".")) {
+ free(info);
+ free(name);
+ continue;
+ }
+ name[u16_strlen(buf->file_name)] = '\\';
+ info->is_directory = true;
+ }
+
+ info->file_name = name;
+ info->file_info = file_info;
+ tmp_infos[entry_num++] = info;
+ }
+
+ qsort(tmp_infos, entry_num, sizeof(*tmp_infos),
+ (int (*)(const void *, const void *))sort_file);
+
+ for (i = 0; i < entry_num; i++) {
+ ret = eficonfig_append_menu_entry(efi_menu, tmp_infos[i]->file_name,
+ eficonfig_file_selected, tmp_infos[i]);
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * eficonfig_show_file_selection() - construct the file selection menu
+ *
+ * @file_info: pointer to the file selection structure
+ * @root: pointer to the file handle
+ * Return: status code
+ */
+static efi_status_t eficonfig_show_file_selection(struct eficonfig_select_file_info *file_info,
+ struct efi_file_handle *root)
+{
+ u32 count = 0, i;
+ efi_uintn_t len;
+ efi_status_t ret;
+ struct efimenu *efi_menu;
+ struct efi_file_handle *f;
+ struct efi_file_info *buf;
+ struct eficonfig_file_entry_data **tmp_infos;
+
+ buf = calloc(1, sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE);
+ if (!buf)
+ return EFI_OUT_OF_RESOURCES;
+
+ while (!file_info->file_selected) {
+ efi_menu = calloc(1, sizeof(struct efimenu));
+ if (!efi_menu) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ INIT_LIST_HEAD(&efi_menu->list);
+
+ ret = EFI_CALL(root->open(root, &f, file_info->current_path,
+ EFI_FILE_MODE_READ, 0));
+ if (ret != EFI_SUCCESS) {
+ eficonfig_print_msg("Reading volume failed!");
+ free(efi_menu);
+ ret = EFI_ABORTED;
+ goto out;
+ }
+
+ /* Count the number of directory entries */
+ for (;;) {
+ len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE;
+ ret = EFI_CALL(f->read(f, &len, buf));
+ if (ret != EFI_SUCCESS || len == 0)
+ break;
+
+ count++;
+ }
+
+ /* allocate array to sort the entry */
+ tmp_infos = calloc(count, sizeof(*tmp_infos));
+ if (!tmp_infos) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
+ }
+
+ ret = eficonfig_create_file_entry(efi_menu, count, tmp_infos,
+ f, buf, file_info);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ ret = eficonfig_append_quit_entry(efi_menu);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ ret = eficonfig_process_common(efi_menu, " ** Select File **",
+ eficonfig_menu_desc,
+ eficonfig_display_statusline,
+ eficonfig_print_entry,
+ eficonfig_choice_entry);
+err:
+ EFI_CALL(f->close(f));
+ eficonfig_destroy(efi_menu);
+
+ if (tmp_infos) {
+ for (i = 0; i < count; i++)
+ free(tmp_infos[i]);
+ }
+
+ free(tmp_infos);
+
+ if (ret != EFI_SUCCESS)
+ break;
+ }
+
+out:
+ free(buf);
+
+ return ret;
+}
+
+/**
+ * handle_user_input() - handle user input
+ *
+ * @buf: pointer to the buffer
+ * @buf_size: size of the buffer
+ * @cursor_col: cursor column for user input
+ * @msg: pointer to the string to display
+ * Return: status code
+ */
+static efi_status_t handle_user_input(u16 *buf, int buf_size,
+ int cursor_col, char *msg)
+{
+ u16 *tmp;
+ efi_status_t ret;
+
+ printf(ANSI_CLEAR_CONSOLE
+ ANSI_CURSOR_POSITION
+ "%s"
+ ANSI_CURSOR_POSITION
+ " Press ENTER to complete, ESC to quit",
+ 0, 1, msg, 8, 1);
+
+ /* tmp is used to accept user cancel */
+ tmp = calloc(1, buf_size * sizeof(u16));
+ if (!tmp)
+ return EFI_OUT_OF_RESOURCES;
+
+ ret = efi_console_get_u16_string(cin, tmp, buf_size, NULL, 4, cursor_col);
+ if (ret == EFI_SUCCESS)
+ u16_strcpy(buf, tmp);
+
+ free(tmp);
+
+ /* to stay the parent menu */
+ ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
+
+ return ret;
+}
+
+/**
+ * eficonfig_boot_add_enter_description() - handle user input for description
+ *
+ * @data: pointer to the internal boot option structure
+ * Return: status code
+ */
+static efi_status_t eficonfig_boot_add_enter_description(void *data)
+{
+ struct eficonfig_boot_option *bo = data;
+
+ return handle_user_input(bo->description, EFICONFIG_DESCRIPTION_MAX, 22,
+ "\n ** Edit Description **\n"
+ "\n"
+ " Enter description: ");
+}
+
+/**
+ * eficonfig_boot_add_optional_data() - handle user input for optional data
+ *
+ * @data: pointer to the internal boot option structure
+ * Return: status code
+ */
+static efi_status_t eficonfig_boot_add_optional_data(void *data)
+{
+ struct eficonfig_boot_option *bo = data;
+
+ return handle_user_input(bo->optional_data, EFICONFIG_OPTIONAL_DATA_MAX, 24,
+ "\n ** Edit Optional Data **\n"
+ "\n"
+ " enter optional data:");
+}
+
+/**
+ * eficonfig_boot_add_uri() - handle user input for HTTP Boot URI
+ *
+ * @data: pointer to the internal boot option structure
+ * Return: status code
+ */
+static efi_status_t eficonfig_boot_add_uri(void *data)
+{
+ struct eficonfig_select_file_info *file_info = data;
+
+ return handle_user_input(file_info->uri, EFICONFIG_URI_MAX, 24,
+ "\n ** Edit URI **\n"
+ "\n"
+ " enter HTTP Boot URI:");
+}
+
+/**
+ * eficonfig_boot_edit_save() - handler to save the boot option
+ *
+ * @data: pointer to the internal boot option structure
+ * Return: status code
+ */
+static efi_status_t eficonfig_boot_edit_save(void *data)
+{
+ struct eficonfig_boot_option *bo = data;
+
+ if (u16_strlen(bo->description) == 0) {
+ eficonfig_print_msg("Boot Description is empty!");
+ bo->edit_completed = false;
+ return EFI_NOT_READY;
+ }
+ if (u16_strlen(bo->file_info.current_path) == 0 &&
+ u16_strlen(bo->file_info.uri) == 0) {
+ eficonfig_print_msg("File is not selected!");
+ bo->edit_completed = false;
+ return EFI_NOT_READY;
+ }
+
+ bo->edit_completed = true;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * eficonfig_process_clear_file_selection() - callback function for "Clear" entry
+ *
+ * @data: pointer to the data
+ * Return: status code
+ */
+efi_status_t eficonfig_process_clear_file_selection(void *data)
+{
+ struct eficonfig_select_file_info *file_info = data;
+
+ /* clear the existing file information */
+ file_info->current_volume = NULL;
+ file_info->current_path[0] = u'\0';
+ file_info->dp_volume = NULL;
+
+ if (file_info->uri)
+ file_info->uri[0] = u'\0';
+
+ return EFI_ABORTED;
+}
+
+static struct eficonfig_item select_boot_file_menu_items[] = {
+ {"Select File", eficonfig_process_select_file},
+ {"Enter URI", eficonfig_boot_add_uri},
+ {"Clear", eficonfig_process_clear_file_selection},
+ {"Quit", eficonfig_process_quit},
+};
+
+static struct eficonfig_item select_file_menu_items[] = {
+ {"Select File", eficonfig_process_select_file},
+ {"Clear", eficonfig_process_clear_file_selection},
+ {"Quit", eficonfig_process_quit},
+};
+
+/**
+ * eficonfig_process_show_file_option() - display select file option
+ *
+ * @file_info: pointer to the file information structure
+ * Return: status code
+ */
+efi_status_t eficonfig_process_show_file_option(void *data)
+{
+ efi_status_t ret;
+ unsigned int menu_count;
+ struct efimenu *efi_menu;
+ struct eficonfig_item *menu_items;
+ struct eficonfig_select_file_info *file_info = data;
+
+ menu_items = file_info->uri ? select_boot_file_menu_items :
+ select_file_menu_items;
+
+ menu_count = file_info->uri ?
+ ARRAY_SIZE(select_boot_file_menu_items) :
+ ARRAY_SIZE(select_file_menu_items);
+
+ menu_items[0].data = data;
+ menu_items[1].data = data;
+ menu_items[2].data = data;
+
+ efi_menu = eficonfig_create_fixed_menu(menu_items, menu_count);
+ if (!efi_menu)
+ return EFI_OUT_OF_RESOURCES;
+
+ ret = eficonfig_process_common(efi_menu,
+ file_info->uri ?
+ " ** Update File/URI **" :
+ " ** Update File **",
+ eficonfig_menu_desc,
+ eficonfig_display_statusline,
+ eficonfig_print_entry,
+ eficonfig_choice_entry);
+ if (ret != EFI_SUCCESS) /* User selects "Clear" or "Quit" */
+ ret = EFI_NOT_READY;
+
+ eficonfig_destroy(efi_menu);
+
+ return ret;
+}
+
+/**
+ * eficonfig_process_select_file() - handle user file selection
+ *
+ * @data: pointer to the data
+ * Return: status code
+ */
+efi_status_t eficonfig_process_select_file(void *data)
+{
+ size_t len;
+ efi_status_t ret;
+ struct list_head *pos, *n;
+ struct efi_file_handle *root;
+ struct eficonfig_filepath_info *item;
+ struct eficonfig_select_file_info *tmp = NULL;
+ struct eficonfig_select_file_info *file_info = data;
+
+ tmp = calloc(1, sizeof(struct eficonfig_select_file_info));
+ if (!tmp)
+ return EFI_OUT_OF_RESOURCES;
+
+ tmp->current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
+ if (!tmp->current_path) {
+ free(tmp);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ INIT_LIST_HEAD(&tmp->filepath_list);
+
+ while (!tmp->file_selected) {
+ tmp->current_volume = NULL;
+ memset(tmp->current_path, 0, EFICONFIG_FILE_PATH_BUF_SIZE);
+
+ ret = eficonfig_select_volume(tmp);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (!tmp->current_volume)
+ return EFI_INVALID_PARAMETER;
+
+ ret = EFI_CALL(tmp->current_volume->open_volume(tmp->current_volume, &root));
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = eficonfig_show_file_selection(tmp, root);
+ if (ret == EFI_ABORTED)
+ continue;
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
+out:
+ if (ret == EFI_SUCCESS) {
+ len = u16_strlen(tmp->current_path);
+ len = (len >= EFICONFIG_FILE_PATH_MAX) ? (EFICONFIG_FILE_PATH_MAX - 1) : len;
+ memcpy(file_info->current_path, tmp->current_path, len * sizeof(u16));
+ file_info->current_path[len] = u'\0';
+ file_info->current_volume = tmp->current_volume;
+ file_info->dp_volume = tmp->dp_volume;
+
+ /*
+ * File being selected, set the URI string to
+ * null so that the file gets picked as the
+ * boot image.
+ */
+ if (file_info->uri)
+ file_info->uri[0] = u'\0';
+ }
+
+ list_for_each_safe(pos, n, &tmp->filepath_list) {
+ item = list_entry(pos, struct eficonfig_filepath_info, list);
+ list_del(&item->list);
+ free(item->name);
+ free(item);
+ }
+ free(tmp->current_path);
+ free(tmp);
+
+ /* to stay the parent menu */
+ ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
+
+ return ret;
+}
+
+/**
+ * eficonfig_set_boot_option() - set boot option
+ *
+ * @varname: pointer to variable name
+ * @dp: pointer to device path
+ * @label: pointer to label string
+ * @optional_data: pointer to optional data
+ * Return: status code
+ */
+static efi_status_t eficonfig_set_boot_option(u16 *varname, struct efi_device_path *dp,
+ efi_uintn_t dp_size, u16 *label, char *optional_data)
+{
+ void *p = NULL;
+ efi_status_t ret;
+ efi_uintn_t size;
+ struct efi_load_option lo;
+
+ lo.file_path = dp;
+ lo.file_path_length = dp_size;
+ lo.attributes = LOAD_OPTION_ACTIVE;
+ lo.optional_data = optional_data;
+ lo.label = label;
+
+ size = efi_serialize_load_option(&lo, (u8 **)&p);
+ if (!size)
+ return EFI_INVALID_PARAMETER;
+
+ ret = efi_set_variable_int(varname, &efi_global_variable_guid,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ size, p, false);
+ free(p);
+
+ return ret;
+}
+
+/**
+ * create_boot_option_entry() - create boot option entry
+ *
+ * @efi_menu: pointer to the efimenu structure
+ * @title: pointer to the entry title
+ * @val: pointer to boot option label
+ * @func: callback of each entry
+ * @data: pointer to the data to be passed to each entry callback
+ * Return: status code
+ */
+static efi_status_t create_boot_option_entry(struct efimenu *efi_menu, char *title, u16 *val,
+ eficonfig_entry_func func, void *data)
+{
+ u32 len;
+ char *p, *buf;
+
+ len = strlen(title) + 1;
+ if (val)
+ len += utf16_utf8_strlen(val);
+ buf = calloc(1, len);
+ if (!buf)
+ return EFI_OUT_OF_RESOURCES;
+
+ strcpy(buf, title);
+ if (val) {
+ p = buf + strlen(title);
+ utf16_utf8_strcpy(&p, val);
+ }
+
+ return eficonfig_append_menu_entry(efi_menu, buf, func, data);
+}
+
+/**
+ * prepare_file_selection_entry() - prepare file selection entry
+ *
+ * @efi_menu: pointer to the efimenu structure
+ * @title: pointer to the title string
+ * @file_info: pointer to the file info
+ * Return: status code
+ */
+static efi_status_t prepare_file_selection_entry(struct efimenu *efi_menu, char *title,
+ struct eficonfig_select_file_info *file_info)
+{
+ u32 len;
+ efi_status_t ret;
+ u16 *file_name = NULL, *p;
+ efi_handle_t handle;
+ char *devname;
+
+ /* Check for URI based boot file */
+ if (file_info->uri && utf16_utf8_strlen(file_info->uri))
+ return create_boot_option_entry(efi_menu, title, file_info->uri,
+ eficonfig_process_show_file_option,
+ file_info);
+
+ devname = calloc(1, EFICONFIG_VOLUME_PATH_MAX + 1);
+ if (!devname)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* get the device name only when the user already selected the file path */
+ handle = efi_dp_find_obj(file_info->dp_volume, NULL, NULL);
+ if (handle) {
+ ret = efi_disk_get_device_name(handle, devname, EFICONFIG_VOLUME_PATH_MAX);
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
+ /*
+ * If the preconfigured volume does not exist in the system, display the text
+ * converted volume device path instead of U-Boot friendly name(e.g. "usb 0:1").
+ */
+ if (!handle && file_info->dp_volume) {
+ u16 *dp_str;
+ char *q = devname;
+
+ dp_str = efi_dp_str(file_info->dp_volume);
+ if (dp_str)
+ utf16_utf8_strncpy(&q, dp_str, EFICONFIG_VOLUME_PATH_MAX);
+
+ efi_free_pool(dp_str);
+ }
+
+ /* append u'/' to devname, it is just for display purpose. */
+ if (file_info->current_path[0] != u'\0' && file_info->current_path[0] != u'/')
+ strlcat(devname, "/", EFICONFIG_VOLUME_PATH_MAX + 1);
+
+ len = strlen(devname);
+ len += utf16_utf8_strlen(file_info->current_path) + 1;
+ file_name = calloc(1, len * sizeof(u16));
+ if (!file_name) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ p = file_name;
+ utf8_utf16_strcpy(&p, devname);
+ u16_strlcat(file_name, file_info->current_path, len);
+ ret = create_boot_option_entry(efi_menu, title, file_name,
+ eficonfig_process_show_file_option, file_info);
+out:
+ free(devname);
+ free(file_name);
+
+ return ret;
+}
+
+/**
+ * eficonfig_show_boot_option() - prepare menu entry for editing boot option
+ *
+ * Construct the structures to create edit boot option menu
+ *
+ * @bo: pointer to the boot option
+ * @header_str: pointer to the header string
+ * Return: status code
+ */
+static efi_status_t eficonfig_show_boot_option(struct eficonfig_boot_option *bo,
+ char *header_str)
+{
+ efi_status_t ret;
+ struct efimenu *efi_menu;
+
+ efi_menu = calloc(1, sizeof(struct efimenu));
+ if (!efi_menu)
+ return EFI_OUT_OF_RESOURCES;
+
+ INIT_LIST_HEAD(&efi_menu->list);
+
+ ret = create_boot_option_entry(efi_menu, "Description: ", bo->description,
+ eficonfig_boot_add_enter_description, bo);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = prepare_file_selection_entry(efi_menu, "File: ", &bo->file_info);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = prepare_file_selection_entry(efi_menu, "Initrd File: ", &bo->initrd_info);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = prepare_file_selection_entry(efi_menu, "Fdt File: ", &bo->fdt_info);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = create_boot_option_entry(efi_menu, "Optional Data: ", bo->optional_data,
+ eficonfig_boot_add_optional_data, bo);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = create_boot_option_entry(efi_menu, "Save", NULL,
+ eficonfig_boot_edit_save, bo);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = create_boot_option_entry(efi_menu, "Quit", NULL,
+ eficonfig_process_quit, NULL);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = eficonfig_process_common(efi_menu, header_str,
+ eficonfig_menu_desc,
+ eficonfig_display_statusline,
+ eficonfig_print_entry,
+ eficonfig_choice_entry);
+
+out:
+ eficonfig_destroy(efi_menu);
+
+ return ret;
+}
+
+/**
+ * fill_dp_uri() - copy the URI string in the device path
+ * @dp: pointer to the URI device path
+ * @uri_str: URI string to be copied
+ *
+ * Copy the passed URI string to the URI device path. This
+ * requires utf8_utf16_strcpy() to copy the u16 string to
+ * the u8 array in the device path structure.
+ *
+ * Return: None
+ */
+static void fill_dp_uri(struct efi_device_path *dp, u16 **uri_str)
+{
+ u16 *p = *uri_str;
+ struct efi_device_path_uri *uridp;
+
+ uridp = (struct efi_device_path_uri *)dp;
+
+ utf8_utf16_strcpy(&p, uridp->uri);
+}
+
+/**
+ * fill_file_info() - fill the file info from efi_device_path structure
+ *
+ * @dp: pointer to the device path
+ * @file_info: pointer to the file info structure
+ * @device_dp: pointer to the volume device path
+ */
+static void fill_file_info(struct efi_device_path *dp,
+ struct eficonfig_select_file_info *file_info,
+ struct efi_device_path *device_dp)
+{
+ u16 *file_str, *p;
+ struct efi_device_path *file_dp = NULL;
+
+ efi_dp_split_file_path(dp, &device_dp, &file_dp);
+ file_info->dp_volume = device_dp;
+
+ if (file_dp) {
+ file_str = efi_dp_str(file_dp);
+ /*
+ * efi_convert_device_path_to_text() automatically adds u'/' at the
+ * beginning of file name, remove u'/' before copying to current_path
+ */
+ p = file_str;
+ if (p[0] == u'/')
+ p++;
+
+ u16_strcpy(file_info->current_path, p);
+ efi_free_pool(file_dp);
+ efi_free_pool(file_str);
+ }
+}
+
+/**
+ * eficonfig_edit_boot_option() - prepare boot option structure for editing
+ *
+ * Construct the boot option structure and copy the existing value
+ *
+ * @varname: pointer to the UEFI variable name
+ * @bo: pointer to the boot option
+ * @load_option: pointer to the load option
+ * @load_option_size: size of the load option
+ * @header_str: pointer to the header string
+ * Return : status code
+ */
+static efi_status_t eficonfig_edit_boot_option(u16 *varname, struct eficonfig_boot_option *bo,
+ void *load_option, efi_uintn_t load_option_size,
+ char *header_str)
+{
+ size_t len;
+ efi_status_t ret;
+ char *tmp = NULL, *p;
+ u16 *current_path = NULL;
+ struct efi_load_option lo = {0};
+ efi_uintn_t dp_size;
+ struct efi_device_path *dp = NULL;
+ efi_uintn_t size = load_option_size;
+ struct efi_device_path *dp_volume = NULL;
+ struct efi_device_path *uri_dp = NULL;
+ struct efi_device_path *device_dp = NULL;
+ struct efi_device_path *initrd_dp = NULL;
+ struct efi_device_path *fdt_dp = NULL;
+ struct efi_device_path *initrd_device_dp = NULL;
+ struct efi_device_path *fdt_device_dp = NULL;
+
+ const struct efi_lo_dp_prefix initrd_prefix = {
+ .vendor = {
+ {
+ DEVICE_PATH_TYPE_MEDIA_DEVICE,
+ DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
+ sizeof(initrd_prefix.vendor),
+ },
+ EFI_INITRD_MEDIA_GUID,
+ },
+ .end = {
+ DEVICE_PATH_TYPE_END,
+ DEVICE_PATH_SUB_TYPE_END,
+ sizeof(initrd_prefix.end),
+ }
+ };
+
+ const struct efi_lo_dp_prefix fdt_prefix = {
+ .vendor = {
+ {
+ DEVICE_PATH_TYPE_MEDIA_DEVICE,
+ DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
+ sizeof(fdt_prefix.vendor),
+ },
+ EFI_FDT_GUID,
+ },
+ .end = {
+ DEVICE_PATH_TYPE_END,
+ DEVICE_PATH_SUB_TYPE_END,
+ sizeof(initrd_prefix.end),
+ }
+ };
+
+ bo->file_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
+ if (!bo->file_info.current_path) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ bo->initrd_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
+ if (!bo->initrd_info.current_path) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ bo->fdt_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
+ if (!bo->fdt_info.current_path) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ bo->description = calloc(1, EFICONFIG_DESCRIPTION_MAX * sizeof(u16));
+ if (!bo->description) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ bo->optional_data = calloc(1, EFICONFIG_OPTIONAL_DATA_MAX * sizeof(u16));
+ if (!bo->optional_data) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ bo->file_info.uri = calloc(1, EFICONFIG_URI_MAX * sizeof(u16));
+ if (!bo->file_info.uri) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ /* copy the preset value */
+ if (load_option) {
+ ret = efi_deserialize_load_option(&lo, load_option, &size);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (!lo.label) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /* truncate the long label string */
+ if (u16_strlen(lo.label) >= EFICONFIG_DESCRIPTION_MAX)
+ lo.label[EFICONFIG_DESCRIPTION_MAX - 1] = u'\0';
+
+ u16_strcpy(bo->description, lo.label);
+
+ /* EFI image file path is a first instance */
+ if (lo.file_path && EFI_DP_TYPE(lo.file_path, MESSAGING_DEVICE,
+ MSG_URI))
+ fill_dp_uri(lo.file_path, &bo->file_info.uri);
+ else if (lo.file_path)
+ fill_file_info(lo.file_path, &bo->file_info, device_dp);
+
+ /* Initrd file path (optional) is placed at second instance. */
+ initrd_dp = efi_dp_from_lo(&lo, &efi_lf2_initrd_guid);
+ if (initrd_dp) {
+ fill_file_info(initrd_dp, &bo->initrd_info, initrd_device_dp);
+ efi_free_pool(initrd_dp);
+ }
+
+ /* Fdt file path (optional) is placed as third instance. */
+ fdt_dp = efi_dp_from_lo(&lo, &efi_guid_fdt);
+ if (fdt_dp) {
+ fill_file_info(fdt_dp, &bo->fdt_info, fdt_device_dp);
+ efi_free_pool(fdt_dp);
+ }
+
+ if (size > 0)
+ memcpy(bo->optional_data, lo.optional_data, size);
+ }
+
+ while (1) {
+ ret = eficonfig_show_boot_option(bo, header_str);
+ if (ret == EFI_SUCCESS && bo->edit_completed)
+ break;
+ if (ret == EFI_NOT_READY)
+ continue;
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
+ if (utf16_utf8_strlen(bo->file_info.uri))
+ uri_dp = eficonfig_create_uri_device_path(bo->file_info.uri);
+
+ if (bo->initrd_info.dp_volume) {
+ dp = eficonfig_create_device_path(bo->initrd_info.dp_volume,
+ bo->initrd_info.current_path);
+ if (!dp) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ initrd_dp = efi_dp_concat((const struct efi_device_path *)&initrd_prefix,
+ dp, 0);
+ efi_free_pool(dp);
+ }
+
+ if (bo->fdt_info.dp_volume) {
+ dp = eficonfig_create_device_path(bo->fdt_info.dp_volume,
+ bo->fdt_info.current_path);
+ if (!dp) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ fdt_dp = efi_dp_concat((const struct efi_device_path *)&fdt_prefix,
+ dp, 0);
+ efi_free_pool(dp);
+ }
+
+ dp_volume = bo->file_info.dp_volume;
+ current_path = bo->file_info.current_path;
+ dp = uri_dp ?
+ uri_dp : eficonfig_create_device_path(dp_volume, current_path);
+ if (!dp) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ ret = efi_load_option_dp_join(&dp, &dp_size, initrd_dp, fdt_dp);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (utf16_utf8_strlen(bo->optional_data)) {
+ len = utf16_utf8_strlen(bo->optional_data) + 1;
+ tmp = calloc(1, len);
+ if (!tmp)
+ goto out;
+ p = tmp;
+ utf16_utf8_strncpy(&p, bo->optional_data, u16_strlen(bo->optional_data));
+ }
+
+ ret = eficonfig_set_boot_option(varname, dp, dp_size, bo->description, tmp);
+out:
+ free(tmp);
+ free(bo->optional_data);
+ free(bo->description);
+ free(bo->file_info.uri);
+ free(bo->file_info.current_path);
+ free(bo->initrd_info.current_path);
+ free(bo->fdt_info.current_path);
+ efi_free_pool(device_dp);
+ efi_free_pool(initrd_device_dp);
+ efi_free_pool(initrd_dp);
+ efi_free_pool(fdt_device_dp);
+ efi_free_pool(fdt_dp);
+ efi_free_pool(dp);
+
+ return ret;
+}
+
+/**
+ * eficonfig_process_add_boot_option() - handler to add boot option
+ *
+ * @data: pointer to the data for each entry
+ * Return: status code
+ */
+static efi_status_t eficonfig_process_add_boot_option(void *data)
+{
+ u16 varname[9];
+ efi_status_t ret;
+ struct eficonfig_boot_option *bo = NULL;
+
+ bo = calloc(1, sizeof(struct eficonfig_boot_option));
+ if (!bo)
+ return EFI_OUT_OF_RESOURCES;
+
+ ret = efi_bootmgr_get_unused_bootoption(varname, sizeof(varname), &bo->boot_index);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ ret = eficonfig_edit_boot_option(varname, bo, NULL, 0, " ** Add Boot Option ** ");
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = efi_bootmgr_append_bootorder((u16)bo->boot_index);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+out:
+ free(bo);
+
+ /* to stay the parent menu */
+ ret = (ret == EFI_ABORTED) ? EFI_SUCCESS : ret;
+
+ return ret;
+}
+
+/**
+ * eficonfig_process_boot_selected() - handler to select boot option entry
+ *
+ * @data: pointer to the data for each entry
+ * Return: status code
+ */
+static efi_status_t eficonfig_process_boot_selected(void *data)
+{
+ struct eficonfig_boot_selection_data *info = data;
+
+ if (info)
+ *info->selected = info->boot_index;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * eficonfig_add_boot_selection_entry() - add boot option menu entry
+ *
+ * @efi_menu: pointer to store the efimenu structure
+ * @boot_index: boot option index to be added
+ * @selected: pointer to store the selected boot option index
+ * Return: status code
+ */
+static efi_status_t eficonfig_add_boot_selection_entry(struct efimenu *efi_menu,
+ unsigned int boot_index,
+ unsigned int *selected)
+{
+ char *buf, *p;
+ efi_status_t ret;
+ efi_uintn_t size;
+ void *load_option;
+ struct efi_load_option lo;
+ u16 varname[] = u"Boot####";
+ struct eficonfig_boot_selection_data *info;
+
+ efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index);
+ load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
+ if (!load_option)
+ return EFI_SUCCESS;
+
+ ret = efi_deserialize_load_option(&lo, load_option, &size);
+ if (ret != EFI_SUCCESS) {
+ log_warning("Invalid load option for %ls\n", varname);
+ free(load_option);
+ return ret;
+ }
+
+ if (size >= sizeof(efi_guid_t) &&
+ !guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated)) {
+ /*
+ * auto generated entry has GUID in optional_data,
+ * skip auto generated entry because it will be generated
+ * again even if it is edited or deleted.
+ */
+ free(load_option);
+ return EFI_SUCCESS;
+ }
+
+ info = calloc(1, sizeof(struct eficonfig_boot_selection_data));
+ if (!info) {
+ free(load_option);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ buf = calloc(1, utf16_utf8_strlen(lo.label) + 1);
+ if (!buf) {
+ free(load_option);
+ free(info);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ p = buf;
+ utf16_utf8_strcpy(&p, lo.label);
+ info->boot_index = boot_index;
+ info->selected = selected;
+ ret = eficonfig_append_menu_entry(efi_menu, buf, eficonfig_process_boot_selected, info);
+ if (ret != EFI_SUCCESS) {
+ free(load_option);
+ free(info);
+ return ret;
+ }
+ free(load_option);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * eficonfig_show_boot_selection() - construct boot option menu entry
+ *
+ * @selected: pointer to store the selected boot option index
+ * Return: status code
+ */
+static efi_status_t eficonfig_show_boot_selection(unsigned int *selected)
+{
+ u32 i;
+ u16 *bootorder;
+ efi_status_t ret;
+ u16 *var_name16 = NULL;
+ efi_uintn_t num, size, buf_size;
+ struct efimenu *efi_menu;
+ struct list_head *pos, *n;
+ struct eficonfig_entry *entry;
+
+ efi_menu = calloc(1, sizeof(struct efimenu));
+ if (!efi_menu)
+ return EFI_OUT_OF_RESOURCES;
+
+ bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
+
+ INIT_LIST_HEAD(&efi_menu->list);
+ num = size / sizeof(u16);
+ /* list the load option in the order of BootOrder variable */
+ for (i = 0; i < num; i++) {
+ ret = eficonfig_add_boot_selection_entry(efi_menu, bootorder[i], selected);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
+ break;
+ }
+
+ /* list the remaining load option not included in the BootOrder */
+ buf_size = 128;
+ var_name16 = malloc(buf_size);
+ if (!var_name16)
+ return EFI_OUT_OF_RESOURCES;
+
+ var_name16[0] = 0;
+ for (;;) {
+ int index;
+ efi_guid_t guid;
+
+ ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
+ if (ret == EFI_NOT_FOUND)
+ break;
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (efi_varname_is_load_option(var_name16, &index)) {
+ /* If the index is included in the BootOrder, skip it */
+ if (efi_search_bootorder(bootorder, num, index, NULL))
+ continue;
+
+ ret = eficonfig_add_boot_selection_entry(efi_menu, index, selected);
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
+ if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
+ break;
+ }
+
+ ret = eficonfig_append_quit_entry(efi_menu);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = eficonfig_process_common(efi_menu, " ** Select Boot Option **",
+ eficonfig_menu_desc,
+ eficonfig_display_statusline,
+ eficonfig_print_entry,
+ eficonfig_choice_entry);
+out:
+ list_for_each_safe(pos, n, &efi_menu->list) {
+ entry = list_entry(pos, struct eficonfig_entry, list);
+ free(entry->data);
+ }
+ eficonfig_destroy(efi_menu);
+
+ free(var_name16);
+
+ return ret;
+}
+
+/**
+ * eficonfig_process_edit_boot_option() - handler to edit boot option
+ *
+ * @data: pointer to the data for each entry
+ * Return: status code
+ */
+static efi_status_t eficonfig_process_edit_boot_option(void *data)
+{
+ efi_status_t ret;
+ efi_uintn_t size;
+ struct eficonfig_boot_option *bo = NULL;
+
+ while (1) {
+ unsigned int selected;
+ void *load_option;
+ u16 varname[] = u"Boot####";
+
+ ret = eficonfig_show_boot_selection(&selected);
+ if (ret != EFI_SUCCESS)
+ break;
+
+ bo = calloc(1, sizeof(struct eficonfig_boot_option));
+ if (!bo) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ bo->boot_index = selected;
+ efi_create_indexed_name(varname, sizeof(varname), "Boot", selected);
+ load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
+ if (!load_option) {
+ free(bo);
+ ret = EFI_NOT_FOUND;
+ goto out;
+ }
+
+ ret = eficonfig_edit_boot_option(varname, bo, load_option, size,
+ " ** Edit Boot Option ** ");
+
+ free(load_option);
+ free(bo);
+ if (ret != EFI_SUCCESS && ret != EFI_ABORTED)
+ break;
+ }
+out:
+ /* to stay the parent menu */
+ ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
+
+ return ret;
+}
+
+/**
+ * eficonfig_print_change_boot_order_entry() - print the boot option entry
+ *
+ * @data: pointer to the data associated with each menu entry
+ */
+static void eficonfig_print_change_boot_order_entry(void *data)
+{
+ struct eficonfig_entry *entry = data;
+ bool reverse = (entry->efi_menu->active == entry->num);
+
+ if (entry->efi_menu->start > entry->num || entry->efi_menu->end < entry->num)
+ return;
+
+ printf(ANSI_CURSOR_POSITION ANSI_CLEAR_LINE,
+ (entry->num - entry->efi_menu->start) + EFICONFIG_MENU_HEADER_ROW_NUM + 1, 7);
+
+ if (reverse)
+ puts(ANSI_COLOR_REVERSE);
+
+ if (entry->num < entry->efi_menu->count - 2) {
+ if (((struct eficonfig_boot_order_data *)entry->data)->active)
+ printf("[*] ");
+ else
+ printf("[ ] ");
+ }
+
+ printf("%s", entry->title);
+
+ if (reverse)
+ puts(ANSI_COLOR_RESET);
+}
+
+/**
+ * eficonfig_choice_change_boot_order() - user key input handler
+ *
+ * @data: pointer to the menu entry
+ * Return: key string to identify the selected entry
+ */
+char *eficonfig_choice_change_boot_order(void *data)
+{
+ struct cli_ch_state s_cch, *cch = &s_cch;
+ struct list_head *pos, *n;
+ struct efimenu *efi_menu = data;
+ enum bootmenu_key key = BKEY_NONE;
+ struct eficonfig_entry *entry, *tmp;
+
+ cli_ch_init(cch);
+ while (1) {
+ key = bootmenu_loop(NULL, cch);
+
+ switch (key) {
+ case BKEY_PLUS:
+ if (efi_menu->active > 0 &&
+ efi_menu->active < efi_menu->count - 2) {
+ list_for_each_safe(pos, n, &efi_menu->list) {
+ entry = list_entry(pos, struct eficonfig_entry, list);
+ if (entry->num == efi_menu->active)
+ break;
+ }
+ tmp = list_entry(pos->prev, struct eficonfig_entry, list);
+ entry->num--;
+ tmp->num++;
+ list_del(&tmp->list);
+ list_add(&tmp->list, &entry->list);
+
+ eficonfig_menu_up(efi_menu);
+ }
+ return NULL;
+ case BKEY_UP:
+ if (efi_menu->active > 0)
+ eficonfig_menu_up(efi_menu);
+
+ return NULL;
+ case BKEY_MINUS:
+ if (efi_menu->active < efi_menu->count - 3) {
+ list_for_each_safe(pos, n, &efi_menu->list) {
+ entry = list_entry(pos, struct eficonfig_entry, list);
+ if (entry->num == efi_menu->active)
+ break;
+ }
+ tmp = list_entry(pos->next, struct eficonfig_entry, list);
+ entry->num++;
+ tmp->num--;
+ list_del(&entry->list);
+ list_add(&entry->list, &tmp->list);
+
+ eficonfig_menu_down(efi_menu);
+ }
+ return NULL;
+ case BKEY_DOWN:
+ if (efi_menu->active < efi_menu->count - 1)
+ eficonfig_menu_down(efi_menu);
+
+ return NULL;
+ case BKEY_SAVE:
+ /* force to select "Save" entry */
+ efi_menu->active = efi_menu->count - 2;
+ fallthrough;
+ case BKEY_SELECT:
+ /* "Save" */
+ if (efi_menu->active == efi_menu->count - 2) {
+ list_for_each_prev_safe(pos, n, &efi_menu->list) {
+ entry = list_entry(pos, struct eficonfig_entry, list);
+ if (entry->num == efi_menu->active)
+ break;
+ }
+ return entry->key;
+ }
+ /* "Quit" */
+ if (efi_menu->active == efi_menu->count - 1) {
+ entry = list_last_entry(&efi_menu->list,
+ struct eficonfig_entry,
+ list);
+ return entry->key;
+ }
+ /* Pressed key is not valid, wait next key press */
+ break;
+ case BKEY_SPACE:
+ if (efi_menu->active < efi_menu->count - 2) {
+ list_for_each_safe(pos, n, &efi_menu->list) {
+ entry = list_entry(pos, struct eficonfig_entry, list);
+ if (entry->num == efi_menu->active) {
+ struct eficonfig_boot_order_data *data = entry->data;
+
+ data->active = !data->active;
+ return NULL;
+ }
+ }
+ }
+ /* Pressed key is not valid, wait next key press */
+ break;
+ case BKEY_QUIT:
+ entry = list_last_entry(&efi_menu->list,
+ struct eficonfig_entry, list);
+ return entry->key;
+ default:
+ /* Pressed key is not valid, wait next key press */
+ break;
+ }
+ }
+}
+
+/**
+ * eficonfig_process_save_boot_order() - callback function for "Save" entry
+ *
+ * @data: pointer to the data
+ * Return: status code
+ */
+static efi_status_t eficonfig_process_save_boot_order(void *data)
+{
+ u32 count = 0;
+ efi_status_t ret;
+ efi_uintn_t size;
+ struct list_head *pos, *n;
+ u16 *new_bootorder;
+ struct efimenu *efi_menu;
+ struct eficonfig_entry *entry;
+ struct eficonfig_save_boot_order_data *save_data = data;
+
+ efi_menu = save_data->efi_menu;
+
+ /*
+ * The change boot order menu always has "Save" and "Quit" entries.
+ * !(efi_menu->count - 2) means there is no user defined boot option.
+ */
+ if (!(efi_menu->count - 2))
+ return EFI_SUCCESS;
+
+ new_bootorder = calloc(1, (efi_menu->count - 2) * sizeof(u16));
+ if (!new_bootorder) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ /* create new BootOrder */
+ count = 0;
+ list_for_each_safe(pos, n, &efi_menu->list) {
+ struct eficonfig_boot_order_data *data;
+
+ entry = list_entry(pos, struct eficonfig_entry, list);
+ /* exit the loop when iteration reaches "Save" */
+ if (!strncmp(entry->title, "Save", strlen("Save")))
+ break;
+
+ data = entry->data;
+ if (data->active)
+ new_bootorder[count++] = data->boot_index;
+ }
+
+ size = count * sizeof(u16);
+ ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ size, new_bootorder, false);
+
+ save_data->selected = true;
+out:
+ free(new_bootorder);
+
+ return ret;
+}
+
+/**
+ * eficonfig_add_change_boot_order_entry() - add boot order entry
+ *
+ * @efi_menu: pointer to the efimenu structure
+ * @boot_index: boot option index to be added
+ * @active: flag to include the boot option into BootOrder
+ * Return: status code
+ */
+static efi_status_t eficonfig_add_change_boot_order_entry(struct efimenu *efi_menu,
+ u32 boot_index, bool active)
+{
+ char *title, *p;
+ efi_status_t ret;
+ efi_uintn_t size;
+ void *load_option;
+ struct efi_load_option lo;
+ u16 varname[] = u"Boot####";
+ struct eficonfig_boot_order_data *data;
+
+ efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index);
+ load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
+ if (!load_option)
+ return EFI_SUCCESS;
+
+ ret = efi_deserialize_load_option(&lo, load_option, &size);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ data = calloc(1, sizeof(*data));
+ if (!data) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ title = calloc(1, utf16_utf8_strlen(lo.label) + 1);
+ if (!title) {
+ free(data);
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ p = title;
+ utf16_utf8_strcpy(&p, lo.label);
+
+ data->boot_index = boot_index;
+ data->active = active;
+
+ ret = eficonfig_append_menu_entry(efi_menu, title, NULL, data);
+ if (ret != EFI_SUCCESS) {
+ free(data);
+ free(title);
+ goto out;
+ }
+
+out:
+ free(load_option);
+
+ return ret;
+}
+
+/**
+ * eficonfig_create_change_boot_order_entry() - create boot order entry
+ *
+ * @efi_menu: pointer to the efimenu structure
+ * @bootorder: pointer to the BootOrder variable
+ * @num: number of BootOrder entry
+ * Return: status code
+ */
+static efi_status_t eficonfig_create_change_boot_order_entry(struct efimenu *efi_menu,
+ u16 *bootorder, efi_uintn_t num)
+{
+ u32 i;
+ char *title;
+ efi_status_t ret;
+ u16 *var_name16 = NULL;
+ efi_uintn_t size, buf_size;
+ struct eficonfig_save_boot_order_data *save_data;
+
+ /* list the load option in the order of BootOrder variable */
+ for (i = 0; i < num; i++) {
+ if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2)
+ break;
+
+ ret = eficonfig_add_change_boot_order_entry(efi_menu, bootorder[i], true);
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
+ /* list the remaining load option not included in the BootOrder */
+ buf_size = 128;
+ var_name16 = malloc(buf_size);
+ if (!var_name16)
+ return EFI_OUT_OF_RESOURCES;
+
+ var_name16[0] = 0;
+ for (;;) {
+ int index;
+ efi_guid_t guid;
+
+ if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2)
+ break;
+
+ size = buf_size;
+ ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
+ if (ret == EFI_NOT_FOUND)
+ break;
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (efi_varname_is_load_option(var_name16, &index)) {
+ /* If the index is included in the BootOrder, skip it */
+ if (efi_search_bootorder(bootorder, num, index, NULL))
+ continue;
+
+ ret = eficonfig_add_change_boot_order_entry(efi_menu, index, false);
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+ }
+
+ /* add "Save" and "Quit" entries */
+ title = strdup("Save");
+ if (!title) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ save_data = malloc(sizeof(struct eficonfig_save_boot_order_data));
+ if (!save_data) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ save_data->efi_menu = efi_menu;
+ save_data->selected = false;
+
+ ret = eficonfig_append_menu_entry(efi_menu, title,
+ eficonfig_process_save_boot_order,
+ save_data);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = eficonfig_append_quit_entry(efi_menu);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ efi_menu->active = 0;
+out:
+ free(var_name16);
+
+ return ret;
+}
+
+/**
+ * eficonfig_process_change_boot_order() - handler to change boot order
+ *
+ * @data: pointer to the data for each entry
+ * Return: status code
+ */
+static efi_status_t eficonfig_process_change_boot_order(void *data)
+{
+ u16 *bootorder;
+ efi_status_t ret;
+ efi_uintn_t num, size;
+ struct list_head *pos, *n;
+ struct eficonfig_entry *entry;
+ struct efimenu *efi_menu;
+
+ efi_menu = calloc(1, sizeof(struct efimenu));
+ if (!efi_menu)
+ return EFI_OUT_OF_RESOURCES;
+
+ bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
+
+ INIT_LIST_HEAD(&efi_menu->list);
+ num = size / sizeof(u16);
+ ret = eficonfig_create_change_boot_order_entry(efi_menu, bootorder, num);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ while (1) {
+ ret = eficonfig_process_common(efi_menu,
+ " ** Change Boot Order **",
+ eficonfig_change_boot_order_desc,
+ eficonfig_display_statusline,
+ eficonfig_print_change_boot_order_entry,
+ eficonfig_choice_change_boot_order);
+ /* exit from the menu if user selects the "Save" entry. */
+ if (ret == EFI_SUCCESS && efi_menu->active == (efi_menu->count - 2)) {
+ list_for_each_prev_safe(pos, n, &efi_menu->list) {
+ entry = list_entry(pos, struct eficonfig_entry, list);
+ if (entry->num == efi_menu->active)
+ break;
+ }
+ if (((struct eficonfig_save_boot_order_data *)entry->data)->selected)
+ break;
+ }
+ if (ret != EFI_SUCCESS)
+ break;
+ }
+out:
+ free(bootorder);
+ list_for_each_safe(pos, n, &efi_menu->list) {
+ entry = list_entry(pos, struct eficonfig_entry, list);
+ free(entry->data);
+ }
+ eficonfig_destroy(efi_menu);
+
+ /* to stay the parent menu */
+ ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
+
+ return ret;
+}
+
+/**
+ * eficonfig_process_delete_boot_option() - handler to delete boot option
+ *
+ * @data: pointer to the data for each entry
+ * Return: status code
+ */
+static efi_status_t eficonfig_process_delete_boot_option(void *data)
+{
+ efi_status_t ret;
+ unsigned int selected;
+
+ while (1) {
+ ret = eficonfig_show_boot_selection(&selected);
+ if (ret == EFI_SUCCESS)
+ ret = efi_bootmgr_delete_boot_option(selected);
+
+ if (ret != EFI_SUCCESS)
+ break;
+ }
+
+ /* to stay the parent menu */
+ ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
+
+ return ret;
+}
+
+/**
+ * eficonfig_init() - do required initialization for eficonfig command
+ *
+ * Return: status code
+ */
+static efi_status_t eficonfig_init(void)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ static bool init;
+ unsigned long columns, rows;
+
+ if (!init) {
+ cout = systab.con_out;
+ cin = systab.con_in;
+
+ cout->query_mode(cout, cout->mode->mode, &columns, &rows);
+ avail_row = rows - (EFICONFIG_MENU_HEADER_ROW_NUM +
+ EFICONFIG_MENU_DESC_ROW_NUM);
+ if (avail_row <= 0) {
+ eficonfig_print_msg("Console size is too small!");
+ return EFI_INVALID_PARAMETER;
+ }
+ /* TODO: Should we check the minimum column size? */
+ }
+
+ init = true;
+
+ return ret;
+}
+
+static const struct eficonfig_item maintenance_menu_items[] = {
+ {"Add Boot Option", eficonfig_process_add_boot_option},
+ {"Edit Boot Option", eficonfig_process_edit_boot_option},
+ {"Change Boot Order", eficonfig_process_change_boot_order},
+ {"Delete Boot Option", eficonfig_process_delete_boot_option},
+#if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT) && IS_ENABLED(CONFIG_EFI_MM_COMM_TEE))
+ {"Secure Boot Configuration", eficonfig_process_secure_boot_config},
+#endif
+ {"Quit", eficonfig_process_quit},
+};
+
+/**
+ * do_eficonfig() - execute `eficonfig` command
+ *
+ * @cmdtp: table entry describing command
+ * @flag: bitmap indicating how the command was invoked
+ * @argc: number of arguments
+ * @argv: command line arguments
+ * Return: status code
+ */
+static int do_eficonfig(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ efi_status_t ret;
+ struct efimenu *efi_menu;
+
+ if (argc > 1)
+ return CMD_RET_USAGE;
+
+ ret = efi_init_obj_list();
+ if (ret != EFI_SUCCESS) {
+ log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
+ ret & ~EFI_ERROR_MASK);
+
+ return CMD_RET_FAILURE;
+ }
+
+ ret = eficonfig_init();
+ if (ret != EFI_SUCCESS)
+ return CMD_RET_FAILURE;
+
+ ret = efi_bootmgr_update_media_device_boot_option();
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ while (1) {
+ efi_menu = eficonfig_create_fixed_menu(maintenance_menu_items,
+ ARRAY_SIZE(maintenance_menu_items));
+ if (!efi_menu)
+ return CMD_RET_FAILURE;
+
+ ret = eficonfig_process_common(efi_menu,
+ " ** UEFI Maintenance Menu **",
+ eficonfig_menu_desc,
+ eficonfig_display_statusline,
+ eficonfig_print_entry,
+ eficonfig_choice_entry);
+ eficonfig_destroy(efi_menu);
+
+ if (ret == EFI_ABORTED)
+ break;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+ eficonfig, 1, 0, do_eficonfig,
+ "provide menu-driven UEFI variable maintenance interface",
+ ""
+);
diff --git a/cmd/eficonfig_sbkey.c b/cmd/eficonfig_sbkey.c
new file mode 100644
index 00000000000..b3325a540f9
--- /dev/null
+++ b/cmd/eficonfig_sbkey.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Menu-driven UEFI Secure Boot Key Maintenance
+ *
+ * Copyright (c) 2022 Masahisa Kojima, Linaro Limited
+ */
+
+#include <ansi.h>
+#include <charset.h>
+#include <hexdump.h>
+#include <log.h>
+#include <malloc.h>
+#include <menu.h>
+#include <efi_loader.h>
+#include <efi_config.h>
+#include <efi_variable.h>
+#include <crypto/pkcs7_parser.h>
+
+struct eficonfig_sig_data {
+ struct efi_signature_list *esl;
+ struct efi_signature_data *esd;
+ struct list_head list;
+ u16 *varname;
+};
+
+enum efi_sbkey_signature_type {
+ SIG_TYPE_X509 = 0,
+ SIG_TYPE_HASH,
+ SIG_TYPE_CRL,
+ SIG_TYPE_RSA2048,
+};
+
+struct eficonfig_sigtype_to_str {
+ efi_guid_t sig_type;
+ char *str;
+ enum efi_sbkey_signature_type type;
+};
+
+static const struct eficonfig_sigtype_to_str sigtype_to_str[] = {
+ {EFI_CERT_X509_GUID, "X509", SIG_TYPE_X509},
+ {EFI_CERT_SHA256_GUID, "SHA256", SIG_TYPE_HASH},
+ {EFI_CERT_X509_SHA256_GUID, "X509_SHA256 CRL", SIG_TYPE_CRL},
+ {EFI_CERT_X509_SHA384_GUID, "X509_SHA384 CRL", SIG_TYPE_CRL},
+ {EFI_CERT_X509_SHA512_GUID, "X509_SHA512 CRL", SIG_TYPE_CRL},
+ /* U-Boot does not support the following signature types */
+/* {EFI_CERT_RSA2048_GUID, "RSA2048", SIG_TYPE_RSA2048}, */
+/* {EFI_CERT_RSA2048_SHA256_GUID, "RSA2048_SHA256", SIG_TYPE_RSA2048}, */
+/* {EFI_CERT_SHA1_GUID, "SHA1", SIG_TYPE_HASH}, */
+/* {EFI_CERT_RSA2048_SHA_GUID, "RSA2048_SHA", SIG_TYPE_RSA2048 }, */
+/* {EFI_CERT_SHA224_GUID, "SHA224", SIG_TYPE_HASH}, */
+/* {EFI_CERT_SHA384_GUID, "SHA384", SIG_TYPE_HASH}, */
+/* {EFI_CERT_SHA512_GUID, "SHA512", SIG_TYPE_HASH}, */
+};
+
+/**
+ * file_have_auth_header() - check file has EFI_VARIABLE_AUTHENTICATION_2 header
+ * @buf: pointer to file
+ * @size: file size
+ * Return: true if file has auth header, false otherwise
+ */
+static bool file_have_auth_header(void *buf, efi_uintn_t size)
+{
+ struct efi_variable_authentication_2 *auth = buf;
+
+ if (auth->auth_info.hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID)
+ return false;
+
+ if (guidcmp(&auth->auth_info.cert_type, &efi_guid_cert_type_pkcs7))
+ return false;
+
+ return true;
+}
+
+/**
+ * file_is_null_key() - check the file is an authenticated and signed null key
+ *
+ * @auth: pointer to the file
+ * @size: file size
+ * @null_key: pointer to store the result
+ * Return: status code
+ */
+static efi_status_t file_is_null_key(struct efi_variable_authentication_2 *auth,
+ efi_uintn_t size, bool *null_key)
+{
+ efi_uintn_t auth_size =
+ sizeof(auth->time_stamp) + auth->auth_info.hdr.dwLength;
+
+ if (size < auth_size)
+ return EFI_INVALID_PARAMETER;
+
+ *null_key = (size == auth_size);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * eficonfig_process_enroll_key() - enroll key into signature database
+ *
+ * @data: pointer to the data for each entry
+ * Return: status code
+ */
+static efi_status_t eficonfig_process_enroll_key(void *data)
+{
+ u32 attr;
+ char *buf = NULL;
+ efi_uintn_t size;
+ efi_status_t ret;
+ bool null_key = false;
+ struct efi_file_handle *f = NULL;
+ struct efi_device_path *full_dp = NULL;
+ struct eficonfig_select_file_info file_info;
+
+ file_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
+ if (!file_info.current_path) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ ret = eficonfig_process_select_file(&file_info);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ full_dp = eficonfig_create_device_path(file_info.dp_volume, file_info.current_path);
+ if (!full_dp) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ f = efi_file_from_path(full_dp);
+ if (!f) {
+ ret = EFI_NOT_FOUND;
+ goto out;
+ }
+
+ size = 0;
+ ret = EFI_CALL(f->getinfo(f, &efi_file_info_guid, &size, NULL));
+ if (ret != EFI_BUFFER_TOO_SMALL)
+ goto out;
+
+ buf = malloc(size);
+ if (!buf) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ ret = EFI_CALL(f->getinfo(f, &efi_file_info_guid, &size, buf));
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ size = ((struct efi_file_info *)buf)->file_size;
+ free(buf);
+
+ if (!size) {
+ eficonfig_print_msg("ERROR! File is empty.");
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ buf = malloc(size);
+ if (!buf) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ ret = EFI_CALL(f->read(f, &size, buf));
+ if (ret != EFI_SUCCESS) {
+ eficonfig_print_msg("ERROR! Failed to read file.");
+ goto out;
+ }
+ if (!file_have_auth_header(buf, size)) {
+ eficonfig_print_msg("ERROR! Invalid file format. Only .auth variables is allowed.");
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = file_is_null_key((struct efi_variable_authentication_2 *)buf,
+ size, &null_key);
+ if (ret != EFI_SUCCESS) {
+ eficonfig_print_msg("ERROR! Invalid file format.");
+ goto out;
+ }
+
+ attr = EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
+
+ /*
+ * PK can enroll only one certificate.
+ * The signed null key is used to clear KEK, db and dbx.
+ * EFI_VARIABLE_APPEND_WRITE attribute must not be set in these cases.
+ */
+ if (u16_strcmp(data, u"PK") && !null_key) {
+ efi_uintn_t db_size = 0;
+
+ /* check the variable exists. If exists, add APPEND_WRITE attribute */
+ ret = efi_get_variable_int(data, efi_auth_var_get_guid(data), NULL,
+ &db_size, NULL, NULL);
+ if (ret == EFI_BUFFER_TOO_SMALL)
+ attr |= EFI_VARIABLE_APPEND_WRITE;
+ }
+
+ ret = efi_set_variable_int((u16 *)data, efi_auth_var_get_guid((u16 *)data),
+ attr, size, buf, false);
+ if (ret != EFI_SUCCESS)
+ eficonfig_print_msg("ERROR! Failed to update signature database");
+
+out:
+ free(file_info.current_path);
+ free(buf);
+ efi_free_pool(full_dp);
+ if (f)
+ EFI_CALL(f->close(f));
+
+ /* return to the parent menu */
+ ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
+
+ return ret;
+}
+
+/**
+ * eficonfig_process_show_siglist() - show signature list content
+ *
+ * @data: pointer to the data for each entry
+ * Return: status code
+ */
+static efi_status_t eficonfig_process_show_siglist(void *data)
+{
+ u32 i;
+ struct eficonfig_sig_data *sg = data;
+
+ puts(ANSI_CURSOR_HIDE);
+ puts(ANSI_CLEAR_CONSOLE);
+ printf(ANSI_CURSOR_POSITION, 1, 1);
+
+ printf("\n ** Show Signature Database (%ls) **\n\n"
+ " Owner GUID:\n"
+ " %pUL\n",
+ sg->varname, sg->esd->signature_owner.b);
+
+ for (i = 0; i < ARRAY_SIZE(sigtype_to_str); i++) {
+ if (!guidcmp(&sg->esl->signature_type, &sigtype_to_str[i].sig_type)) {
+ printf(" Signature Type:\n"
+ " %s\n", sigtype_to_str[i].str);
+
+ switch (sigtype_to_str[i].type) {
+ case SIG_TYPE_X509:
+ {
+ struct x509_certificate *cert_tmp;
+
+ cert_tmp = x509_cert_parse(sg->esd->signature_data,
+ sg->esl->signature_size);
+ printf(" Subject:\n"
+ " %s\n"
+ " Issuer:\n"
+ " %s\n",
+ cert_tmp->subject, cert_tmp->issuer);
+ break;
+ }
+ case SIG_TYPE_CRL:
+ {
+ u32 hash_size = sg->esl->signature_size - sizeof(efi_guid_t) -
+ sizeof(struct efi_time);
+ struct efi_time *time =
+ (struct efi_time *)((u8 *)sg->esd->signature_data +
+ hash_size);
+
+ printf(" ToBeSignedHash:\n");
+ print_hex_dump(" ", DUMP_PREFIX_NONE, 16, 1,
+ sg->esd->signature_data, hash_size, false);
+ printf(" TimeOfRevocation:\n"
+ " %d-%d-%d %02d:%02d:%02d\n",
+ time->year, time->month, time->day,
+ time->hour, time->minute, time->second);
+ break;
+ }
+ case SIG_TYPE_HASH:
+ {
+ u32 hash_size = sg->esl->signature_size - sizeof(efi_guid_t);
+
+ printf(" Hash:\n");
+ print_hex_dump(" ", DUMP_PREFIX_NONE, 16, 1,
+ sg->esd->signature_data, hash_size, false);
+ break;
+ }
+ default:
+ eficonfig_print_msg("ERROR! Unsupported format.");
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ while (tstc())
+ getchar();
+
+ printf("\n\n Press any key to continue");
+ getchar();
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * prepare_signature_list_menu() - create the signature list menu entry
+ *
+ * @efimenu: pointer to the efimenu structure
+ * @varname: pointer to the variable name
+ * @db: pointer to the variable raw data
+ * @db_size: variable data size
+ * @func: callback of each entry
+ * Return: status code
+ */
+static efi_status_t prepare_signature_list_menu(struct efimenu *efi_menu, void *varname,
+ void *db, efi_uintn_t db_size,
+ eficonfig_entry_func func)
+{
+ u32 num = 0;
+ efi_uintn_t size;
+ struct eficonfig_sig_data *sg;
+ struct efi_signature_list *esl;
+ struct efi_signature_data *esd;
+ efi_status_t ret = EFI_SUCCESS;
+
+ INIT_LIST_HEAD(&efi_menu->list);
+
+ esl = db;
+ size = db_size;
+ while (size > 0) {
+ u32 remain;
+
+ esd = (struct efi_signature_data *)((u8 *)esl +
+ (sizeof(struct efi_signature_list) +
+ esl->signature_header_size));
+ remain = esl->signature_list_size - sizeof(struct efi_signature_list) -
+ esl->signature_header_size;
+ for (; remain > 0; remain -= esl->signature_size) {
+ char buf[37];
+ char *title;
+
+ if (num >= EFICONFIG_ENTRY_NUM_MAX - 1) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ sg = calloc(1, sizeof(struct eficonfig_sig_data));
+ if (!sg) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
+ }
+
+ snprintf(buf, sizeof(buf), "%pUL", &esd->signature_owner);
+ title = strdup(buf);
+ if (!title) {
+ free(sg);
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
+ }
+
+ sg->esl = esl;
+ sg->esd = esd;
+ sg->varname = varname;
+ ret = eficonfig_append_menu_entry(efi_menu, title, func, sg);
+ if (ret != EFI_SUCCESS) {
+ free(sg);
+ free(title);
+ goto err;
+ }
+ esd = (struct efi_signature_data *)((u8 *)esd + esl->signature_size);
+ num++;
+ }
+
+ size -= esl->signature_list_size;
+ esl = (struct efi_signature_list *)((u8 *)esl + esl->signature_list_size);
+ }
+out:
+ ret = eficonfig_append_quit_entry(efi_menu);
+err:
+ return ret;
+}
+
+/**
+ * enumerate_and_show_signature_database() - enumerate and show the signature database
+ *
+ * @data: pointer to the data for each entry
+ * Return: status code
+ */
+static efi_status_t enumerate_and_show_signature_database(void *varname)
+{
+ void *db;
+ char buf[50];
+ efi_status_t ret;
+ efi_uintn_t db_size;
+ struct efimenu *efi_menu;
+ struct list_head *pos, *n;
+ struct eficonfig_entry *entry;
+
+ db = efi_get_var(varname, efi_auth_var_get_guid(varname), &db_size);
+ if (!db) {
+ eficonfig_print_msg("There is no entry in the signature database.");
+ return EFI_NOT_FOUND;
+ }
+
+ efi_menu = calloc(1, sizeof(struct efimenu));
+ if (!efi_menu) {
+ free(db);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ret = prepare_signature_list_menu(efi_menu, varname, db, db_size,
+ eficonfig_process_show_siglist);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ snprintf(buf, sizeof(buf), " ** Show Signature Database (%ls) **", (u16 *)varname);
+ ret = eficonfig_process_common(efi_menu, buf, eficonfig_menu_desc,
+ eficonfig_display_statusline,
+ eficonfig_print_entry,
+ eficonfig_choice_entry);
+out:
+ list_for_each_safe(pos, n, &efi_menu->list) {
+ entry = list_entry(pos, struct eficonfig_entry, list);
+ free(entry->data);
+ }
+ eficonfig_destroy(efi_menu);
+ free(db);
+
+ return ret;
+}
+
+/**
+ * eficonfig_process_show_signature_database() - process show signature database
+ *
+ * @data: pointer to the data for each entry
+ * Return: status code
+ */
+static efi_status_t eficonfig_process_show_signature_database(void *data)
+{
+ efi_status_t ret;
+
+ while (1) {
+ ret = enumerate_and_show_signature_database(data);
+ if (ret != EFI_SUCCESS && ret != EFI_NOT_READY)
+ break;
+ }
+
+ /* return to the parent menu */
+ ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
+
+ return ret;
+}
+
+static struct eficonfig_item key_config_menu_items[] = {
+ {"Enroll New Key", eficonfig_process_enroll_key},
+ {"Show Signature Database", eficonfig_process_show_signature_database},
+ {"Quit", eficonfig_process_quit},
+};
+
+/**
+ * eficonfig_process_set_secure_boot_key() - display the key configuration menu
+ *
+ * @data: pointer to the data for each entry
+ * Return: status code
+ */
+static efi_status_t eficonfig_process_set_secure_boot_key(void *data)
+{
+ u32 i;
+ efi_status_t ret;
+ char header_str[32];
+ struct efimenu *efi_menu;
+
+ for (i = 0; i < ARRAY_SIZE(key_config_menu_items); i++)
+ key_config_menu_items[i].data = data;
+
+ snprintf(header_str, sizeof(header_str), " ** Configure %ls **", (u16 *)data);
+
+ while (1) {
+ efi_menu = eficonfig_create_fixed_menu(key_config_menu_items,
+ ARRAY_SIZE(key_config_menu_items));
+
+ ret = eficonfig_process_common(efi_menu, header_str,
+ eficonfig_menu_desc,
+ eficonfig_display_statusline,
+ eficonfig_print_entry,
+ eficonfig_choice_entry);
+ eficonfig_destroy(efi_menu);
+
+ if (ret == EFI_ABORTED)
+ break;
+ }
+
+ /* return to the parent menu */
+ ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
+
+ return ret;
+}
+
+static const struct eficonfig_item secure_boot_menu_items[] = {
+ {"PK", eficonfig_process_set_secure_boot_key, u"PK"},
+ {"KEK", eficonfig_process_set_secure_boot_key, u"KEK"},
+ {"db", eficonfig_process_set_secure_boot_key, u"db"},
+ {"dbx", eficonfig_process_set_secure_boot_key, u"dbx"},
+ {"Quit", eficonfig_process_quit},
+};
+
+/**
+ * eficonfig_process_secure_boot_config() - display the key list menu
+ *
+ * @data: pointer to the data for each entry
+ * Return: status code
+ */
+efi_status_t eficonfig_process_secure_boot_config(void *data)
+{
+ efi_status_t ret;
+ struct efimenu *efi_menu;
+
+ while (1) {
+ char header_str[64];
+
+ snprintf(header_str, sizeof(header_str),
+ " ** UEFI Secure Boot Key Configuration (SecureBoot : %s) **",
+ (efi_secure_boot_enabled() ? "ON" : "OFF"));
+
+ efi_menu = eficonfig_create_fixed_menu(secure_boot_menu_items,
+ ARRAY_SIZE(secure_boot_menu_items));
+ if (!efi_menu) {
+ ret = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+
+ ret = eficonfig_process_common(efi_menu, header_str,
+ eficonfig_menu_desc,
+ eficonfig_display_statusline,
+ eficonfig_print_entry,
+ eficonfig_choice_entry);
+
+ eficonfig_destroy(efi_menu);
+
+ if (ret == EFI_ABORTED)
+ break;
+ }
+
+ /* return to the parent menu */
+ ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
+
+ return ret;
+}
diff --git a/cmd/efidebug.c b/cmd/efidebug.c
new file mode 100644
index 00000000000..109496d9e95
--- /dev/null
+++ b/cmd/efidebug.c
@@ -0,0 +1,1700 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * UEFI Shell-like command
+ *
+ * Copyright (c) 2018 AKASHI Takahiro, Linaro Limited
+ */
+
+#include <charset.h>
+#include <command.h>
+#include <dm/device.h>
+#include <efi_device_path.h>
+#include <efi_dt_fixup.h>
+#include <efi_load_initrd.h>
+#include <efi_loader.h>
+#include <efi_rng.h>
+#include <efi_variable.h>
+#include <exports.h>
+#include <hexdump.h>
+#include <log.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <net.h>
+#include <part.h>
+#include <search.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+
+#define BS systab.boottime
+
+#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
+/**
+ * do_efi_capsule_update() - process a capsule update
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "capsule update" sub-command.
+ * process a capsule update.
+ *
+ * efidebug capsule update [-v] <capsule address>
+ */
+static int do_efi_capsule_update(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct efi_capsule_header *capsule;
+ int verbose = 0;
+ char *endp;
+ efi_status_t ret;
+
+ if (argc != 2 && argc != 3)
+ return CMD_RET_USAGE;
+
+ if (argc == 3) {
+ if (strcmp(argv[1], "-v"))
+ return CMD_RET_USAGE;
+
+ verbose = 1;
+ argc--;
+ argv++;
+ }
+
+ capsule = (typeof(capsule))hextoul(argv[1], &endp);
+ if (endp == argv[1]) {
+ printf("Invalid address: %s", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ if (verbose) {
+ printf("Capsule guid: %pUl\n", &capsule->capsule_guid);
+ printf("Capsule flags: 0x%x\n", capsule->flags);
+ printf("Capsule header size: 0x%x\n", capsule->header_size);
+ printf("Capsule image size: 0x%x\n",
+ capsule->capsule_image_size);
+ }
+
+ ret = EFI_CALL(efi_update_capsule(&capsule, 1, 0));
+ if (ret) {
+ printf("Cannot handle a capsule at %p\n", capsule);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+#ifdef CONFIG_EFI_CAPSULE_ON_DISK
+static int do_efi_capsule_on_disk_update(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ efi_status_t ret;
+
+ ret = efi_launch_capsules();
+
+ return ret == EFI_SUCCESS ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+#endif
+
+/**
+ * do_efi_capsule_show() - show capsule information
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "capsule show" sub-command.
+ * show capsule information.
+ *
+ * efidebug capsule show <capsule address>
+ */
+static int do_efi_capsule_show(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct efi_capsule_header *capsule;
+ char *endp;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ capsule = (typeof(capsule))hextoul(argv[1], &endp);
+ if (endp == argv[1]) {
+ printf("Invalid address: %s", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Capsule guid: %pUl\n", &capsule->capsule_guid);
+ printf("Capsule flags: 0x%x\n", capsule->flags);
+ printf("Capsule header size: 0x%x\n", capsule->header_size);
+ printf("Capsule image size: 0x%x\n",
+ capsule->capsule_image_size);
+
+ return CMD_RET_SUCCESS;
+}
+
+#ifdef CONFIG_EFI_ESRT
+
+#define EFI_ESRT_FW_TYPE_NUM 4
+char *efi_fw_type_str[EFI_ESRT_FW_TYPE_NUM] = {"unknown", "system FW", "device FW",
+ "UEFI driver"};
+
+#define EFI_ESRT_UPDATE_STATUS_NUM 9
+char *efi_update_status_str[EFI_ESRT_UPDATE_STATUS_NUM] = {"success", "unsuccessful",
+ "insufficient resources", "incorrect version", "invalid format",
+ "auth error", "power event (AC)", "power event (batt)",
+ "unsatisfied dependencies"};
+
+#define EFI_FW_TYPE_STR_GET(idx) (\
+EFI_ESRT_FW_TYPE_NUM > (idx) ? efi_fw_type_str[(idx)] : "error"\
+)
+
+#define EFI_FW_STATUS_STR_GET(idx) (\
+EFI_ESRT_UPDATE_STATUS_NUM > (idx) ? efi_update_status_str[(idx)] : "error"\
+)
+
+/**
+ * do_efi_capsule_esrt() - manage UEFI capsules
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success,
+ * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "capsule esrt" sub-command.
+ * The prints the current ESRT table.
+ *
+ * efidebug capsule esrt
+ */
+static int do_efi_capsule_esrt(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct efi_system_resource_table *esrt;
+
+ if (argc != 1)
+ return CMD_RET_USAGE;
+
+ esrt = efi_get_configuration_table(&efi_esrt_guid);
+ if (!esrt) {
+ log_info("ESRT: table not present\n");
+ return CMD_RET_SUCCESS;
+ }
+
+ printf("========================================\n");
+ printf("ESRT: fw_resource_count=%d\n", esrt->fw_resource_count);
+ printf("ESRT: fw_resource_count_max=%d\n", esrt->fw_resource_count_max);
+ printf("ESRT: fw_resource_version=%lld\n", esrt->fw_resource_version);
+
+ for (int idx = 0; idx < esrt->fw_resource_count; idx++) {
+ printf("[entry %d]==============================\n", idx);
+ printf("ESRT: fw_class=%pUL\n", &esrt->entries[idx].fw_class);
+ printf("ESRT: fw_type=%s\n", EFI_FW_TYPE_STR_GET(esrt->entries[idx].fw_type));
+ printf("ESRT: fw_version=%d\n", esrt->entries[idx].fw_version);
+ printf("ESRT: lowest_supported_fw_version=%d\n",
+ esrt->entries[idx].lowest_supported_fw_version);
+ printf("ESRT: capsule_flags=%d\n",
+ esrt->entries[idx].capsule_flags);
+ printf("ESRT: last_attempt_version=%d\n",
+ esrt->entries[idx].last_attempt_version);
+ printf("ESRT: last_attempt_status=%s\n",
+ EFI_FW_STATUS_STR_GET(esrt->entries[idx].last_attempt_status));
+ }
+ printf("========================================\n");
+
+ return CMD_RET_SUCCESS;
+}
+#endif /* CONFIG_EFI_ESRT */
+/**
+ * do_efi_capsule_res() - show a capsule update result
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "capsule result" sub-command.
+ * show a capsule update result.
+ * If result number is not specified, CapsuleLast will be shown.
+ *
+ * efidebug capsule result [<capsule result number>]
+ */
+static int do_efi_capsule_res(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ int capsule_id;
+ char *endp;
+ u16 var_name16[12];
+ efi_guid_t guid;
+ struct efi_capsule_result_variable_header *result = NULL;
+ efi_uintn_t size;
+ efi_status_t ret;
+
+ if (argc != 1 && argc != 2)
+ return CMD_RET_USAGE;
+
+ guid = efi_guid_capsule_report;
+ if (argc == 1) {
+ size = sizeof(var_name16);
+ ret = efi_get_variable_int(u"CapsuleLast", &guid, NULL,
+ &size, var_name16, NULL);
+
+ if (ret != EFI_SUCCESS) {
+ if (ret == EFI_NOT_FOUND)
+ printf("CapsuleLast doesn't exist\n");
+ else
+ printf("Failed to get CapsuleLast\n");
+
+ return CMD_RET_FAILURE;
+ }
+ printf("CapsuleLast is %ls\n", var_name16);
+ } else {
+ argc--;
+ argv++;
+
+ capsule_id = hextoul(argv[0], &endp);
+ if (capsule_id < 0 || capsule_id > 0xffff)
+ return CMD_RET_USAGE;
+
+ efi_create_indexed_name(var_name16, sizeof(var_name16),
+ "Capsule", capsule_id);
+ }
+
+ size = 0;
+ ret = efi_get_variable_int(var_name16, &guid, NULL, &size, NULL, NULL);
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ result = malloc(size);
+ if (!result)
+ return CMD_RET_FAILURE;
+ ret = efi_get_variable_int(var_name16, &guid, NULL, &size,
+ result, NULL);
+ }
+ if (ret != EFI_SUCCESS) {
+ free(result);
+ printf("Failed to get %ls\n", var_name16);
+
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Result total size: 0x%x\n", result->variable_total_size);
+ printf("Capsule guid: %pUl\n", &result->capsule_guid);
+ printf("Time processed: %04d-%02d-%02d %02d:%02d:%02d\n",
+ result->capsule_processed.year, result->capsule_processed.month,
+ result->capsule_processed.day, result->capsule_processed.hour,
+ result->capsule_processed.minute,
+ result->capsule_processed.second);
+ printf("Capsule status: 0x%lx\n", result->capsule_status);
+
+ free(result);
+
+ return CMD_RET_SUCCESS;
+}
+
+static struct cmd_tbl cmd_efidebug_capsule_sub[] = {
+ U_BOOT_CMD_MKENT(update, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_update,
+ "", ""),
+ U_BOOT_CMD_MKENT(show, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_show,
+ "", ""),
+#ifdef CONFIG_EFI_ESRT
+ U_BOOT_CMD_MKENT(esrt, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_esrt,
+ "", ""),
+#endif
+#ifdef CONFIG_EFI_CAPSULE_ON_DISK
+ U_BOOT_CMD_MKENT(disk-update, 0, 0, do_efi_capsule_on_disk_update,
+ "", ""),
+#endif
+ U_BOOT_CMD_MKENT(result, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_res,
+ "", ""),
+};
+
+/**
+ * do_efi_capsule() - manage UEFI capsules
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success,
+ * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "capsule" sub-command.
+ */
+static int do_efi_capsule(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct cmd_tbl *cp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ argc--; argv++;
+
+ cp = find_cmd_tbl(argv[0], cmd_efidebug_capsule_sub,
+ ARRAY_SIZE(cmd_efidebug_capsule_sub));
+ if (!cp)
+ return CMD_RET_USAGE;
+
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */
+
+#define EFI_HANDLE_WIDTH ((int)sizeof(efi_handle_t) * 2)
+
+static const char spc[] = " ";
+static const char sep[] = "================";
+
+/**
+ * efi_get_driver_handle_info() - get information of UEFI driver
+ *
+ * @handle: Handle of UEFI device
+ * @driver_name: Driver name
+ * @image_path: Pointer to text of device path
+ * Return: 0 on success, -1 on failure
+ *
+ * Currently return no useful information as all UEFI drivers are
+ * built-in..
+ */
+static int efi_get_driver_handle_info(efi_handle_t handle, u16 **driver_name,
+ u16 **image_path)
+{
+ struct efi_handler *handler;
+ struct efi_loaded_image *image;
+ efi_status_t ret;
+
+ /*
+ * driver name
+ * TODO: support EFI_COMPONENT_NAME2_PROTOCOL
+ */
+ *driver_name = NULL;
+
+ /* image name */
+ ret = efi_search_protocol(handle, &efi_guid_loaded_image, &handler);
+ if (ret != EFI_SUCCESS) {
+ *image_path = NULL;
+ return 0;
+ }
+
+ image = handler->protocol_interface;
+ *image_path = efi_dp_str(image->file_path);
+
+ return 0;
+}
+
+/**
+ * do_efi_show_drivers() - show UEFI drivers
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "drivers" sub-command.
+ * Show all UEFI drivers and their information.
+ */
+static int do_efi_show_drivers(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ efi_handle_t *handles;
+ efi_uintn_t num, i;
+ u16 *driver_name, *image_path_text;
+ efi_status_t ret;
+
+ ret = EFI_CALL(efi_locate_handle_buffer(
+ BY_PROTOCOL, &efi_guid_driver_binding_protocol,
+ NULL, &num, &handles));
+ if (ret != EFI_SUCCESS)
+ return CMD_RET_FAILURE;
+
+ if (!num)
+ return CMD_RET_SUCCESS;
+
+ printf("Driver%.*s Name Image Path\n",
+ EFI_HANDLE_WIDTH - 6, spc);
+ printf("%.*s ==================== ====================\n",
+ EFI_HANDLE_WIDTH, sep);
+ for (i = 0; i < num; i++) {
+ if (!efi_get_driver_handle_info(handles[i], &driver_name,
+ &image_path_text)) {
+ if (image_path_text)
+ printf("%p %-20ls %ls\n", handles[i],
+ driver_name, image_path_text);
+ else
+ printf("%p %-20ls <built-in>\n",
+ handles[i], driver_name);
+ efi_free_pool(driver_name);
+ efi_free_pool(image_path_text);
+ }
+ }
+
+ efi_free_pool(handles);
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * do_efi_show_handles() - show UEFI handles
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "dh" sub-command.
+ * Show all UEFI handles and their information, currently all protocols
+ * added to handle.
+ */
+static int do_efi_show_handles(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ efi_handle_t *handles;
+ efi_guid_t **guid;
+ efi_uintn_t num, count, i, j;
+ efi_status_t ret;
+
+ ret = EFI_CALL(efi_locate_handle_buffer(ALL_HANDLES, NULL, NULL,
+ &num, &handles));
+ if (ret != EFI_SUCCESS)
+ return CMD_RET_FAILURE;
+
+ if (!num)
+ return CMD_RET_SUCCESS;
+
+ for (i = 0; i < num; i++) {
+ struct efi_handler *handler;
+
+ printf("\n%p", handles[i]);
+ if (handles[i]->dev)
+ printf(" (%s)", handles[i]->dev->name);
+ printf("\n");
+ /* Print device path */
+ ret = efi_search_protocol(handles[i], &efi_guid_device_path,
+ &handler);
+ if (ret == EFI_SUCCESS)
+ printf(" %pD\n", handler->protocol_interface);
+ ret = EFI_CALL(BS->protocols_per_handle(handles[i], &guid,
+ &count));
+ /* Print other protocols */
+ for (j = 0; j < count; j++) {
+ if (guidcmp(guid[j], &efi_guid_device_path))
+ printf(" %pUs\n", guid[j]);
+ }
+ efi_free_pool(guid);
+ }
+
+ efi_free_pool(handles);
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * do_efi_show_images() - show UEFI images
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "images" sub-command.
+ * Show all UEFI loaded images and their information.
+ */
+static int do_efi_show_images(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ efi_print_image_infos(NULL);
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * do_efi_show_defaults() - show UEFI default filename and PXE architecture
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "defaults" sub-command.
+ * Shows the default EFI filename and PXE architecture
+ */
+static int do_efi_show_defaults(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ printf("Default boot path: EFI\\BOOT\\%s\n", efi_get_basename());
+ printf("PXE arch: 0x%02x\n", efi_get_pxe_arch());
+
+ return CMD_RET_SUCCESS;
+}
+
+static const char * const efi_mem_type_string[] = {
+ [EFI_RESERVED_MEMORY_TYPE] = "RESERVED",
+ [EFI_LOADER_CODE] = "LOADER CODE",
+ [EFI_LOADER_DATA] = "LOADER DATA",
+ [EFI_BOOT_SERVICES_CODE] = "BOOT CODE",
+ [EFI_BOOT_SERVICES_DATA] = "BOOT DATA",
+ [EFI_RUNTIME_SERVICES_CODE] = "RUNTIME CODE",
+ [EFI_RUNTIME_SERVICES_DATA] = "RUNTIME DATA",
+ [EFI_CONVENTIONAL_MEMORY] = "CONVENTIONAL",
+ [EFI_UNUSABLE_MEMORY] = "UNUSABLE MEM",
+ [EFI_ACPI_RECLAIM_MEMORY] = "ACPI RECLAIM MEM",
+ [EFI_ACPI_MEMORY_NVS] = "ACPI NVS",
+ [EFI_MMAP_IO] = "IO",
+ [EFI_MMAP_IO_PORT] = "IO PORT",
+ [EFI_PAL_CODE] = "PAL",
+ [EFI_PERSISTENT_MEMORY_TYPE] = "PERSISTENT",
+};
+
+static const struct efi_mem_attrs {
+ const u64 bit;
+ const char *text;
+} efi_mem_attrs[] = {
+ {EFI_MEMORY_UC, "UC"},
+ {EFI_MEMORY_WC, "WC"},
+ {EFI_MEMORY_WT, "WT"},
+ {EFI_MEMORY_WB, "WB"},
+ {EFI_MEMORY_UCE, "UCE"},
+ {EFI_MEMORY_WP, "WP"},
+ {EFI_MEMORY_RP, "RP"},
+ {EFI_MEMORY_XP, "XP"},
+ {EFI_MEMORY_NV, "NV"},
+ {EFI_MEMORY_MORE_RELIABLE, "REL"},
+ {EFI_MEMORY_RO, "RO"},
+ {EFI_MEMORY_SP, "SP"},
+ {EFI_MEMORY_CPU_CRYPTO, "CRYPT"},
+ {EFI_MEMORY_HOT_PLUGGABLE, "HOTPL"},
+ {EFI_MEMORY_RUNTIME, "RT"},
+};
+
+/**
+ * print_memory_attributes() - print memory map attributes
+ *
+ * @attributes: Attribute value
+ *
+ * Print memory map attributes
+ */
+static void print_memory_attributes(u64 attributes)
+{
+ int sep, i;
+
+ for (sep = 0, i = 0; i < ARRAY_SIZE(efi_mem_attrs); i++)
+ if (attributes & efi_mem_attrs[i].bit) {
+ if (sep) {
+ putc('|');
+ } else {
+ putc(' ');
+ sep = 1;
+ }
+ puts(efi_mem_attrs[i].text);
+ }
+}
+
+#define EFI_PHYS_ADDR_WIDTH (int)(sizeof(efi_physical_addr_t) * 2)
+
+/**
+ * do_efi_show_memmap() - show UEFI memory map
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "memmap" sub-command.
+ * Show UEFI memory map.
+ */
+static int do_efi_show_memmap(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct efi_mem_desc *memmap, *map;
+ efi_uintn_t map_size;
+ const char *type;
+ int i;
+ efi_status_t ret;
+
+ ret = efi_get_memory_map_alloc(&map_size, &memmap);
+ if (ret != EFI_SUCCESS)
+ return CMD_RET_FAILURE;
+
+ printf("Type Start%.*s End%.*s Attributes\n",
+ EFI_PHYS_ADDR_WIDTH - 5, spc, EFI_PHYS_ADDR_WIDTH - 3, spc);
+ printf("================ %.*s %.*s ==========\n",
+ EFI_PHYS_ADDR_WIDTH, sep, EFI_PHYS_ADDR_WIDTH, sep);
+ /*
+ * Coverity check: dereferencing null pointer "map."
+ * This is a false positive as memmap will always be
+ * populated by allocate_pool() above.
+ */
+ for (i = 0, map = memmap; i < map_size / sizeof(*map); map++, i++) {
+ if (map->type < ARRAY_SIZE(efi_mem_type_string))
+ type = efi_mem_type_string[map->type];
+ else
+ type = "(unknown)";
+
+ printf("%-16s %.*llx-%.*llx", type,
+ EFI_PHYS_ADDR_WIDTH,
+ (u64)map_to_sysmem((void *)(uintptr_t)
+ map->physical_start),
+ EFI_PHYS_ADDR_WIDTH,
+ (u64)map_to_sysmem((void *)(uintptr_t)
+ (map->physical_start +
+ map->num_pages * EFI_PAGE_SIZE)));
+
+ print_memory_attributes(map->attribute);
+ putc('\n');
+ }
+
+ efi_free_pool(memmap);
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * do_efi_show_tables() - show UEFI configuration tables
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "tables" sub-command.
+ * Show UEFI configuration tables.
+ */
+static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ efi_show_tables(&systab);
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * enum efi_lo_dp_part - part of device path in load option
+ */
+enum efi_lo_dp_part {
+ /** @EFI_LO_DP_PART_BINARY: binary */
+ EFI_LO_DP_PART_BINARY,
+ /** @EFI_LO_DP_PART_INITRD: initial RAM disk */
+ EFI_LO_DP_PART_INITRD,
+ /** @EFI_LP_DP_PART_FDT: device-tree */
+ EFI_LP_DP_PART_FDT,
+};
+
+/**
+ * create_lo_dp_part() - create a special device path for our Boot### option
+ *
+ * @dev: device
+ * @part: disk partition
+ * @file: filename
+ * @shortform: create short form device path
+ * @type: part of device path to be created
+ * Return: pointer to the device path or ERR_PTR
+ */
+static
+struct efi_device_path *create_lo_dp_part(const char *dev, const char *part,
+ const char *file, bool shortform,
+ enum efi_lo_dp_part type)
+
+{
+ struct efi_device_path *tmp_dp = NULL, *tmp_fp = NULL, *short_fp = NULL;
+ struct efi_device_path *dp = NULL;
+ const struct efi_device_path *dp_prefix;
+ efi_status_t ret;
+ const struct efi_lo_dp_prefix fdt_dp = {
+ .vendor = {
+ {
+ DEVICE_PATH_TYPE_MEDIA_DEVICE,
+ DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
+ sizeof(fdt_dp.vendor),
+ },
+ EFI_FDT_GUID,
+ },
+ .end = {
+ DEVICE_PATH_TYPE_END,
+ DEVICE_PATH_SUB_TYPE_END,
+ sizeof(fdt_dp.end),
+ }
+ };
+ const struct efi_lo_dp_prefix initrd_dp = {
+ .vendor = {
+ {
+ DEVICE_PATH_TYPE_MEDIA_DEVICE,
+ DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
+ sizeof(initrd_dp.vendor),
+ },
+ EFI_INITRD_MEDIA_GUID,
+ },
+ .end = {
+ DEVICE_PATH_TYPE_END,
+ DEVICE_PATH_SUB_TYPE_END,
+ sizeof(initrd_dp.end),
+ }
+ };
+
+ switch (type) {
+ case EFI_LO_DP_PART_INITRD:
+ dp_prefix = &initrd_dp.vendor.dp;
+ break;
+ case EFI_LP_DP_PART_FDT:
+ dp_prefix = &fdt_dp.vendor.dp;
+ break;
+ default:
+ dp_prefix = NULL;
+ break;
+ }
+
+ ret = efi_dp_from_name(dev, part, file, &tmp_dp, &tmp_fp);
+ if (ret != EFI_SUCCESS) {
+ printf("Cannot create device path for \"%s %s\"\n", part, file);
+ goto out;
+ }
+ if (shortform)
+ short_fp = efi_dp_shorten(tmp_fp);
+ if (!short_fp)
+ short_fp = tmp_fp;
+
+ dp = efi_dp_concat(dp_prefix, short_fp, 0);
+
+out:
+ efi_free_pool(tmp_dp);
+ efi_free_pool(tmp_fp);
+ return dp;
+}
+
+/**
+ * efi_boot_add_uri() - set URI load option
+ *
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * @var_name16: variable name buffer
+ * @var_name16_size: variable name buffer size
+ * @lo: pointer to the load option
+ * @file_path: buffer to set the generated device path pointer
+ * @fp_size: file_path size
+ * Return: CMD_RET_SUCCESS on success,
+ * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ */
+static int efi_boot_add_uri(int argc, char *const argv[], u16 *var_name16,
+ size_t var_name16_size, struct efi_load_option *lo,
+ struct efi_device_path **file_path,
+ efi_uintn_t *fp_size)
+{
+ int id;
+ char *pos;
+ char *endp;
+ u16 *label;
+ efi_uintn_t uridp_len;
+ struct efi_device_path_uri *uridp;
+
+ if (argc < 3 || lo->label)
+ return CMD_RET_USAGE;
+
+ id = (int)hextoul(argv[1], &endp);
+ if (*endp != '\0' || id > 0xffff)
+ return CMD_RET_USAGE;
+
+ label = efi_convert_string(argv[2]);
+ if (!label)
+ return CMD_RET_FAILURE;
+
+ if (!wget_validate_uri(argv[3])) {
+ printf("ERROR: invalid URI\n");
+ return CMD_RET_FAILURE;
+ }
+
+ efi_create_indexed_name(var_name16, var_name16_size, "Boot", id);
+ lo->label = label;
+
+ uridp_len = sizeof(struct efi_device_path) + strlen(argv[3]) + 1;
+ uridp = efi_alloc(uridp_len + sizeof(EFI_DP_END));
+ if (!uridp) {
+ log_err("Out of memory\n");
+ return CMD_RET_FAILURE;
+ }
+ uridp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ uridp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_URI;
+ uridp->dp.length = uridp_len;
+ strcpy(uridp->uri, argv[3]);
+ pos = (char *)uridp + uridp_len;
+ memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END));
+
+ *file_path = &uridp->dp;
+ *fp_size += uridp_len + sizeof(EFI_DP_END);
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * do_efi_boot_add() - set UEFI load option
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success,
+ * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "boot add" sub-command. Create or change UEFI load option.
+ *
+ * efidebug boot add -b <id> <label> <interface> <devnum>[:<part>] <file>
+ * -i <file> <interface2> <devnum2>[:<part>] <initrd>
+ * -s '<options>'
+ */
+static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ int id;
+ char *endp;
+ u16 var_name16[9];
+ efi_guid_t guid;
+ u16 *label;
+ struct efi_device_path *file_path = NULL;
+ struct efi_device_path *initrd_dp = NULL;
+ struct efi_device_path *fdt_dp = NULL;
+ struct efi_load_option lo;
+ void *data = NULL;
+ efi_uintn_t size;
+ efi_uintn_t fp_size = 0;
+ efi_status_t ret;
+ int r = CMD_RET_SUCCESS;
+
+ guid = efi_global_variable_guid;
+
+ /* attributes */
+ lo.attributes = LOAD_OPTION_ACTIVE; /* always ACTIVE */
+ lo.optional_data = NULL;
+ lo.label = NULL;
+
+ argc--;
+ argv++; /* 'add' */
+ for (; argc > 0; argc--, argv++) {
+ int shortform;
+
+ if (*argv[0] != '-' || strlen(argv[0]) != 2) {
+ r = CMD_RET_USAGE;
+ goto out;
+ }
+ shortform = 0;
+ switch (argv[0][1]) {
+ case 'b':
+ shortform = 1;
+ /* fallthrough */
+ case 'B':
+ if (argc < 5 || lo.label) {
+ r = CMD_RET_USAGE;
+ goto out;
+ }
+ id = (int)hextoul(argv[1], &endp);
+ if (*endp != '\0' || id > 0xffff)
+ return CMD_RET_USAGE;
+
+ efi_create_indexed_name(var_name16, sizeof(var_name16),
+ "Boot", id);
+
+ /* label */
+ label = efi_convert_string(argv[2]);
+ if (!label)
+ return CMD_RET_FAILURE;
+ lo.label = label; /* label will be changed below */
+
+ /* file path */
+ file_path = create_lo_dp_part(argv[3], argv[4], argv[5],
+ shortform,
+ EFI_LO_DP_PART_BINARY);
+ argc -= 5;
+ argv += 5;
+ break;
+ case 'd':
+ shortform = 1;
+ fallthrough;
+ case 'D':
+ if (argc < 3 || fdt_dp) {
+ r = CMD_RET_USAGE;
+ goto out;
+ }
+
+ fdt_dp = create_lo_dp_part(argv[1], argv[2], argv[3],
+ shortform,
+ EFI_LP_DP_PART_FDT);
+ if (!fdt_dp) {
+ printf("Cannot add a device-tree\n");
+ r = CMD_RET_FAILURE;
+ goto out;
+ }
+ argc -= 3;
+ argv += 3;
+ break;
+ case 'i':
+ shortform = 1;
+ /* fallthrough */
+ case 'I':
+ if (argc < 3 || initrd_dp) {
+ r = CMD_RET_USAGE;
+ goto out;
+ }
+
+ initrd_dp = create_lo_dp_part(argv[1], argv[2], argv[3],
+ shortform,
+ EFI_LO_DP_PART_INITRD);
+ if (!initrd_dp) {
+ printf("Cannot add an initrd\n");
+ r = CMD_RET_FAILURE;
+ goto out;
+ }
+ argc -= 3;
+ argv += 3;
+ break;
+ case 's':
+ if (argc < 1 || lo.optional_data) {
+ r = CMD_RET_USAGE;
+ goto out;
+ }
+ lo.optional_data = (const u8 *)argv[1];
+ argc -= 1;
+ argv += 1;
+ break;
+ case 'u':
+ if (IS_ENABLED(CONFIG_EFI_HTTP_BOOT)) {
+ r = efi_boot_add_uri(argc, argv, var_name16,
+ sizeof(var_name16), &lo,
+ &file_path, &fp_size);
+ if (r != CMD_RET_SUCCESS)
+ goto out;
+ argc -= 3;
+ argv += 3;
+ } else{
+ r = CMD_RET_USAGE;
+ goto out;
+ }
+ break;
+ default:
+ r = CMD_RET_USAGE;
+ goto out;
+ }
+ }
+
+ if (!file_path) {
+ printf("Missing binary\n");
+ r = CMD_RET_USAGE;
+ goto out;
+ }
+
+ ret = efi_load_option_dp_join(&file_path, &fp_size, initrd_dp, fdt_dp);
+ if (ret != EFI_SUCCESS) {
+ printf("Cannot create final device path\n");
+ r = CMD_RET_FAILURE;
+ goto out;
+ }
+
+ lo.file_path = file_path;
+ lo.file_path_length = fp_size;
+
+ size = efi_serialize_load_option(&lo, (u8 **)&data);
+ if (!size) {
+ r = CMD_RET_FAILURE;
+ goto out;
+ }
+
+ ret = efi_set_variable_int(var_name16, &guid,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ size, data, false);
+ if (ret != EFI_SUCCESS) {
+ printf("Cannot set %ls\n", var_name16);
+ r = CMD_RET_FAILURE;
+ }
+
+out:
+ free(data);
+ efi_free_pool(initrd_dp);
+ efi_free_pool(fdt_dp);
+ efi_free_pool(file_path);
+ free(lo.label);
+
+ return r;
+}
+
+/**
+ * do_efi_boot_rm() - delete UEFI load options
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "boot rm" sub-command.
+ * Delete UEFI load options.
+ *
+ * efidebug boot rm <id> ...
+ */
+static int do_efi_boot_rm(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ efi_guid_t guid;
+ int id, i;
+ char *endp;
+ u16 var_name16[9];
+ efi_status_t ret;
+
+ if (argc == 1)
+ return CMD_RET_USAGE;
+
+ guid = efi_global_variable_guid;
+ for (i = 1; i < argc; i++, argv++) {
+ id = (int)hextoul(argv[1], &endp);
+ if (*endp != '\0' || id > 0xffff)
+ return CMD_RET_FAILURE;
+
+ efi_create_indexed_name(var_name16, sizeof(var_name16),
+ "Boot", id);
+ ret = efi_set_variable_int(var_name16, &guid, 0, 0, NULL,
+ false);
+ if (ret) {
+ printf("Cannot remove %ls\n", var_name16);
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * show_efi_boot_opt_data() - dump UEFI load option
+ *
+ * @varname16: variable name
+ * @data: value of UEFI load option variable
+ * @size: size of the boot option
+ *
+ * Decode the value of UEFI load option variable and print information.
+ */
+static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
+{
+ struct efi_device_path *fdt_path;
+ struct efi_device_path *initrd_path;
+ struct efi_load_option lo;
+ efi_status_t ret;
+
+ ret = efi_deserialize_load_option(&lo, data, size);
+ if (ret != EFI_SUCCESS) {
+ printf("%ls: invalid load option\n", varname16);
+ return;
+ }
+
+ printf("%ls:\nattributes: %c%c%c (0x%08x)\n",
+ varname16,
+ /* ACTIVE */
+ lo.attributes & LOAD_OPTION_ACTIVE ? 'A' : '-',
+ /* FORCE RECONNECT */
+ lo.attributes & LOAD_OPTION_FORCE_RECONNECT ? 'R' : '-',
+ /* HIDDEN */
+ lo.attributes & LOAD_OPTION_HIDDEN ? 'H' : '-',
+ lo.attributes);
+ printf(" label: %ls\n", lo.label);
+
+ printf(" file_path: %pD\n", lo.file_path);
+
+ initrd_path = efi_dp_from_lo(&lo, &efi_lf2_initrd_guid);
+ if (initrd_path) {
+ printf(" initrd_path: %pD\n", initrd_path);
+ efi_free_pool(initrd_path);
+ }
+
+ fdt_path = efi_dp_from_lo(&lo, &efi_guid_fdt);
+ if (fdt_path) {
+ printf(" device-tree path: %pD\n", fdt_path);
+ efi_free_pool(fdt_path);
+ }
+
+ printf(" data:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
+ lo.optional_data, *size, true);
+}
+
+/**
+ * show_efi_boot_opt() - dump UEFI load option
+ *
+ * @varname16: variable name
+ *
+ * Dump information defined by UEFI load option.
+ */
+static void show_efi_boot_opt(u16 *varname16)
+{
+ void *data;
+ efi_uintn_t size;
+ efi_status_t ret;
+
+ size = 0;
+ ret = efi_get_variable_int(varname16, &efi_global_variable_guid,
+ NULL, &size, NULL, NULL);
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ data = malloc(size);
+ if (!data) {
+ printf("ERROR: Out of memory\n");
+ return;
+ }
+ ret = efi_get_variable_int(varname16, &efi_global_variable_guid,
+ NULL, &size, data, NULL);
+ if (ret == EFI_SUCCESS)
+ show_efi_boot_opt_data(varname16, data, &size);
+ free(data);
+ }
+}
+
+/**
+ * do_efi_boot_dump() - dump all UEFI load options
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "boot dump" sub-command.
+ * Dump information of all UEFI load options defined.
+ *
+ * efidebug boot dump
+ */
+static int do_efi_boot_dump(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ u16 *var_name16, *p;
+ efi_uintn_t buf_size, size;
+ efi_guid_t guid;
+ efi_status_t ret;
+
+ if (argc > 1)
+ return CMD_RET_USAGE;
+
+ buf_size = 128;
+ var_name16 = malloc(buf_size);
+ if (!var_name16)
+ return CMD_RET_FAILURE;
+
+ var_name16[0] = 0;
+ for (;;) {
+ size = buf_size;
+ ret = efi_get_next_variable_name_int(&size, var_name16, &guid);
+ if (ret == EFI_NOT_FOUND)
+ break;
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ buf_size = size;
+ p = realloc(var_name16, buf_size);
+ if (!p) {
+ free(var_name16);
+ return CMD_RET_FAILURE;
+ }
+ var_name16 = p;
+ ret = efi_get_next_variable_name_int(&size, var_name16,
+ &guid);
+ }
+ if (ret != EFI_SUCCESS) {
+ free(var_name16);
+ return CMD_RET_FAILURE;
+ }
+
+ if (efi_varname_is_load_option(var_name16, NULL))
+ show_efi_boot_opt(var_name16);
+ }
+
+ free(var_name16);
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * show_efi_boot_order() - show order of UEFI load options
+ *
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Show order of UEFI load options defined by BootOrder variable.
+ */
+static int show_efi_boot_order(void)
+{
+ u16 *bootorder;
+ efi_uintn_t size;
+ int num, i;
+ u16 var_name16[9];
+ void *data;
+ struct efi_load_option lo;
+ efi_status_t ret;
+
+ size = 0;
+ ret = efi_get_variable_int(u"BootOrder", &efi_global_variable_guid,
+ NULL, &size, NULL, NULL);
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ if (ret == EFI_NOT_FOUND) {
+ printf("BootOrder not defined\n");
+ return CMD_RET_SUCCESS;
+ } else {
+ return CMD_RET_FAILURE;
+ }
+ }
+ bootorder = malloc(size);
+ if (!bootorder) {
+ printf("ERROR: Out of memory\n");
+ return CMD_RET_FAILURE;
+ }
+ ret = efi_get_variable_int(u"BootOrder", &efi_global_variable_guid,
+ NULL, &size, bootorder, NULL);
+ if (ret != EFI_SUCCESS) {
+ ret = CMD_RET_FAILURE;
+ goto out;
+ }
+
+ num = size / sizeof(u16);
+ for (i = 0; i < num; i++) {
+ efi_create_indexed_name(var_name16, sizeof(var_name16),
+ "Boot", bootorder[i]);
+
+ size = 0;
+ ret = efi_get_variable_int(var_name16,
+ &efi_global_variable_guid, NULL,
+ &size, NULL, NULL);
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ printf("%2d: %ls: (not defined)\n", i + 1, var_name16);
+ continue;
+ }
+
+ data = malloc(size);
+ if (!data) {
+ ret = CMD_RET_FAILURE;
+ goto out;
+ }
+ ret = efi_get_variable_int(var_name16,
+ &efi_global_variable_guid, NULL,
+ &size, data, NULL);
+ if (ret != EFI_SUCCESS) {
+ free(data);
+ ret = CMD_RET_FAILURE;
+ goto out;
+ }
+
+ ret = efi_deserialize_load_option(&lo, data, &size);
+ if (ret != EFI_SUCCESS) {
+ printf("%ls: invalid load option\n", var_name16);
+ ret = CMD_RET_FAILURE;
+ goto out;
+ }
+
+ printf("%2d: %ls: %ls\n", i + 1, var_name16, lo.label);
+
+ free(data);
+ }
+out:
+ free(bootorder);
+
+ return ret;
+}
+
+/**
+ * do_efi_boot_next() - manage UEFI BootNext variable
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success,
+ * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "boot next" sub-command.
+ * Set BootNext variable.
+ *
+ * efidebug boot next <id>
+ */
+static int do_efi_boot_next(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ u16 bootnext;
+ efi_uintn_t size;
+ char *endp;
+ efi_guid_t guid;
+ efi_status_t ret;
+ int r = CMD_RET_SUCCESS;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ bootnext = (u16)hextoul(argv[1], &endp);
+ if (*endp) {
+ printf("invalid value: %s\n", argv[1]);
+ r = CMD_RET_FAILURE;
+ goto out;
+ }
+
+ guid = efi_global_variable_guid;
+ size = sizeof(u16);
+ ret = efi_set_variable_int(u"BootNext", &guid,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ size, &bootnext, false);
+ if (ret != EFI_SUCCESS) {
+ printf("Cannot set BootNext\n");
+ r = CMD_RET_FAILURE;
+ }
+out:
+ return r;
+}
+
+/**
+ * do_efi_boot_order() - manage UEFI BootOrder variable
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "boot order" sub-command.
+ * Show order of UEFI load options, or change it in BootOrder variable.
+ *
+ * efidebug boot order [<id> ...]
+ */
+static int do_efi_boot_order(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ u16 *bootorder = NULL;
+ efi_uintn_t size;
+ int id, i;
+ char *endp;
+ efi_guid_t guid;
+ efi_status_t ret;
+ int r = CMD_RET_SUCCESS;
+
+ if (argc == 1)
+ return show_efi_boot_order();
+
+ argc--;
+ argv++;
+
+ size = argc * sizeof(u16);
+ bootorder = malloc(size);
+ if (!bootorder)
+ return CMD_RET_FAILURE;
+
+ for (i = 0; i < argc; i++) {
+ id = (int)hextoul(argv[i], &endp);
+ if (*endp != '\0' || id > 0xffff) {
+ printf("invalid value: %s\n", argv[i]);
+ r = CMD_RET_FAILURE;
+ goto out;
+ }
+
+ bootorder[i] = (u16)id;
+ }
+
+ guid = efi_global_variable_guid;
+ ret = efi_set_variable_int(u"BootOrder", &guid,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ size, bootorder, true);
+ if (ret != EFI_SUCCESS) {
+ printf("Cannot set BootOrder\n");
+ r = CMD_RET_FAILURE;
+ }
+out:
+ free(bootorder);
+
+ return r;
+}
+
+static struct cmd_tbl cmd_efidebug_boot_sub[] = {
+ U_BOOT_CMD_MKENT(add, CONFIG_SYS_MAXARGS, 1, do_efi_boot_add, "", ""),
+ U_BOOT_CMD_MKENT(rm, CONFIG_SYS_MAXARGS, 1, do_efi_boot_rm, "", ""),
+ U_BOOT_CMD_MKENT(dump, CONFIG_SYS_MAXARGS, 1, do_efi_boot_dump, "", ""),
+ U_BOOT_CMD_MKENT(next, CONFIG_SYS_MAXARGS, 1, do_efi_boot_next, "", ""),
+ U_BOOT_CMD_MKENT(order, CONFIG_SYS_MAXARGS, 1, do_efi_boot_order,
+ "", ""),
+};
+
+/**
+ * do_efi_boot_opt() - manage UEFI load options
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success,
+ * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "boot" sub-command.
+ */
+static int do_efi_boot_opt(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ argc--; argv++;
+
+ cp = find_cmd_tbl(argv[0], cmd_efidebug_boot_sub,
+ ARRAY_SIZE(cmd_efidebug_boot_sub));
+ if (!cp)
+ return CMD_RET_USAGE;
+
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+
+/**
+ * do_efi_test_bootmgr() - run simple bootmgr for test
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success,
+ * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "test bootmgr" sub-command.
+ * Run simple bootmgr for test.
+ *
+ * efidebug test bootmgr
+ */
+static __maybe_unused int do_efi_test_bootmgr(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ efi_handle_t image;
+ efi_uintn_t exit_data_size = 0;
+ u16 *exit_data = NULL;
+ efi_status_t ret;
+ void *load_options = NULL;
+
+ ret = efi_bootmgr_load(&image, &load_options);
+ printf("efi_bootmgr_load() returned: %ld\n", ret & ~EFI_ERROR_MASK);
+ if (ret != EFI_SUCCESS)
+ return CMD_RET_SUCCESS;
+
+ /* We call efi_start_image() even if error for test purpose. */
+ ret = EFI_CALL(efi_start_image(image, &exit_data_size, &exit_data));
+ printf("efi_start_image() returned: %ld\n", ret & ~EFI_ERROR_MASK);
+ if (ret && exit_data)
+ efi_free_pool(exit_data);
+
+ free(load_options);
+ return CMD_RET_SUCCESS;
+}
+
+static struct cmd_tbl cmd_efidebug_test_sub[] = {
+#ifdef CONFIG_EFI_BOOTMGR
+ U_BOOT_CMD_MKENT(bootmgr, CONFIG_SYS_MAXARGS, 1, do_efi_test_bootmgr,
+ "", ""),
+#endif
+};
+
+/**
+ * do_efi_test() - manage UEFI load options
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success,
+ * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "test" sub-command.
+ */
+static int do_efi_test(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct cmd_tbl *cp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ argc--; argv++;
+
+ cp = find_cmd_tbl(argv[0], cmd_efidebug_test_sub,
+ ARRAY_SIZE(cmd_efidebug_test_sub));
+ if (!cp)
+ return CMD_RET_USAGE;
+
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+
+/**
+ * do_efi_query_info() - QueryVariableInfo EFI service
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success,
+ * CMD_RET_USAGE or CMD_RET_FAILURE on failure
+ *
+ * Implement efidebug "test" sub-command.
+ */
+
+static int do_efi_query_info(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ efi_status_t ret;
+ u32 attr = 0;
+ u64 max_variable_storage_size;
+ u64 remain_variable_storage_size;
+ u64 max_variable_size;
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-bs"))
+ attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
+ else if (!strcmp(argv[i], "-rt"))
+ attr |= EFI_VARIABLE_RUNTIME_ACCESS;
+ else if (!strcmp(argv[i], "-nv"))
+ attr |= EFI_VARIABLE_NON_VOLATILE;
+ else if (!strcmp(argv[i], "-at"))
+ attr |=
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
+ }
+
+ ret = efi_query_variable_info_int(attr, &max_variable_storage_size,
+ &remain_variable_storage_size,
+ &max_variable_size);
+ if (ret != EFI_SUCCESS) {
+ printf("Error: Cannot query UEFI variables, r = %lu\n",
+ ret & ~EFI_ERROR_MASK);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Max storage size %llu\n", max_variable_storage_size);
+ printf("Remaining storage size %llu\n", remain_variable_storage_size);
+ printf("Max variable size %llu\n", max_variable_size);
+
+ return CMD_RET_SUCCESS;
+}
+
+static struct cmd_tbl cmd_efidebug_sub[] = {
+ U_BOOT_CMD_MKENT(boot, CONFIG_SYS_MAXARGS, 1, do_efi_boot_opt, "", ""),
+#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
+ U_BOOT_CMD_MKENT(capsule, CONFIG_SYS_MAXARGS, 1, do_efi_capsule,
+ "", ""),
+#endif
+ U_BOOT_CMD_MKENT(drivers, CONFIG_SYS_MAXARGS, 1, do_efi_show_drivers,
+ "", ""),
+ U_BOOT_CMD_MKENT(dh, CONFIG_SYS_MAXARGS, 1, do_efi_show_handles,
+ "", ""),
+ U_BOOT_CMD_MKENT(defaults, CONFIG_SYS_MAXARGS, 1, do_efi_show_defaults,
+ "", ""),
+ U_BOOT_CMD_MKENT(images, CONFIG_SYS_MAXARGS, 1, do_efi_show_images,
+ "", ""),
+ U_BOOT_CMD_MKENT(memmap, CONFIG_SYS_MAXARGS, 1, do_efi_show_memmap,
+ "", ""),
+ U_BOOT_CMD_MKENT(tables, CONFIG_SYS_MAXARGS, 1, do_efi_show_tables,
+ "", ""),
+ U_BOOT_CMD_MKENT(test, CONFIG_SYS_MAXARGS, 1, do_efi_test,
+ "", ""),
+ U_BOOT_CMD_MKENT(query, CONFIG_SYS_MAXARGS, 1, do_efi_query_info,
+ "", ""),
+};
+
+/**
+ * do_efidebug() - display and configure UEFI environment
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success,
+ * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug command which allows us to display and
+ * configure UEFI environment.
+ */
+static int do_efidebug(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct cmd_tbl *cp;
+ efi_status_t r;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ argc--; argv++;
+
+ /* Initialize UEFI drivers */
+ r = efi_init_obj_list();
+ if (r != EFI_SUCCESS) {
+ printf("Error: Cannot initialize UEFI sub-system, r = %lu\n",
+ r & ~EFI_ERROR_MASK);
+ return CMD_RET_FAILURE;
+ }
+
+ cp = find_cmd_tbl(argv[0], cmd_efidebug_sub,
+ ARRAY_SIZE(cmd_efidebug_sub));
+ if (!cp)
+ return CMD_RET_USAGE;
+
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_LONGHELP(efidebug,
+ " - UEFI Shell-like interface to configure UEFI environment\n"
+ "\n"
+ "efidebug boot add - set UEFI BootXXXX variable\n"
+ " -b|-B <bootid> <label> <interface> <devnum>[:<part>] <file path>\n"
+ " -d|-D <interface> <devnum>[:<part>] <device-tree file path>\n"
+ " -i|-I <interface> <devnum>[:<part>] <initrd file path>\n"
+ " (-b, -d, -i for short form device path)\n"
+#if (IS_ENABLED(CONFIG_EFI_HTTP_BOOT))
+ " -u <bootid> <label> <uri>\n"
+#endif
+ " -s '<optional data>'\n"
+ "efidebug boot rm <bootid#1> [<bootid#2> [<bootid#3> [...]]]\n"
+ " - delete UEFI BootXXXX variables\n"
+ "efidebug boot dump\n"
+ " - dump all UEFI BootXXXX variables\n"
+ "efidebug boot next <bootid>\n"
+ " - set UEFI BootNext variable\n"
+ "efidebug boot order [<bootid#1> [<bootid#2> [<bootid#3> [...]]]]\n"
+ " - set/show UEFI boot order\n"
+ "\n"
+#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
+ "efidebug capsule update [-v] <capsule address>\n"
+ " - process a capsule\n"
+ "efidebug capsule disk-update\n"
+ " - update a capsule from disk\n"
+ "efidebug capsule show <capsule address>\n"
+ " - show capsule information\n"
+ "efidebug capsule result [<capsule result var>]\n"
+ " - show a capsule update result\n"
+#ifdef CONFIG_EFI_ESRT
+ "efidebug capsule esrt\n"
+ " - print the ESRT\n"
+#endif
+ "\n"
+#endif
+ "efidebug drivers\n"
+ " - show UEFI drivers\n"
+ "efidebug dh\n"
+ " - show UEFI handles\n"
+ "efidebug defaults\n"
+ " - show default EFI filename and PXE architecture\n"
+ "efidebug images\n"
+ " - show loaded images\n"
+ "efidebug memmap\n"
+ " - show UEFI memory map\n"
+ "efidebug tables\n"
+ " - show UEFI configuration tables\n"
+#ifdef CONFIG_EFI_BOOTMGR
+ "efidebug test bootmgr\n"
+ " - run simple bootmgr for test\n"
+#endif
+ "efidebug query [-nv][-bs][-rt][-at]\n"
+ " - show size of UEFI variables store\n");
+
+U_BOOT_CMD(
+ efidebug, CONFIG_SYS_MAXARGS, 0, do_efidebug,
+ "Configure UEFI environment",
+ efidebug_help_text
+);
diff --git a/cmd/elf.c b/cmd/elf.c
new file mode 100644
index 00000000000..53ec193aaa6
--- /dev/null
+++ b/cmd/elf.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (c) 2001 William L. Pitts
+ * All rights reserved.
+ */
+
+#include <command.h>
+#include <cpu_func.h>
+#include <elf.h>
+#include <env.h>
+#include <image.h>
+#include <log.h>
+#ifdef CONFIG_CMD_ELF_BOOTVX
+#include <net.h>
+#include <vxworks.h>
+#endif
+#ifdef CONFIG_X86
+#include <vesa.h>
+#include <asm/cache.h>
+#include <asm/e820.h>
+#include <linux/linkage.h>
+#endif
+
+#define BOOTLINE_BUF_LEN 128
+
+/* Interpreter command to boot an arbitrary ELF image from memory */
+int do_bootelf(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+#if CONFIG_IS_ENABLED(CMD_ELF_FDT_SETUP)
+ struct bootm_headers img = {0};
+ unsigned long fdt_addr = 0; /* Address of the FDT */
+#endif
+ unsigned long addr; /* Address of the ELF image */
+ unsigned long rc; /* Return value from user code */
+ int rcode = CMD_RET_SUCCESS;
+ Bootelf_flags flags = {0};
+
+ /* Consume 'bootelf' */
+ argc--; argv++;
+
+ /* Check for [-p|-s] flag. */
+ if (argc >= 1 && (argv[0][0] == '-' && \
+ (argv[0][1] == 'p' || argv[0][1] == 's'))) {
+ if (argv[0][1] == 'p')
+ flags.phdr = 1;
+ log_debug("Using ELF header format %s\n",
+ flags.phdr ? "phdr" : "shdr");
+ /* Consume flag. */
+ argc--; argv++;
+ }
+
+#if CONFIG_IS_ENABLED(CMD_ELF_FDT_SETUP)
+ /* Check for [-d fdt_addr_r] option. */
+ if ((argc >= 2) && (argv[0][0] == '-') && (argv[0][1] == 'd')) {
+ if (strict_strtoul(argv[1], 16, &fdt_addr) != 0)
+ return CMD_RET_USAGE;
+ /* Consume option. */
+ argc -= 2;
+ argv += 2;
+ }
+#endif
+
+ /* Check for address. */
+ if (argc >= 1 && strict_strtoul(argv[0], 16, &addr) != -EINVAL) {
+ /* Consume address */
+ argc--; argv++;
+ } else
+ addr = image_load_addr;
+
+#if CONFIG_IS_ENABLED(CMD_ELF_FDT_SETUP)
+ if (fdt_addr) {
+ log_debug("Setting up FDT at 0x%08lx ...\n", fdt_addr);
+ flush();
+
+ fdt_set_totalsize((void *)fdt_addr,
+ fdt_totalsize(fdt_addr) + CONFIG_SYS_FDT_PAD);
+ if (image_setup_libfdt(&img, (void *)fdt_addr, false))
+ return 1;
+ }
+#endif
+
+ if (env_get_autostart()) {
+ flags.autostart = 1;
+ log_debug("Starting application at 0x%08lx ...\n", addr);
+ flush();
+ }
+
+ /*
+ * pass address parameter as argv[0] (aka command name),
+ * and all remaining arguments
+ */
+ rc = bootelf(addr, flags, argc, argv);
+ if (rc != 0)
+ rcode = CMD_RET_FAILURE;
+
+ if (flags.autostart)
+ {
+ if (ENOEXEC == errno)
+ log_err("Invalid ELF image\n");
+ else
+ log_debug("## Application terminated, rc = 0x%lx\n", rc);
+ }
+
+ return rcode;
+}
+
+#ifdef CONFIG_CMD_ELF_BOOTVX
+/*
+ * Interpreter command to boot VxWorks from a memory image. The image can
+ * be either an ELF image or a raw binary. Will attempt to setup the
+ * bootline and other parameters correctly.
+ */
+int do_bootvx(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ unsigned long addr; /* Address of image */
+ unsigned long bootaddr = 0; /* Address to put the bootline */
+ char *bootline; /* Text of the bootline */
+ char *tmp; /* Temporary char pointer */
+ char build_buf[BOOTLINE_BUF_LEN]; /* Buffer for building the bootline */
+ int ptr = 0;
+#ifdef CONFIG_X86
+ ulong base;
+ struct e820_info *info;
+ struct e820_entry *data;
+ struct efi_gop_info *gop;
+ struct vesa_mode_info *vesa = &mode_info.vesa;
+#endif
+
+ /*
+ * Check the loadaddr variable.
+ * If we don't know where the image is then we're done.
+ */
+ if (argc < 2)
+ addr = image_load_addr;
+ else
+ addr = hextoul(argv[1], NULL);
+
+#if defined(CONFIG_CMD_NET) && !defined(CONFIG_NET_LWIP)
+ /*
+ * Check to see if we need to tftp the image ourselves
+ * before starting
+ */
+ if ((argc == 2) && (strcmp(argv[1], "tftp") == 0)) {
+ if (net_loop(TFTPGET) <= 0)
+ return 1;
+ printf("Automatic boot of VxWorks image at address 0x%08lx ...\n",
+ addr);
+ }
+#endif
+
+ /*
+ * This should equate to
+ * NV_RAM_ADRS + NV_BOOT_OFFSET + NV_ENET_OFFSET
+ * from the VxWorks BSP header files.
+ * This will vary from board to board
+ */
+#if defined(CONFIG_SYS_VXWORKS_MAC_PTR)
+ tmp = (char *)CONFIG_SYS_VXWORKS_MAC_PTR;
+ eth_env_get_enetaddr("ethaddr", (uchar *)build_buf);
+ memcpy(tmp, build_buf, 6);
+#else
+ puts("## Ethernet MAC address not copied to NV RAM\n");
+#endif
+
+#ifdef CONFIG_X86
+ /*
+ * Get VxWorks's physical memory base address from environment,
+ * if we don't specify it in the environment, use a default one.
+ */
+ base = env_get_hex("vx_phys_mem_base", VXWORKS_PHYS_MEM_BASE);
+ data = (struct e820_entry *)(base + E820_DATA_OFFSET);
+ info = (struct e820_info *)(base + E820_INFO_OFFSET);
+
+ memset(info, 0, sizeof(struct e820_info));
+ info->sign = E820_SIGNATURE;
+ info->entries = install_e820_map(E820MAX, data);
+ info->addr = (info->entries - 1) * sizeof(struct e820_entry) +
+ E820_DATA_OFFSET;
+
+ /*
+ * Explicitly clear the bootloader image size otherwise if memory
+ * at this offset happens to contain some garbage data, the final
+ * available memory size for the kernel is insane.
+ */
+ *(u32 *)(base + BOOT_IMAGE_SIZE_OFFSET) = 0;
+
+ /*
+ * Prepare compatible framebuffer information block.
+ * The VESA mode has to be 32-bit RGBA.
+ */
+ if (vesa->x_resolution && vesa->y_resolution) {
+ gop = (struct efi_gop_info *)(base + EFI_GOP_INFO_OFFSET);
+ gop->magic = EFI_GOP_INFO_MAGIC;
+ gop->info.version = 0;
+ gop->info.width = vesa->x_resolution;
+ gop->info.height = vesa->y_resolution;
+ gop->info.pixel_format = EFI_GOT_RGBA8;
+ gop->info.pixels_per_scanline = vesa->bytes_per_scanline / 4;
+ gop->fb_base = vesa->phys_base_ptr;
+ gop->fb_size = vesa->bytes_per_scanline * vesa->y_resolution;
+ }
+#endif
+
+ /*
+ * Use bootaddr to find the location in memory that VxWorks
+ * will look for the bootline string. The default value is
+ * (LOCAL_MEM_LOCAL_ADRS + BOOT_LINE_OFFSET) as defined by
+ * VxWorks BSP. For example, on PowerPC it defaults to 0x4200.
+ */
+ tmp = env_get("bootaddr");
+ if (!tmp) {
+#ifdef CONFIG_X86
+ bootaddr = base + X86_BOOT_LINE_OFFSET;
+#else
+ printf("## VxWorks bootline address not specified\n");
+ return 1;
+#endif
+ }
+
+ if (!bootaddr)
+ bootaddr = hextoul(tmp, NULL);
+
+ /*
+ * Check to see if the bootline is defined in the 'bootargs' parameter.
+ * If it is not defined, we may be able to construct the info.
+ */
+ bootline = env_get("bootargs");
+ if (!bootline) {
+ tmp = env_get("bootdev");
+ if (tmp) {
+ strlcpy(build_buf, tmp, BOOTLINE_BUF_LEN);
+ ptr = strlen(tmp);
+ } else {
+ printf("## VxWorks boot device not specified\n");
+ }
+
+ tmp = env_get("bootfile");
+ if (tmp)
+ ptr += sprintf(build_buf + ptr, "host:%s ", tmp);
+ else
+ ptr += sprintf(build_buf + ptr, "host:vxWorks ");
+
+ /*
+ * The following parameters are only needed if 'bootdev'
+ * is an ethernet device, otherwise they are optional.
+ */
+ tmp = env_get("ipaddr");
+ if (tmp) {
+ ptr += sprintf(build_buf + ptr, "e=%s", tmp);
+ tmp = env_get("netmask");
+ if (tmp) {
+ u32 mask = string_to_ip(tmp).s_addr;
+ ptr += sprintf(build_buf + ptr,
+ ":%08x ", ntohl(mask));
+ } else {
+ ptr += sprintf(build_buf + ptr, " ");
+ }
+ }
+
+ tmp = env_get("serverip");
+ if (tmp)
+ ptr += sprintf(build_buf + ptr, "h=%s ", tmp);
+
+ tmp = env_get("gatewayip");
+ if (tmp)
+ ptr += sprintf(build_buf + ptr, "g=%s ", tmp);
+
+ tmp = env_get("hostname");
+ if (tmp)
+ ptr += sprintf(build_buf + ptr, "tn=%s ", tmp);
+
+ tmp = env_get("othbootargs");
+ if (tmp) {
+ strcpy(build_buf + ptr, tmp);
+ ptr += strlen(tmp);
+ }
+
+ bootline = build_buf;
+ }
+
+ memcpy((void *)bootaddr, bootline, max(strlen(bootline), (size_t)255));
+ flush_cache(bootaddr, max(strlen(bootline), (size_t)255));
+ printf("## Using bootline (@ 0x%lx): %s\n", bootaddr, (char *)bootaddr);
+
+ /*
+ * If the data at the load address is an elf image, then
+ * treat it like an elf image. Otherwise, assume that it is a
+ * binary image.
+ */
+ if (valid_elf_image(addr))
+ addr = load_elf_image_phdr(addr);
+ else
+ puts("## Not an ELF image, assuming binary\n");
+
+ printf("## Starting vxWorks at 0x%08lx ...\n", addr);
+ flush();
+
+ dcache_disable();
+#if defined(CONFIG_ARM64) && defined(CONFIG_ARMV8_PSCI)
+ armv8_setup_psci();
+ smp_kick_all_cpus();
+#endif
+
+#ifdef CONFIG_X86
+ /* VxWorks on x86 uses stack to pass parameters */
+ ((asmlinkage void (*)(int))addr)(0);
+#else
+ ((void (*)(int))addr)(0);
+#endif
+
+ puts("## vxWorks terminated\n");
+
+ return 1;
+}
+#endif
+
+U_BOOT_CMD(
+ bootelf, CONFIG_SYS_MAXARGS, 0, do_bootelf,
+ "Boot from an ELF image in memory",
+ "[-p|-s] "
+#if CONFIG_IS_ENABLED(CMD_ELF_FDT_SETUP)
+ "[-d fdt_addr_r] "
+#endif
+ "[address]\n"
+ "\t- load ELF image at [address] via program headers (-p)\n"
+ "\t or via section headers (-s)\n"
+#if CONFIG_IS_ENABLED(CMD_ELF_FDT_SETUP)
+ "\t- setup FDT image at [fdt_addr_r] (-d)"
+#endif
+);
+
+#ifdef CONFIG_CMD_ELF_BOOTVX
+U_BOOT_CMD(
+ bootvx, 2, 0, do_bootvx,
+ "Boot vxWorks from an ELF image",
+ " [address] - load address of vxWorks ELF image."
+);
+#endif
diff --git a/cmd/erofs.c b/cmd/erofs.c
new file mode 100644
index 00000000000..add80b8b594
--- /dev/null
+++ b/cmd/erofs.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2022 Huang Jianan <jnhuang95@gmail.com>
+ *
+ * Author: Huang Jianan <jnhuang95@gmail.com>
+ *
+ * erofs.c: implements EROFS related commands
+ */
+
+#include <command.h>
+#include <fs.h>
+#include <erofs.h>
+
+static int do_erofs_ls(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ return do_ls(cmdtp, flag, argc, argv, FS_TYPE_EROFS);
+}
+
+U_BOOT_CMD(erofsls, 4, 1, do_erofs_ls,
+ "List files in directory. Default: root (/).",
+ "<interface> [<dev[:part]>] [directory]\n"
+ " - list files from 'dev' on 'interface' in 'directory'\n"
+);
+
+static int do_erofs_load(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ return do_load(cmdtp, flag, argc, argv, FS_TYPE_EROFS);
+}
+
+U_BOOT_CMD(erofsload, 7, 0, do_erofs_load,
+ "load binary file from a EROFS filesystem",
+ "<interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]\n"
+ " - Load binary file 'filename' from 'dev' on 'interface'\n"
+ " to address 'addr' from EROFS filesystem.\n"
+ " 'pos' gives the file position to start loading from.\n"
+ " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
+ " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
+ " the load stops on end of file.\n"
+ " If either 'pos' or 'bytes' are not aligned to\n"
+ " ARCH_DMA_MINALIGN then a misaligned buffer warning will\n"
+ " be printed and performance will suffer for the load."
+);
diff --git a/cmd/ethsw.c b/cmd/ethsw.c
new file mode 100644
index 00000000000..4bf49ac598f
--- /dev/null
+++ b/cmd/ethsw.c
@@ -0,0 +1,1103 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ *
+ * Ethernet Switch commands
+ */
+
+#include <command.h>
+#include <env.h>
+#include <errno.h>
+#include <env_flags.h>
+#include <ethsw.h>
+#include <net.h>
+#include <vsprintf.h>
+
+static const char *ethsw_name;
+
+#define ETHSW_PORT_STATS_HELP "ethsw [port <port_no>] statistics " \
+"{ [help] | [clear] } - show an l2 switch port's statistics"
+
+static int ethsw_port_stats_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+ printf(ETHSW_PORT_STATS_HELP"\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+#define ETHSW_LEARN_HELP "ethsw [port <port_no>] learning " \
+"{ [help] | show | auto | disable } " \
+"- enable/disable/show learning configuration on a port"
+
+static int ethsw_learn_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+ printf(ETHSW_LEARN_HELP"\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+#define ETHSW_FDB_HELP "ethsw [port <port_no>] [vlan <vid>] fdb " \
+"{ [help] | show | flush | { add | del } <mac> } " \
+"- Add/delete a mac entry in FDB; use show to see FDB entries; " \
+"if vlan <vid> is missing, VID 1 will be used"
+
+static int ethsw_fdb_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+ printf(ETHSW_FDB_HELP"\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+#define ETHSW_PVID_HELP "ethsw [port <port_no>] " \
+"pvid { [help] | show | <pvid> } " \
+"- set/show PVID (ingress and egress VLAN tagging) for a port"
+
+static int ethsw_pvid_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+ printf(ETHSW_PVID_HELP"\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+#define ETHSW_VLAN_HELP "ethsw [port <port_no>] vlan " \
+"{ [help] | show | add <vid> | del <vid> } " \
+"- add a VLAN to a port (VLAN members)"
+
+static int ethsw_vlan_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+ printf(ETHSW_VLAN_HELP"\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+#define ETHSW_PORT_UNTAG_HELP "ethsw [port <port_no>] untagged " \
+"{ [help] | show | all | none | pvid } " \
+" - set egress tagging mode for a port"
+
+static int ethsw_port_untag_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+ printf(ETHSW_PORT_UNTAG_HELP"\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+#define ETHSW_EGR_VLAN_TAG_HELP "ethsw [port <port_no>] egress tag " \
+"{ [help] | show | pvid | classified } " \
+"- Configure VID source for egress tag. " \
+"Tag's VID could be the frame's classified VID or the PVID of the port"
+
+static int ethsw_egr_tag_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+ printf(ETHSW_EGR_VLAN_TAG_HELP"\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+#define ETHSW_VLAN_FDB_HELP "ethsw vlan fdb " \
+"{ [help] | show | shared | private } " \
+"- make VLAN learning shared or private"
+
+static int ethsw_vlan_learn_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+ printf(ETHSW_VLAN_FDB_HELP"\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+#define ETHSW_PORT_INGR_FLTR_HELP "ethsw [port <port_no>] ingress filtering" \
+" { [help] | show | enable | disable } " \
+"- enable/disable VLAN ingress filtering on port"
+
+static int ethsw_ingr_fltr_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+ printf(ETHSW_PORT_INGR_FLTR_HELP"\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+#define ETHSW_PORT_AGGR_HELP "ethsw [port <port_no>] aggr" \
+" { [help] | show | <lag_group_no> } " \
+"- get/set LAG group for a port"
+
+static int ethsw_port_aggr_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+ printf(ETHSW_PORT_AGGR_HELP"\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+static struct keywords_to_function {
+ enum ethsw_keyword_id cmd_keyword[ETHSW_MAX_CMD_PARAMS];
+ int cmd_func_offset;
+ int (*keyword_function)(struct ethsw_command_def *parsed_cmd);
+} ethsw_cmd_def[] = {
+ {
+ .cmd_keyword = {
+ ethsw_id_enable,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_enable),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_disable,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_disable),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_show,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_show),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_statistics,
+ ethsw_id_help,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_port_stats_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_statistics,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_stats),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_statistics,
+ ethsw_id_clear,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_stats_clear),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_learning,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_learn_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_learning,
+ ethsw_id_help,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_learn_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_learning,
+ ethsw_id_show,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_learn_show),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_learning,
+ ethsw_id_auto,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_learn),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_learning,
+ ethsw_id_disable,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_learn),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_fdb,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_fdb_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_fdb,
+ ethsw_id_help,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_fdb_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_fdb,
+ ethsw_id_show,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ fdb_show),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_fdb,
+ ethsw_id_flush,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ fdb_flush),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_fdb,
+ ethsw_id_add,
+ ethsw_id_add_del_mac,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ fdb_entry_add),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_fdb,
+ ethsw_id_del,
+ ethsw_id_add_del_mac,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ fdb_entry_del),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_pvid,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_pvid_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_pvid,
+ ethsw_id_help,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_pvid_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_pvid,
+ ethsw_id_show,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ pvid_show),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_pvid,
+ ethsw_id_pvid_no,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ pvid_set),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_vlan,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_vlan_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_vlan,
+ ethsw_id_help,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_vlan_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_vlan,
+ ethsw_id_show,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ vlan_show),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_vlan,
+ ethsw_id_add,
+ ethsw_id_add_del_no,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ vlan_set),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_vlan,
+ ethsw_id_del,
+ ethsw_id_add_del_no,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ vlan_set),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_untagged,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_port_untag_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_untagged,
+ ethsw_id_help,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_port_untag_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_untagged,
+ ethsw_id_show,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_untag_show),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_untagged,
+ ethsw_id_all,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_untag_set),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_untagged,
+ ethsw_id_none,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_untag_set),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_untagged,
+ ethsw_id_pvid,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_untag_set),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_egress,
+ ethsw_id_tag,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_egr_tag_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_egress,
+ ethsw_id_tag,
+ ethsw_id_help,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_egr_tag_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_egress,
+ ethsw_id_tag,
+ ethsw_id_show,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_egr_vlan_show),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_egress,
+ ethsw_id_tag,
+ ethsw_id_pvid,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_egr_vlan_set),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_egress,
+ ethsw_id_tag,
+ ethsw_id_classified,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_egr_vlan_set),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_vlan,
+ ethsw_id_fdb,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_vlan_learn_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_vlan,
+ ethsw_id_fdb,
+ ethsw_id_help,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_vlan_learn_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_vlan,
+ ethsw_id_fdb,
+ ethsw_id_show,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ vlan_learn_show),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_vlan,
+ ethsw_id_fdb,
+ ethsw_id_shared,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ vlan_learn_set),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_vlan,
+ ethsw_id_fdb,
+ ethsw_id_private,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ vlan_learn_set),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_ingress,
+ ethsw_id_filtering,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_ingr_fltr_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_ingress,
+ ethsw_id_filtering,
+ ethsw_id_help,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_ingr_fltr_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_ingress,
+ ethsw_id_filtering,
+ ethsw_id_show,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_ingr_filt_show),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_ingress,
+ ethsw_id_filtering,
+ ethsw_id_enable,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_ingr_filt_set),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_ingress,
+ ethsw_id_filtering,
+ ethsw_id_disable,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_ingr_filt_set),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_aggr,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_port_aggr_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_aggr,
+ ethsw_id_help,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = -1,
+ .keyword_function = &ethsw_port_aggr_help_key_func,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_aggr,
+ ethsw_id_show,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_aggr_show),
+ .keyword_function = NULL,
+ }, {
+ .cmd_keyword = {
+ ethsw_id_aggr,
+ ethsw_id_aggr_no,
+ ethsw_id_key_end,
+ },
+ .cmd_func_offset = offsetof(struct ethsw_command_func,
+ port_aggr_set),
+ .keyword_function = NULL,
+ },
+};
+
+struct keywords_optional {
+ int cmd_keyword[ETHSW_MAX_CMD_PARAMS];
+} cmd_opt_def[] = {
+ {
+ .cmd_keyword = {
+ ethsw_id_port,
+ ethsw_id_port_no,
+ ethsw_id_key_end,
+ },
+ }, {
+ .cmd_keyword = {
+ ethsw_id_vlan,
+ ethsw_id_vlan_no,
+ ethsw_id_key_end,
+ },
+ }, {
+ .cmd_keyword = {
+ ethsw_id_port,
+ ethsw_id_port_no,
+ ethsw_id_vlan,
+ ethsw_id_vlan_no,
+ ethsw_id_key_end,
+ },
+ },
+};
+
+static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, char
+ *const argv[], int *argc_nr,
+ struct ethsw_command_def *parsed_cmd);
+static int keyword_match_port(enum ethsw_keyword_id key_id, int argc,
+ char *const argv[], int *argc_nr,
+ struct ethsw_command_def *parsed_cmd);
+static int keyword_match_vlan(enum ethsw_keyword_id key_id, int argc,
+ char *const argv[], int *argc_nr,
+ struct ethsw_command_def *parsed_cmd);
+static int keyword_match_pvid(enum ethsw_keyword_id key_id, int argc,
+ char *const argv[], int *argc_nr,
+ struct ethsw_command_def *parsed_cmd);
+static int keyword_match_mac_addr(enum ethsw_keyword_id key_id, int argc,
+ char *const argv[], int *argc_nr,
+ struct ethsw_command_def *parsed_cmd);
+static int keyword_match_aggr(enum ethsw_keyword_id key_id, int argc,
+ char *const argv[], int *argc_nr,
+ struct ethsw_command_def *parsed_cmd);
+
+/*
+ * Define properties for each keyword;
+ * keep the order synced with enum ethsw_keyword_id
+ */
+struct keyword_def {
+ const char *keyword_name;
+ int (*match)(enum ethsw_keyword_id key_id, int argc, char *const argv[],
+ int *argc_nr, struct ethsw_command_def *parsed_cmd);
+} keyword[] = {
+ {
+ .keyword_name = "help",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "show",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "port",
+ .match = &keyword_match_port
+ }, {
+ .keyword_name = "enable",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "disable",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "statistics",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "clear",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "learning",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "auto",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "vlan",
+ .match = &keyword_match_vlan,
+ }, {
+ .keyword_name = "fdb",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "add",
+ .match = &keyword_match_mac_addr,
+ }, {
+ .keyword_name = "del",
+ .match = &keyword_match_mac_addr,
+ }, {
+ .keyword_name = "flush",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "pvid",
+ .match = &keyword_match_pvid,
+ }, {
+ .keyword_name = "untagged",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "all",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "none",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "egress",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "tag",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "classified",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "shared",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "private",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "ingress",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "filtering",
+ .match = &keyword_match_gen,
+ }, {
+ .keyword_name = "aggr",
+ .match = &keyword_match_aggr,
+ },
+};
+
+/*
+ * Function used by an Ethernet Switch driver to set the functions
+ * that must be called by the parser when an ethsw command is given
+ */
+int ethsw_define_functions(const struct ethsw_command_func *cmd_func)
+{
+ int i;
+ void **aux_p;
+ int (*cmd_func_aux)(struct ethsw_command_def *);
+
+ if (!cmd_func->ethsw_name)
+ return -EINVAL;
+
+ ethsw_name = cmd_func->ethsw_name;
+
+ for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
+ /*
+ * get the pointer to the function send by the Ethernet Switch
+ * driver that corresponds to the proper ethsw command
+ */
+ if (ethsw_cmd_def[i].keyword_function)
+ continue;
+
+ aux_p = (void *)cmd_func + ethsw_cmd_def[i].cmd_func_offset;
+
+ cmd_func_aux = (int (*)(struct ethsw_command_def *)) *aux_p;
+ ethsw_cmd_def[i].keyword_function = cmd_func_aux;
+ }
+
+ return 0;
+}
+
+/* Generic function used to match a keyword only by a string */
+static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc,
+ char *const argv[], int *argc_nr,
+ struct ethsw_command_def *parsed_cmd)
+{
+ if (strcmp(argv[*argc_nr], keyword[key_id].keyword_name) == 0) {
+ parsed_cmd->cmd_to_keywords[*argc_nr] = key_id;
+
+ return 1;
+ }
+ return 0;
+}
+
+/* Function used to match the command's port */
+static int keyword_match_port(enum ethsw_keyword_id key_id, int argc,
+ char *const argv[], int *argc_nr,
+ struct ethsw_command_def *parsed_cmd)
+{
+ unsigned long val;
+
+ if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
+ return 0;
+
+ if (*argc_nr + 1 >= argc)
+ return 0;
+
+ if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) {
+ parsed_cmd->port = val;
+ (*argc_nr)++;
+ parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_port_no;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Function used to match the command's vlan */
+static int keyword_match_vlan(enum ethsw_keyword_id key_id, int argc,
+ char *const argv[], int *argc_nr,
+ struct ethsw_command_def *parsed_cmd)
+{
+ unsigned long val;
+ int aux;
+
+ if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
+ return 0;
+
+ if (*argc_nr + 1 >= argc)
+ return 0;
+
+ if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) {
+ parsed_cmd->vid = val;
+ (*argc_nr)++;
+ parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_vlan_no;
+ return 1;
+ }
+
+ aux = *argc_nr + 1;
+
+ if (keyword_match_gen(ethsw_id_add, argc, argv, &aux, parsed_cmd))
+ parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_add;
+ else if (keyword_match_gen(ethsw_id_del, argc, argv, &aux, parsed_cmd))
+ parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_del;
+ else
+ return 0;
+
+ if (*argc_nr + 2 >= argc)
+ return 0;
+
+ if (strict_strtoul(argv[*argc_nr + 2], 10, &val) != -EINVAL) {
+ parsed_cmd->vid = val;
+ (*argc_nr) += 2;
+ parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_add_del_no;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Function used to match the command's pvid */
+static int keyword_match_pvid(enum ethsw_keyword_id key_id, int argc,
+ char *const argv[], int *argc_nr,
+ struct ethsw_command_def *parsed_cmd)
+{
+ unsigned long val;
+
+ if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
+ return 0;
+
+ if (*argc_nr + 1 >= argc)
+ return 1;
+
+ if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) {
+ parsed_cmd->vid = val;
+ (*argc_nr)++;
+ parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_pvid_no;
+ }
+
+ return 1;
+}
+
+/* Function used to match the command's MAC address */
+static int keyword_match_mac_addr(enum ethsw_keyword_id key_id, int argc,
+ char *const argv[], int *argc_nr,
+ struct ethsw_command_def *parsed_cmd)
+{
+ if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
+ return 0;
+
+ if ((*argc_nr + 1 >= argc) ||
+ !is_broadcast_ethaddr(parsed_cmd->ethaddr))
+ return 1;
+
+ if (eth_validate_ethaddr_str(argv[*argc_nr + 1])) {
+ printf("Invalid MAC address: %s\n", argv[*argc_nr + 1]);
+ return 0;
+ }
+
+ string_to_enetaddr(argv[*argc_nr + 1], parsed_cmd->ethaddr);
+
+ if (is_broadcast_ethaddr(parsed_cmd->ethaddr)) {
+ memset(parsed_cmd->ethaddr, 0xFF, sizeof(parsed_cmd->ethaddr));
+ return 0;
+ }
+
+ parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_add_del_mac;
+
+ return 1;
+}
+
+/* Function used to match the command's aggregation number */
+static int keyword_match_aggr(enum ethsw_keyword_id key_id, int argc,
+ char *const argv[], int *argc_nr,
+ struct ethsw_command_def *parsed_cmd)
+{
+ unsigned long val;
+
+ if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
+ return 0;
+
+ if (*argc_nr + 1 >= argc)
+ return 1;
+
+ if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) {
+ parsed_cmd->aggr_grp = val;
+ (*argc_nr)++;
+ parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_aggr_no;
+ }
+
+ return 1;
+}
+
+/* Finds optional keywords and modifies *argc_va to skip them */
+static void cmd_keywords_opt_check(const struct ethsw_command_def *parsed_cmd,
+ int *argc_val)
+{
+ int i;
+ int keyw_opt_matched;
+ int argc_val_max;
+ int const *cmd_keyw_p;
+ int const *cmd_keyw_opt_p;
+
+ /* remember the best match */
+ argc_val_max = *argc_val;
+
+ /*
+ * check if our command's optional keywords match the optional
+ * keywords of an available command
+ */
+ for (i = 0; i < ARRAY_SIZE(cmd_opt_def); i++) {
+ keyw_opt_matched = 0;
+ cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_opt_matched];
+ cmd_keyw_opt_p = &cmd_opt_def[i].cmd_keyword[keyw_opt_matched];
+
+ /*
+ * increase the number of keywords that
+ * matched with a command
+ */
+ while (keyw_opt_matched + *argc_val <
+ parsed_cmd->cmd_keywords_nr &&
+ *cmd_keyw_opt_p != ethsw_id_key_end &&
+ *(cmd_keyw_p + *argc_val) == *cmd_keyw_opt_p) {
+ keyw_opt_matched++;
+ cmd_keyw_p++;
+ cmd_keyw_opt_p++;
+ }
+
+ /*
+ * if all our optional command's keywords perfectly match an
+ * optional pattern, then we can move to the next defined
+ * keywords in our command; remember the one that matched the
+ * greatest number of keywords
+ */
+ if (keyw_opt_matched + *argc_val <=
+ parsed_cmd->cmd_keywords_nr &&
+ *cmd_keyw_opt_p == ethsw_id_key_end &&
+ *argc_val + keyw_opt_matched > argc_val_max)
+ argc_val_max = *argc_val + keyw_opt_matched;
+ }
+
+ *argc_val = argc_val_max;
+}
+
+/*
+ * Finds the function to call based on keywords and
+ * modifies *argc_va to skip them
+ */
+static void cmd_keywords_check(struct ethsw_command_def *parsed_cmd,
+ int *argc_val)
+{
+ int i;
+ int keyw_matched;
+ int *cmd_keyw_p;
+ int *cmd_keyw_def_p;
+
+ /*
+ * check if our command's keywords match the
+ * keywords of an available command
+ */
+ for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
+ keyw_matched = 0;
+ cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_matched];
+ cmd_keyw_def_p = &ethsw_cmd_def[i].cmd_keyword[keyw_matched];
+
+ /*
+ * increase the number of keywords that
+ * matched with a command
+ */
+ while (keyw_matched + *argc_val < parsed_cmd->cmd_keywords_nr &&
+ *cmd_keyw_def_p != ethsw_id_key_end &&
+ *(cmd_keyw_p + *argc_val) == *cmd_keyw_def_p) {
+ keyw_matched++;
+ cmd_keyw_p++;
+ cmd_keyw_def_p++;
+ }
+
+ /*
+ * if all our command's keywords perfectly match an
+ * available command, then we get the function we need to call
+ * to configure the Ethernet Switch
+ */
+ if (keyw_matched && keyw_matched + *argc_val ==
+ parsed_cmd->cmd_keywords_nr &&
+ *cmd_keyw_def_p == ethsw_id_key_end) {
+ *argc_val += keyw_matched;
+ parsed_cmd->cmd_function =
+ ethsw_cmd_def[i].keyword_function;
+ return;
+ }
+ }
+}
+
+/* find all the keywords in the command */
+static int keywords_find(int argc, char *const argv[],
+ struct ethsw_command_def *parsed_cmd)
+{
+ int i;
+ int j;
+ int argc_val;
+ int rc = CMD_RET_SUCCESS;
+
+ for (i = 1; i < argc; i++) {
+ for (j = 0; j < ethsw_id_count; j++) {
+ if (keyword[j].match(j, argc, argv, &i, parsed_cmd))
+ break;
+ }
+ }
+
+ /* if there is no keyword match for a word, the command is invalid */
+ for (i = 1; i < argc; i++)
+ if (parsed_cmd->cmd_to_keywords[i] == ethsw_id_key_end)
+ rc = CMD_RET_USAGE;
+
+ parsed_cmd->cmd_keywords_nr = argc;
+ argc_val = 1;
+
+ /* get optional parameters first */
+ cmd_keywords_opt_check(parsed_cmd, &argc_val);
+
+ if (argc_val == parsed_cmd->cmd_keywords_nr)
+ return CMD_RET_USAGE;
+
+ /*
+ * check the keywords and if a match is found,
+ * get the function to call
+ */
+ cmd_keywords_check(parsed_cmd, &argc_val);
+
+ /* error if not all commands' parameters were matched */
+ if (argc_val == parsed_cmd->cmd_keywords_nr) {
+ if (!parsed_cmd->cmd_function) {
+ printf("Command not available for: %s\n", ethsw_name);
+ rc = CMD_RET_FAILURE;
+ }
+ } else {
+ rc = CMD_RET_USAGE;
+ }
+
+ return rc;
+}
+
+static void command_def_init(struct ethsw_command_def *parsed_cmd)
+{
+ int i;
+
+ for (i = 0; i < ETHSW_MAX_CMD_PARAMS; i++)
+ parsed_cmd->cmd_to_keywords[i] = ethsw_id_key_end;
+
+ parsed_cmd->port = ETHSW_CMD_PORT_ALL;
+ parsed_cmd->vid = ETHSW_CMD_VLAN_ALL;
+ parsed_cmd->aggr_grp = ETHSW_CMD_AGGR_GRP_NONE;
+ parsed_cmd->cmd_function = NULL;
+
+ /* We initialize the MAC address with the Broadcast address */
+ memset(parsed_cmd->ethaddr, 0xff, sizeof(parsed_cmd->ethaddr));
+}
+
+/* function to interpret commands starting with "ethsw " */
+static int do_ethsw(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct ethsw_command_def parsed_cmd;
+ int rc = CMD_RET_SUCCESS;
+
+ if (argc == 1 || argc >= ETHSW_MAX_CMD_PARAMS)
+ return CMD_RET_USAGE;
+
+ command_def_init(&parsed_cmd);
+
+ rc = keywords_find(argc, argv, &parsed_cmd);
+
+ if (rc == CMD_RET_SUCCESS)
+ rc = parsed_cmd.cmd_function(&parsed_cmd);
+
+ return rc;
+}
+
+#define ETHSW_PORT_CONF_HELP "[port <port_no>] { enable | disable | show } " \
+"- enable/disable a port; show a port's configuration"
+
+U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw,
+ "Ethernet l2 switch commands",
+ ETHSW_PORT_CONF_HELP"\n"
+ ETHSW_PORT_STATS_HELP"\n"
+ ETHSW_LEARN_HELP"\n"
+ ETHSW_FDB_HELP"\n"
+ ETHSW_PVID_HELP"\n"
+ ETHSW_VLAN_HELP"\n"
+ ETHSW_PORT_UNTAG_HELP"\n"
+ ETHSW_EGR_VLAN_TAG_HELP"\n"
+ ETHSW_VLAN_FDB_HELP"\n"
+ ETHSW_PORT_INGR_FLTR_HELP"\n"
+ ETHSW_PORT_AGGR_HELP"\n"
+);
diff --git a/cmd/event.c b/cmd/event.c
new file mode 100644
index 00000000000..00c828757ca
--- /dev/null
+++ b/cmd/event.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Command-line access to events
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <command.h>
+#include <event.h>
+
+static int do_event_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ event_show_spy_list();
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(event,
+ "list - list event spies");
+
+U_BOOT_CMD_WITH_SUBCMDS(event, "Events", event_help_text,
+ U_BOOT_SUBCMD_MKENT(list, 1, 1, do_event_list));
diff --git a/cmd/exit.c b/cmd/exit.c
new file mode 100644
index 00000000000..d125ec1e31f
--- /dev/null
+++ b/cmd/exit.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2000-2009
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <vsprintf.h>
+
+static int do_exit(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int r;
+
+ r = 0;
+ if (argc > 1)
+ r = simple_strtoul(argv[1], NULL, 10);
+
+ return -r - 2;
+}
+
+U_BOOT_CMD(
+ exit, 2, 1, do_exit,
+ "exit script",
+ ""
+);
diff --git a/cmd/ext2.c b/cmd/ext2.c
new file mode 100644
index 00000000000..45c8b353b58
--- /dev/null
+++ b/cmd/ext2.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2011 - 2012 Samsung Electronics
+ * EXT4 filesystem implementation in Uboot by
+ * Uma Shankar <uma.shankar@samsung.com>
+ * Manjunatha C Achar <a.manjunatha@samsung.com>
+
+ * (C) Copyright 2004
+ * esd gmbh <www.esd-electronics.com>
+ * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
+ *
+ * made from cmd_reiserfs by
+ *
+ * (C) Copyright 2003 - 2004
+ * Sysgo Real-Time Solutions, AG <www.elinos.com>
+ * Pavel Bartusek <pba@sysgo.com>
+ */
+
+/*
+ * Ext2fs support
+ */
+#include <command.h>
+#include <fs.h>
+
+static int do_ext2ls(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_ls(cmdtp, flag, argc, argv, FS_TYPE_EXT);
+}
+
+/******************************************************************************
+ * Ext2fs boot command intepreter. Derived from diskboot
+ */
+int do_ext2load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ return do_load(cmdtp, flag, argc, argv, FS_TYPE_EXT);
+}
+
+U_BOOT_CMD(
+ ext2ls, 4, 1, do_ext2ls,
+ "list files in a directory (default /)",
+ "<interface> <dev[:part]> [directory]\n"
+ " - list files from 'dev' on 'interface' in a 'directory'"
+);
+
+U_BOOT_CMD(
+ ext2load, 6, 0, do_ext2load,
+ "load binary file from a Ext2 filesystem",
+ "<interface> [<dev[:part]> [addr [filename [bytes [pos]]]]]\n"
+ " - load binary file 'filename' from 'dev' on 'interface'\n"
+ " to address 'addr' from ext2 filesystem."
+);
diff --git a/cmd/ext4.c b/cmd/ext4.c
new file mode 100644
index 00000000000..40d1fe30d5e
--- /dev/null
+++ b/cmd/ext4.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2011 - 2012 Samsung Electronics
+ * EXT4 filesystem implementation in Uboot by
+ * Uma Shankar <uma.shankar@samsung.com>
+ * Manjunatha C Achar <a.manjunatha@samsung.com>
+ *
+ * Ext4fs support
+ * made from existing cmd_ext2.c file of Uboot
+ *
+ * (C) Copyright 2004
+ * esd gmbh <www.esd-electronics.com>
+ * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
+ *
+ * made from cmd_reiserfs by
+ *
+ * (C) Copyright 2003 - 2004
+ * Sysgo Real-Time Solutions, AG <www.elinos.com>
+ * Pavel Bartusek <pba@sysgo.com>
+ */
+
+/*
+ * Changelog:
+ * 0.1 - Newly created file for ext4fs support. Taken from cmd_ext2.c
+ * file in uboot. Added ext4fs ls load and write support.
+ */
+
+#include <part.h>
+#include <config.h>
+#include <command.h>
+#include <image.h>
+#include <linux/ctype.h>
+#include <asm/byteorder.h>
+#include <ext4fs.h>
+#include <linux/stat.h>
+#include <malloc.h>
+#include <fs.h>
+
+#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE)
+#include <usb.h>
+#endif
+
+int do_ext4_size(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ return do_size(cmdtp, flag, argc, argv, FS_TYPE_EXT);
+}
+
+int do_ext4_load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ return do_load(cmdtp, flag, argc, argv, FS_TYPE_EXT);
+}
+
+int do_ext4_ls(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ return do_ls(cmdtp, flag, argc, argv, FS_TYPE_EXT);
+}
+
+#if defined(CONFIG_CMD_EXT4_WRITE)
+int do_ext4_write(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_save(cmdtp, flag, argc, argv, FS_TYPE_EXT);
+}
+
+U_BOOT_CMD(ext4write, 7, 1, do_ext4_write,
+ "create a file in the root directory",
+ "<interface> <dev[:part]> <addr> <absolute filename path>\n"
+ " [sizebytes] [file offset]\n"
+ " - create a file in / directory");
+
+#endif
+
+U_BOOT_CMD(
+ ext4size, 4, 0, do_ext4_size,
+ "determine a file's size",
+ "<interface> <dev[:part]> <filename>\n"
+ " - Find file 'filename' from 'dev' on 'interface'\n"
+ " and determine its size."
+);
+
+U_BOOT_CMD(ext4ls, 4, 1, do_ext4_ls,
+ "list files in a directory (default /)",
+ "<interface> <dev[:part]> [directory]\n"
+ " - list files from 'dev' on 'interface' in a 'directory'");
+
+U_BOOT_CMD(ext4load, 7, 0, do_ext4_load,
+ "load binary file from a Ext4 filesystem",
+ "<interface> [<dev[:part]> [addr [filename [bytes [pos]]]]]\n"
+ " - load binary file 'filename' from 'dev' on 'interface'\n"
+ " to address 'addr' from ext4 filesystem");
diff --git a/cmd/extension_board.c b/cmd/extension_board.c
new file mode 100644
index 00000000000..317b260bf36
--- /dev/null
+++ b/cmd/extension_board.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2021
+ * Köry Maincent, Bootlin, <kory.maincent@bootlin.com>
+ */
+
+#include <bootdev.h>
+#include <command.h>
+#include <dm.h>
+#include <env.h>
+#include <malloc.h>
+#include <extension_board.h>
+#include <mapmem.h>
+#include <linux/libfdt.h>
+#include <fdt_support.h>
+
+static LIST_HEAD(extension_list);
+
+static int extension_apply(struct extension *extension)
+{
+ char *overlay_cmd;
+ ulong extrasize, overlay_addr;
+ struct fdt_header *blob;
+
+ if (!working_fdt) {
+ printf("No FDT memory address configured. Please configure\n"
+ "the FDT address via \"fdt addr <address>\" command.\n");
+ return CMD_RET_FAILURE;
+ }
+
+ overlay_cmd = env_get("extension_overlay_cmd");
+ if (!overlay_cmd) {
+ printf("Environment extension_overlay_cmd is missing\n");
+ return CMD_RET_FAILURE;
+ }
+
+ overlay_addr = env_get_hex("extension_overlay_addr", 0);
+ if (!overlay_addr) {
+ printf("Environment extension_overlay_addr is missing\n");
+ return CMD_RET_FAILURE;
+ }
+
+ env_set("extension_overlay_name", extension->overlay);
+ if (run_command(overlay_cmd, 0) != 0)
+ return CMD_RET_FAILURE;
+
+ extrasize = env_get_hex("filesize", 0);
+ if (!extrasize)
+ return CMD_RET_FAILURE;
+
+ fdt_shrink_to_minimum(working_fdt, extrasize);
+
+ blob = map_sysmem(overlay_addr, 0);
+ if (!fdt_valid(&blob))
+ return CMD_RET_FAILURE;
+
+ /* apply method prints messages on error */
+ if (fdt_overlay_apply_verbose(working_fdt, blob))
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_extension_list(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ int i = 0;
+ struct extension *extension;
+
+ if (list_empty(&extension_list)) {
+ printf("No extension registered - Please run \"extension scan\"\n");
+ return CMD_RET_SUCCESS;
+ }
+
+ list_for_each_entry(extension, &extension_list, list) {
+ printf("Extension %d: %s\n", i++, extension->name);
+ printf("\tManufacturer: \t\t%s\n", extension->owner);
+ printf("\tVersion: \t\t%s\n", extension->version);
+ printf("\tDevicetree overlay: \t%s\n", extension->overlay);
+ printf("\tOther information: \t%s\n", extension->other);
+ }
+ return CMD_RET_SUCCESS;
+}
+
+static int extension_scan(bool show)
+{
+ struct extension *extension, *next;
+ int extension_num;
+
+ list_for_each_entry_safe(extension, next, &extension_list, list) {
+ list_del(&extension->list);
+ free(extension);
+ }
+ extension_num = extension_board_scan(&extension_list);
+ if (show && extension_num >= 0)
+ printf("Found %d extension board(s).\n", extension_num);
+
+ /* either the number of extensions, or -ve for error */
+ return extension_num;
+}
+
+static int do_extension_scan(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ int extension_num;
+
+ extension_num = extension_scan(true);
+ if (extension_num < 0)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_extension_apply(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct extension *extension = NULL;
+ struct list_head *entry;
+ int i = 0, extension_id, ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (strcmp(argv[1], "all") == 0) {
+ ret = CMD_RET_FAILURE;
+ list_for_each_entry(extension, &extension_list, list) {
+ ret = extension_apply(extension);
+ if (ret != CMD_RET_SUCCESS)
+ break;
+ }
+ } else {
+ extension_id = simple_strtol(argv[1], NULL, 10);
+ list_for_each(entry, &extension_list) {
+ if (i == extension_id) {
+ extension = list_entry(entry, struct extension, list);
+ break;
+ }
+ i++;
+ }
+
+ if (!extension) {
+ printf("Wrong extension number\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = extension_apply(extension);
+ }
+
+ return ret;
+}
+
+static struct cmd_tbl cmd_extension[] = {
+ U_BOOT_CMD_MKENT(scan, 1, 1, do_extension_scan, "", ""),
+ U_BOOT_CMD_MKENT(list, 1, 0, do_extension_list, "", ""),
+ U_BOOT_CMD_MKENT(apply, 2, 0, do_extension_apply, "", ""),
+};
+
+static int do_extensionops(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ /* Drop the extension command */
+ argc--;
+ argv++;
+
+ cp = find_cmd_tbl(argv[0], cmd_extension, ARRAY_SIZE(cmd_extension));
+ if (cp)
+ return cp->cmd(cmdtp, flag, argc, argv);
+
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(extension, 3, 1, do_extensionops,
+ "Extension board management sub system",
+ "scan - scan plugged extension(s) board(s)\n"
+ "extension list - lists available extension(s) board(s)\n"
+ "extension apply <extension number|all> - applies DT overlays corresponding to extension boards\n"
+);
+
+static int extension_bootdev_hunt(struct bootdev_hunter *info, bool show)
+{
+ int ret;
+
+ ret = env_set_hex("extension_overlay_addr",
+ env_get_hex("fdtoverlay_addr_r", 0));
+ if (ret)
+ return log_msg_ret("env", ret);
+
+ ret = extension_scan(show);
+ if (ret < 0)
+ return log_msg_ret("ext", ret);
+
+ return 0;
+}
+
+/* extensions should have a uclass - for now we use UCLASS_SIMPLE_BUS uclass */
+BOOTDEV_HUNTER(extension_bootdev_hunter) = {
+ .prio = BOOTDEVP_1_PRE_SCAN,
+ .uclass = UCLASS_SIMPLE_BUS,
+ .hunt = extension_bootdev_hunt,
+};
diff --git a/cmd/fastboot.c b/cmd/fastboot.c
new file mode 100644
index 00000000000..be84a482b81
--- /dev/null
+++ b/cmd/fastboot.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2008 - 2009 Windriver, <www.windriver.com>
+ * Author: Tom Rix <Tom.Rix@windriver.com>
+ *
+ * (C) Copyright 2014 Linaro, Ltd.
+ * Rob Herring <robh@kernel.org>
+ */
+#include <command.h>
+#include <console.h>
+#include <g_dnl.h>
+#include <fastboot.h>
+#include <net.h>
+#include <usb.h>
+#include <watchdog.h>
+#include <linux/printk.h>
+#include <linux/stringify.h>
+
+#if CONFIG_IS_ENABLED(NET)
+static int do_fastboot_udp(int argc, char *const argv[],
+ uintptr_t buf_addr, size_t buf_size)
+{
+ int err;
+
+ if (!IS_ENABLED(CONFIG_UDP_FUNCTION_FASTBOOT)) {
+ pr_err("Fastboot UDP not enabled\n");
+ return CMD_RET_FAILURE;
+ }
+
+ err = net_loop(FASTBOOT_UDP);
+
+ if (err < 0) {
+ printf("fastboot udp error: %d\n", err);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_fastboot_tcp(int argc, char *const argv[],
+ uintptr_t buf_addr, size_t buf_size)
+{
+ int err;
+
+ if (!IS_ENABLED(CONFIG_TCP_FUNCTION_FASTBOOT)) {
+ pr_err("Fastboot TCP not enabled\n");
+ return CMD_RET_FAILURE;
+ }
+
+ err = net_loop(FASTBOOT_TCP);
+
+ if (err < 0) {
+ printf("fastboot tcp error: %d\n", err);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+#endif
+
+static int do_fastboot_usb(int argc, char *const argv[],
+ uintptr_t buf_addr, size_t buf_size)
+{
+ int controller_index;
+ char *usb_controller;
+ struct udevice *udc;
+ char *endp;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_USB_FUNCTION_FASTBOOT)) {
+ pr_err("Fastboot USB not enabled\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ usb_controller = argv[1];
+ controller_index = simple_strtoul(usb_controller, &endp, 0);
+ if (*endp != '\0') {
+ pr_err("Error: Wrong USB controller index format\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = udc_device_get_by_index(controller_index, &udc);
+ if (ret) {
+ pr_err("USB init failed: %d\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ g_dnl_clear_detach();
+ ret = g_dnl_register("usb_dnl_fastboot");
+ if (ret)
+ return ret;
+
+ if (!g_dnl_board_usb_cable_connected()) {
+ puts("\rUSB cable not detected.\n" \
+ "Command exit.\n");
+ ret = CMD_RET_FAILURE;
+ goto exit;
+ }
+
+ while (1) {
+ if (g_dnl_detach())
+ break;
+ if (ctrlc())
+ break;
+ schedule();
+ dm_usb_gadget_handle_interrupts(udc);
+ }
+
+ ret = CMD_RET_SUCCESS;
+
+exit:
+ udc_device_put(udc);
+ g_dnl_unregister();
+ g_dnl_clear_detach();
+
+ return ret;
+}
+
+static int do_fastboot(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ uintptr_t buf_addr = (uintptr_t)NULL;
+ size_t buf_size = 0;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ while (argc > 1 && **(argv + 1) == '-') {
+ char *arg = *++argv;
+
+ --argc;
+ while (*++arg) {
+ switch (*arg) {
+ case 'l':
+ if (--argc <= 0)
+ return CMD_RET_USAGE;
+ buf_addr = hextoul(*++argv, NULL);
+ goto NXTARG;
+
+ case 's':
+ if (--argc <= 0)
+ return CMD_RET_USAGE;
+ buf_size = hextoul(*++argv, NULL);
+ goto NXTARG;
+
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+NXTARG:
+ ;
+ }
+
+ /* Handle case when USB controller param is just '-' */
+ if (argc == 1) {
+ pr_err("Error: Incorrect USB controller index\n");
+ return CMD_RET_USAGE;
+ }
+
+ fastboot_init((void *)buf_addr, buf_size);
+
+#if CONFIG_IS_ENABLED(NET)
+ if (!strcmp(argv[1], "udp"))
+ return do_fastboot_udp(argc, argv, buf_addr, buf_size);
+ if (!strcmp(argv[1], "tcp"))
+ return do_fastboot_tcp(argc, argv, buf_addr, buf_size);
+#endif
+ if (!strcmp(argv[1], "usb")) {
+ argv++;
+ argc--;
+ }
+
+ return do_fastboot_usb(argc, argv, buf_addr, buf_size);
+}
+
+U_BOOT_CMD(
+ fastboot, CONFIG_SYS_MAXARGS, 1, do_fastboot,
+ "run as a fastboot usb or udp device",
+ "[-l addr] [-s size] usb <controller> | udp\n"
+ "\taddr - address of buffer used during data transfers ("
+ __stringify(CONFIG_FASTBOOT_BUF_ADDR) ")\n"
+ "\tsize - size of buffer used during data transfers ("
+ __stringify(CONFIG_FASTBOOT_BUF_SIZE) ")"
+);
diff --git a/cmd/fat.c b/cmd/fat.c
new file mode 100644
index 00000000000..5b7484dc1af
--- /dev/null
+++ b/cmd/fat.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2002
+ * Richard Jones, rjones@nexus-tech.net
+ */
+
+/*
+ * Boot support
+ */
+#include <command.h>
+#include <mapmem.h>
+#include <fat.h>
+#include <fs.h>
+#include <part.h>
+#include <asm/cache.h>
+
+static int do_fat_size(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ return do_size(cmdtp, flag, argc, argv, FS_TYPE_FAT);
+}
+
+U_BOOT_CMD(
+ fatsize, 4, 0, do_fat_size,
+ "determine a file's size",
+ "<interface> <dev[:part]> <filename>\n"
+ " - Find file 'filename' from 'dev' on 'interface'\n"
+ " and determine its size."
+);
+
+int do_fat_fsload(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ return do_load(cmdtp, flag, argc, argv, FS_TYPE_FAT);
+}
+
+U_BOOT_CMD(
+ fatload, 7, 0, do_fat_fsload,
+ "load binary file from a dos filesystem",
+ "<interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]\n"
+ " - Load binary file 'filename' from 'dev' on 'interface'\n"
+ " to address 'addr' from dos filesystem.\n"
+ " 'pos' gives the file position to start loading from.\n"
+ " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
+ " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
+ " the load stops on end of file.\n"
+ " If either 'pos' or 'bytes' are not aligned to\n"
+ " ARCH_DMA_MINALIGN then a misaligned buffer warning will\n"
+ " be printed and performance will suffer for the load."
+);
+
+static int do_fat_ls(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_ls(cmdtp, flag, argc, argv, FS_TYPE_FAT);
+}
+
+U_BOOT_CMD(
+ fatls, 4, 1, do_fat_ls,
+ "list files in a directory (default /)",
+ "<interface> [<dev[:part]>] [directory]\n"
+ " - list files from 'dev' on 'interface' in a 'directory'"
+);
+
+static int do_fat_fsinfo(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int dev, part;
+ struct blk_desc *dev_desc;
+ struct disk_partition info;
+
+ if (argc < 2) {
+ printf("usage: fatinfo <interface> [<dev[:part]>]\n");
+ return 0;
+ }
+
+ part = blk_get_device_part_str(argv[1], argv[2], &dev_desc, &info, 1);
+ if (part < 0)
+ return 1;
+
+ dev = dev_desc->devnum;
+ if (fat_set_blk_dev(dev_desc, &info) != 0) {
+ printf("\n** Unable to use %s %d:%d for fatinfo **\n",
+ argv[1], dev, part);
+ return 1;
+ }
+ return file_fat_detectfs();
+}
+
+U_BOOT_CMD(
+ fatinfo, 3, 1, do_fat_fsinfo,
+ "print information about filesystem",
+ "<interface> [<dev[:part]>]\n"
+ " - print information about filesystem from 'dev' on 'interface'"
+);
+
+#ifdef CONFIG_FAT_WRITE
+static int do_fat_fswrite(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_save(cmdtp, flag, argc, argv, FS_TYPE_FAT);
+}
+
+U_BOOT_CMD(
+ fatwrite, 7, 0, do_fat_fswrite,
+ "write file into a dos filesystem",
+ "<interface> <dev[:part]> <addr> <filename> [<bytes> [<offset>]]\n"
+ " - write file 'filename' from the address 'addr' in RAM\n"
+ " to 'dev' on 'interface'"
+);
+
+static int do_fat_rm(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_rm(cmdtp, flag, argc, argv, FS_TYPE_FAT);
+}
+
+U_BOOT_CMD(
+ fatrm, 4, 1, do_fat_rm,
+ "delete a file",
+ "<interface> [<dev[:part]>] <filename>\n"
+ " - delete a file from 'dev' on 'interface'"
+);
+
+static int do_fat_mkdir(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_mkdir(cmdtp, flag, argc, argv, FS_TYPE_FAT);
+}
+
+U_BOOT_CMD(
+ fatmkdir, 4, 1, do_fat_mkdir,
+ "create a directory",
+ "<interface> [<dev[:part]>] <directory>\n"
+ " - create a directory in 'dev' on 'interface'"
+);
+#endif
diff --git a/cmd/fdt.c b/cmd/fdt.c
new file mode 100644
index 00000000000..a67c30b21d5
--- /dev/null
+++ b/cmd/fdt.c
@@ -0,0 +1,1159 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2007
+ * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
+ * Based on code written by:
+ * Pantelis Antoniou <pantelis.antoniou@gmail.com> and
+ * Matthew McClintock <msm@freescale.com>
+ */
+
+#include <command.h>
+#include <env.h>
+#include <image.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <asm/global_data.h>
+#include <linux/libfdt.h>
+#include <fdt_support.h>
+#include <mapmem.h>
+#include <asm/io.h>
+
+#define MAX_LEVEL 32 /* how deeply nested we will go */
+#define SCRATCHPAD 1024 /* bytes of scratchpad memory */
+
+/*
+ * Global data (for the gd->bd)
+ */
+DECLARE_GLOBAL_DATA_PTR;
+
+static int fdt_parse_prop(char *const*newval, int count, char *data, int *len);
+static int fdt_print(const char *pathp, char *prop, int depth);
+static int is_printable_string(const void *data, int len);
+
+/*
+ * The working_fdt points to our working flattened device tree.
+ */
+struct fdt_header *working_fdt;
+
+static void set_working_fdt_addr_quiet(ulong addr)
+{
+ void *buf;
+
+ buf = map_sysmem(addr, 0);
+ working_fdt = buf;
+ env_set_hex("fdtaddr", addr);
+}
+
+void set_working_fdt_addr(ulong addr)
+{
+ printf("Working FDT set to %lx\n", addr);
+ set_working_fdt_addr_quiet(addr);
+}
+
+/*
+ * Get a value from the fdt and format it to be set in the environment
+ */
+static int fdt_value_env_set(const void *nodep, int len,
+ const char *var, int index)
+{
+ if (is_printable_string(nodep, len)) {
+ const char *nodec = (const char *)nodep;
+ int i;
+
+ /*
+ * Iterate over all members in stringlist and find the one at
+ * offset $index. If no such index exists, indicate failure.
+ */
+ for (i = 0; i < len; ) {
+ if (index-- > 0) {
+ i += strlen(nodec) + 1;
+ nodec += strlen(nodec) + 1;
+ continue;
+ }
+
+ env_set(var, nodec);
+ return 0;
+ }
+
+ return 1;
+ } else if (len == 4) {
+ char buf[11];
+
+ sprintf(buf, "0x%08X", fdt32_to_cpu(*(fdt32_t *)nodep));
+ env_set(var, buf);
+ } else if (len % 4 == 0 && index >= 0) {
+ /* Needed to print integer arrays. */
+ const unsigned int *nodec = (const unsigned int *)nodep;
+ char buf[11];
+
+ if (index * 4 >= len)
+ return 1;
+
+ sprintf(buf, "0x%08X", fdt32_to_cpu(*(nodec + index)));
+ env_set(var, buf);
+ } else if (len % 4 == 0 && len <= 20) {
+ /* Needed to print things like sha1 hashes. */
+ char buf[41];
+ int i;
+
+ for (i = 0; i < len; i += sizeof(unsigned int))
+ sprintf(buf + (i * 2), "%08x",
+ *(unsigned int *)(nodep + i));
+ env_set(var, buf);
+ } else {
+ printf("error: unprintable value\n");
+ return 1;
+ }
+ return 0;
+}
+
+static const char * const fdt_member_table[] = {
+ "magic",
+ "totalsize",
+ "off_dt_struct",
+ "off_dt_strings",
+ "off_mem_rsvmap",
+ "version",
+ "last_comp_version",
+ "boot_cpuid_phys",
+ "size_dt_strings",
+ "size_dt_struct",
+};
+
+static int fdt_get_header_value(int argc, char *const argv[])
+{
+ fdt32_t *fdtp = (fdt32_t *)working_fdt;
+ ulong val;
+ int i;
+
+ if (argv[2][0] != 'g')
+ return CMD_RET_FAILURE;
+
+ for (i = 0; i < ARRAY_SIZE(fdt_member_table); i++) {
+ if (strcmp(fdt_member_table[i], argv[4]))
+ continue;
+
+ val = fdt32_to_cpu(fdtp[i]);
+ env_set_hex(argv[3], val);
+ return CMD_RET_SUCCESS;
+ }
+
+ return CMD_RET_FAILURE;
+}
+
+/*
+ * Flattened Device Tree command, see the help for parameter definitions.
+ */
+static int do_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* fdt addr: Set the address of the fdt */
+ if (strncmp(argv[1], "ad", 2) == 0) {
+ unsigned long addr;
+ int control = 0;
+ int quiet = 0;
+ struct fdt_header *blob;
+
+ /* Set the address [and length] of the fdt */
+ argc -= 2;
+ argv += 2;
+ while (argc > 0 && **argv == '-') {
+ char *arg = *argv;
+
+ while (*++arg) {
+ switch (*arg) {
+ case 'c':
+ control = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+ argc--;
+ argv++;
+ }
+ if (argc == 0) {
+ if (control)
+ blob = (struct fdt_header *)gd->fdt_blob;
+ else
+ blob = working_fdt;
+ if (!blob || !fdt_valid(&blob))
+ return 1;
+ printf("%s fdt: %08lx\n",
+ control ? "Control" : "Working",
+ control ? (ulong)map_to_sysmem(blob) :
+ env_get_hex("fdtaddr", 0));
+ return 0;
+ }
+
+ addr = hextoul(argv[0], NULL);
+ blob = map_sysmem(addr, 0);
+ if ((quiet && fdt_check_header(blob)) ||
+ (!quiet && !fdt_valid(&blob)))
+ return 1;
+ if (control) {
+ gd->fdt_blob = blob;
+ } else {
+ if (quiet)
+ set_working_fdt_addr_quiet(addr);
+ else
+ set_working_fdt_addr(addr);
+ }
+
+ if (argc >= 2) {
+ int len;
+ int err;
+
+ /* Optional new length */
+ len = hextoul(argv[1], NULL);
+ if (len < fdt_totalsize(blob)) {
+ if (!quiet)
+ printf("New length %d < existing length %d, ignoring\n",
+ len, fdt_totalsize(blob));
+ } else {
+ /* Open in place with a new length */
+ err = fdt_open_into(blob, blob, len);
+ if (!quiet && err != 0) {
+ printf("libfdt fdt_open_into(): %s\n",
+ fdt_strerror(err));
+ }
+ }
+ }
+
+ return CMD_RET_SUCCESS;
+
+ /*
+ * Move the working_fdt
+ */
+ } else if (strncmp(argv[1], "mo", 2) == 0) {
+ struct fdt_header *newaddr;
+ int len;
+ int err;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ /*
+ * Set the address and length of the fdt.
+ */
+ working_fdt = map_sysmem(hextoul(argv[2], NULL), 0);
+ if (!fdt_valid(&working_fdt))
+ return 1;
+
+ newaddr = map_sysmem(hextoul(argv[3], NULL), 0);
+
+ /*
+ * If the user specifies a length, use that. Otherwise use the
+ * current length.
+ */
+ if (argc <= 4) {
+ len = fdt_totalsize(working_fdt);
+ } else {
+ len = hextoul(argv[4], NULL);
+ if (len < fdt_totalsize(working_fdt)) {
+ printf ("New length 0x%X < existing length "
+ "0x%X, aborting.\n",
+ len, fdt_totalsize(working_fdt));
+ return 1;
+ }
+ }
+
+ /*
+ * Copy to the new location.
+ */
+ err = fdt_open_into(working_fdt, newaddr, len);
+ if (err != 0) {
+ printf ("libfdt fdt_open_into(): %s\n",
+ fdt_strerror(err));
+ return 1;
+ }
+ set_working_fdt_addr(map_to_sysmem(newaddr));
+
+ return CMD_RET_SUCCESS;
+ }
+
+ if (!working_fdt) {
+ puts("No FDT memory address configured. Please configure\n"
+ "the FDT address via \"fdt addr <address>\" command.\n"
+ "Aborting!\n");
+ return CMD_RET_FAILURE;
+ }
+
+#ifdef CONFIG_OF_SYSTEM_SETUP
+ /* Call the board-specific fixup routine */
+ if (strncmp(argv[1], "sys", 3) == 0) {
+ int err = ft_system_setup(working_fdt, gd->bd);
+
+ if (err) {
+ printf("Failed to add system information to FDT: %s\n",
+ fdt_strerror(err));
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+ }
+#endif
+ /*
+ * Make a new node
+ */
+ if (strncmp(argv[1], "mk", 2) == 0) {
+ char *pathp; /* path */
+ char *nodep; /* new node to add */
+ int nodeoffset; /* node offset from libfdt */
+ int err;
+
+ /*
+ * Parameters: Node path, new node to be appended to the path.
+ */
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ pathp = argv[2];
+ nodep = argv[3];
+
+ nodeoffset = fdt_path_offset (working_fdt, pathp);
+ if (nodeoffset < 0) {
+ /*
+ * Not found or something else bad happened.
+ */
+ printf ("libfdt fdt_path_offset() returned %s\n",
+ fdt_strerror(nodeoffset));
+ return 1;
+ }
+ err = fdt_add_subnode(working_fdt, nodeoffset, nodep);
+ if (err < 0) {
+ printf ("libfdt fdt_add_subnode(): %s\n",
+ fdt_strerror(err));
+ return 1;
+ }
+
+ /*
+ * Set the value of a property in the working_fdt.
+ */
+ } else if (strncmp(argv[1], "se", 2) == 0) {
+ char *pathp; /* path */
+ char *prop; /* property */
+ int nodeoffset; /* node offset from libfdt */
+ static char data[SCRATCHPAD] __aligned(4);/* property storage */
+ const void *ptmp;
+ int len; /* new length of the property */
+ int ret; /* return value */
+
+ /*
+ * Parameters: Node path, property, optional value.
+ */
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ pathp = argv[2];
+ prop = argv[3];
+
+ nodeoffset = fdt_path_offset (working_fdt, pathp);
+ if (nodeoffset < 0) {
+ /*
+ * Not found or something else bad happened.
+ */
+ printf ("libfdt fdt_path_offset() returned %s\n",
+ fdt_strerror(nodeoffset));
+ return 1;
+ }
+
+ if (argc == 4) {
+ len = 0;
+ } else {
+ ptmp = fdt_getprop(working_fdt, nodeoffset, prop, &len);
+ if (len > SCRATCHPAD) {
+ printf("prop (%d) doesn't fit in scratchpad!\n",
+ len);
+ return 1;
+ }
+ if (ptmp != NULL)
+ memcpy(data, ptmp, len);
+
+ ret = fdt_parse_prop(&argv[4], argc - 4, data, &len);
+ if (ret != 0)
+ return ret;
+ }
+
+ ret = fdt_setprop(working_fdt, nodeoffset, prop, data, len);
+ if (ret < 0) {
+ printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret));
+ return 1;
+ }
+
+ /********************************************************************
+ * Get the value of a property in the working_fdt.
+ ********************************************************************/
+ } else if (argv[1][0] == 'g') {
+ char *subcmd; /* sub-command */
+ char *pathp; /* path */
+ char *prop; /* property */
+ char *var; /* variable to store result */
+ int nodeoffset; /* node offset from libfdt */
+ const void *nodep; /* property node pointer */
+ int len = 0; /* new length of the property */
+
+ /*
+ * Parameters: Node path, property, optional value.
+ */
+ if (argc < 5)
+ return CMD_RET_USAGE;
+
+ subcmd = argv[2];
+
+ if (argc < 6 && subcmd[0] != 's')
+ return CMD_RET_USAGE;
+
+ var = argv[3];
+ pathp = argv[4];
+ prop = argv[5];
+
+ nodeoffset = fdt_path_offset(working_fdt, pathp);
+ if (nodeoffset < 0) {
+ /*
+ * Not found or something else bad happened.
+ */
+ printf("libfdt fdt_path_offset() returned %s\n",
+ fdt_strerror(nodeoffset));
+ return 1;
+ }
+
+ if (subcmd[0] == 'n' || (subcmd[0] == 's' && argc == 5)) {
+ int req_index = -1;
+ int startDepth = fdt_node_depth(
+ working_fdt, nodeoffset);
+ int curDepth = startDepth;
+ int cur_index = -1;
+ int nextNodeOffset = fdt_next_node(
+ working_fdt, nodeoffset, &curDepth);
+
+ if (subcmd[0] == 'n')
+ req_index = hextoul(argv[5], NULL);
+
+ while (curDepth > startDepth) {
+ if (curDepth == startDepth + 1)
+ cur_index++;
+ if (subcmd[0] == 'n' &&
+ cur_index == req_index) {
+ const char *node_name;
+
+ node_name = fdt_get_name(working_fdt,
+ nextNodeOffset,
+ NULL);
+ env_set(var, node_name);
+ return 0;
+ }
+ nextNodeOffset = fdt_next_node(
+ working_fdt, nextNodeOffset, &curDepth);
+ if (nextNodeOffset < 0)
+ break;
+ }
+ if (subcmd[0] == 's') {
+ /* get the num nodes at this level */
+ env_set_ulong(var, cur_index + 1);
+ } else {
+ /* node index not found */
+ printf("libfdt node not found\n");
+ return 1;
+ }
+ } else {
+ nodep = fdt_getprop(
+ working_fdt, nodeoffset, prop, &len);
+ if (nodep && len >= 0) {
+ if (subcmd[0] == 'v') {
+ int index = -1;
+ int ret;
+
+ if (len == 0) {
+ /* no property value */
+ env_set(var, "");
+ return 0;
+ }
+
+ if (argc == 7)
+ index = simple_strtoul(argv[6], NULL, 10);
+
+ ret = fdt_value_env_set(nodep, len,
+ var, index);
+ if (ret != 0)
+ return ret;
+ } else if (subcmd[0] == 'a') {
+ env_set_hex(var, (ulong)map_to_sysmem(nodep));
+ } else if (subcmd[0] == 's') {
+ env_set_hex(var, len);
+ } else
+ return CMD_RET_USAGE;
+ return 0;
+ } else {
+ printf("libfdt fdt_getprop(): %s\n",
+ fdt_strerror(len));
+ return 1;
+ }
+ }
+
+ /*
+ * Print (recursive) / List (single level)
+ */
+ } else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) {
+ int depth = MAX_LEVEL; /* how deep to print */
+ char *pathp; /* path */
+ char *prop; /* property */
+ int ret; /* return value */
+ static char root[2] = "/";
+
+ /*
+ * list is an alias for print, but limited to 1 level
+ */
+ if (argv[1][0] == 'l') {
+ depth = 1;
+ }
+
+ /*
+ * Get the starting path. The root node is an oddball,
+ * the offset is zero and has no name.
+ */
+ if (argc == 2)
+ pathp = root;
+ else
+ pathp = argv[2];
+ if (argc > 3)
+ prop = argv[3];
+ else
+ prop = NULL;
+
+ ret = fdt_print(pathp, prop, depth);
+ if (ret != 0)
+ return ret;
+
+ /*
+ * Remove a property/node
+ */
+ } else if (strncmp(argv[1], "rm", 2) == 0) {
+ int nodeoffset; /* node offset from libfdt */
+ int err;
+
+ /*
+ * Get the path. The root node is an oddball, the offset
+ * is zero and has no name.
+ */
+ nodeoffset = fdt_path_offset (working_fdt, argv[2]);
+ if (nodeoffset < 0) {
+ /*
+ * Not found or something else bad happened.
+ */
+ printf ("libfdt fdt_path_offset() returned %s\n",
+ fdt_strerror(nodeoffset));
+ return 1;
+ }
+ /*
+ * Do the delete. A fourth parameter means delete a property,
+ * otherwise delete the node.
+ */
+ if (argc > 3) {
+ err = fdt_delprop(working_fdt, nodeoffset, argv[3]);
+ if (err < 0) {
+ printf("libfdt fdt_delprop(): %s\n",
+ fdt_strerror(err));
+ return CMD_RET_FAILURE;
+ }
+ } else {
+ err = fdt_del_node(working_fdt, nodeoffset);
+ if (err < 0) {
+ printf("libfdt fdt_del_node(): %s\n",
+ fdt_strerror(err));
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ /*
+ * Display header info
+ */
+ } else if (argv[1][0] == 'h') {
+ if (argc == 5)
+ return fdt_get_header_value(argc, argv);
+
+ u32 version = fdt_version(working_fdt);
+ printf("magic:\t\t\t0x%x\n", fdt_magic(working_fdt));
+ printf("totalsize:\t\t0x%x (%d)\n", fdt_totalsize(working_fdt),
+ fdt_totalsize(working_fdt));
+ printf("off_dt_struct:\t\t0x%x\n",
+ fdt_off_dt_struct(working_fdt));
+ printf("off_dt_strings:\t\t0x%x\n",
+ fdt_off_dt_strings(working_fdt));
+ printf("off_mem_rsvmap:\t\t0x%x\n",
+ fdt_off_mem_rsvmap(working_fdt));
+ printf("version:\t\t%d\n", version);
+ printf("last_comp_version:\t%d\n",
+ fdt_last_comp_version(working_fdt));
+ if (version >= 2)
+ printf("boot_cpuid_phys:\t0x%x\n",
+ fdt_boot_cpuid_phys(working_fdt));
+ if (version >= 3)
+ printf("size_dt_strings:\t0x%x\n",
+ fdt_size_dt_strings(working_fdt));
+ if (version >= 17)
+ printf("size_dt_struct:\t\t0x%x\n",
+ fdt_size_dt_struct(working_fdt));
+ printf("number mem_rsv:\t\t0x%x\n",
+ fdt_num_mem_rsv(working_fdt));
+ printf("\n");
+
+ /*
+ * Set boot cpu id
+ */
+ } else if (strncmp(argv[1], "boo", 3) == 0) {
+ unsigned long tmp;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ tmp = hextoul(argv[2], NULL);
+ fdt_set_boot_cpuid_phys(working_fdt, tmp);
+
+ /*
+ * memory command
+ */
+ } else if (strncmp(argv[1], "me", 2) == 0) {
+ uint64_t addr, size;
+ int err;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ addr = simple_strtoull(argv[2], NULL, 16);
+ size = simple_strtoull(argv[3], NULL, 16);
+ err = fdt_fixup_memory(working_fdt, addr, size);
+ if (err < 0)
+ return err;
+
+ /*
+ * mem reserve commands
+ */
+ } else if (strncmp(argv[1], "rs", 2) == 0) {
+ if (argv[2][0] == 'p') {
+ uint64_t addr, size;
+ int total = fdt_num_mem_rsv(working_fdt);
+ int j, err;
+ printf("index\t\t start\t\t size\n");
+ printf("-------------------------------"
+ "-----------------\n");
+ for (j = 0; j < total; j++) {
+ err = fdt_get_mem_rsv(working_fdt, j, &addr, &size);
+ if (err < 0) {
+ printf("libfdt fdt_get_mem_rsv(): %s\n",
+ fdt_strerror(err));
+ return err;
+ }
+ printf(" %x\t%08x%08x\t%08x%08x\n", j,
+ (u32)(addr >> 32),
+ (u32)(addr & 0xffffffff),
+ (u32)(size >> 32),
+ (u32)(size & 0xffffffff));
+ }
+ } else if (argv[2][0] == 'a') {
+ uint64_t addr, size;
+ int err;
+ addr = simple_strtoull(argv[3], NULL, 16);
+ size = simple_strtoull(argv[4], NULL, 16);
+ err = fdt_add_mem_rsv(working_fdt, addr, size);
+
+ if (err < 0) {
+ printf("libfdt fdt_add_mem_rsv(): %s\n",
+ fdt_strerror(err));
+ return CMD_RET_FAILURE;
+ }
+ } else if (argv[2][0] == 'd') {
+ unsigned long idx = hextoul(argv[3], NULL);
+ int err = fdt_del_mem_rsv(working_fdt, idx);
+
+ if (err < 0) {
+ printf("libfdt fdt_del_mem_rsv(): %s\n",
+ fdt_strerror(err));
+ return CMD_RET_FAILURE;
+ }
+ } else {
+ /* Unrecognized command */
+ return CMD_RET_USAGE;
+ }
+ }
+#ifdef CONFIG_OF_BOARD_SETUP
+ /* Call the board-specific fixup routine */
+ else if (strncmp(argv[1], "boa", 3) == 0) {
+ int err = ft_board_setup(working_fdt, gd->bd);
+
+ if (err) {
+ printf("Failed to update board information in FDT: %s\n",
+ fdt_strerror(err));
+ return CMD_RET_FAILURE;
+ }
+
+ if (IS_ENABLED(CONFIG_OF_BOARD_SETUP_EXTENDED))
+ ft_board_setup_ex(working_fdt, gd->bd);
+ }
+#endif
+ /* Create a chosen node */
+ else if (strncmp(argv[1], "cho", 3) == 0) {
+ unsigned long initrd_start = 0, initrd_end = 0;
+
+ if ((argc != 2) && (argc != 4))
+ return CMD_RET_USAGE;
+
+ if (argc == 4) {
+ initrd_start = hextoul(argv[2], NULL);
+ initrd_end = initrd_start + hextoul(argv[3], NULL) - 1;
+ }
+
+ fdt_chosen(working_fdt);
+ fdt_initrd(working_fdt, initrd_start, initrd_end);
+
+#if defined(CONFIG_FIT_SIGNATURE)
+ } else if (strncmp(argv[1], "che", 3) == 0) {
+ int cfg_noffset;
+ int ret;
+ unsigned long addr;
+ struct fdt_header *blob;
+
+ if (!working_fdt)
+ return CMD_RET_FAILURE;
+
+ if (argc > 2) {
+ addr = hextoul(argv[2], NULL);
+ blob = map_sysmem(addr, 0);
+ } else {
+ blob = (struct fdt_header *)gd->fdt_blob;
+ }
+ if (!fdt_valid(&blob))
+ return 1;
+
+ gd->fdt_blob = blob;
+ cfg_noffset = fit_conf_get_node(working_fdt, NULL);
+ if (cfg_noffset < 0) {
+ printf("Could not find configuration node: %s\n",
+ fdt_strerror(cfg_noffset));
+ return CMD_RET_FAILURE;
+ }
+
+ ret = fit_config_verify(working_fdt, cfg_noffset);
+ if (ret == 0)
+ return CMD_RET_SUCCESS;
+ else
+ return CMD_RET_FAILURE;
+#endif
+
+ }
+#ifdef CONFIG_OF_LIBFDT_OVERLAY
+ /* apply an overlay */
+ else if (strncmp(argv[1], "ap", 2) == 0) {
+ unsigned long addr;
+ struct fdt_header *blob;
+ int ret;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ if (!working_fdt)
+ return CMD_RET_FAILURE;
+
+ addr = hextoul(argv[2], NULL);
+ blob = map_sysmem(addr, 0);
+ if (!fdt_valid(&blob))
+ return CMD_RET_FAILURE;
+
+ /* apply method prints messages on error */
+ ret = fdt_overlay_apply_verbose(working_fdt, blob);
+ if (ret)
+ return CMD_RET_FAILURE;
+ }
+#endif
+ /* resize the fdt */
+ else if (strncmp(argv[1], "re", 2) == 0) {
+ uint extrasize;
+ if (argc > 2)
+ extrasize = hextoul(argv[2], NULL);
+ else
+ extrasize = 0;
+ fdt_shrink_to_minimum(working_fdt, extrasize);
+ }
+ else {
+ /* Unrecognized command */
+ return CMD_RET_USAGE;
+ }
+
+ return 0;
+}
+
+/****************************************************************************/
+
+/*
+ * Parse the user's input, partially heuristic. Valid formats:
+ * <0x00112233 4 05> - an array of cells. Numbers follow standard
+ * C conventions.
+ * [00 11 22 .. nn] - byte stream
+ * "string" - If the the value doesn't start with "<" or "[", it is
+ * treated as a string. Note that the quotes are
+ * stripped by the parser before we get the string.
+ * newval: An array of strings containing the new property as specified
+ * on the command line
+ * count: The number of strings in the array
+ * data: A bytestream to be placed in the property
+ * len: The length of the resulting bytestream
+ */
+static int fdt_parse_prop(char * const *newval, int count, char *data, int *len)
+{
+ char *cp; /* temporary char pointer */
+ char *newp; /* temporary newval char pointer */
+ unsigned long tmp; /* holds converted values */
+ int stridx = 0;
+
+ *len = 0;
+ newp = newval[0];
+
+ /* An array of cells */
+ if (*newp == '<') {
+ newp++;
+ while ((*newp != '>') && (stridx < count)) {
+ /*
+ * Keep searching until we find that last ">"
+ * That way users don't have to escape the spaces
+ */
+ if (*newp == '\0') {
+ newp = newval[++stridx];
+ continue;
+ }
+
+ cp = newp;
+ tmp = simple_strtoul(cp, &newp, 0);
+ if (*cp != '?')
+ *(fdt32_t *)data = cpu_to_fdt32(tmp);
+ else
+ newp++;
+
+ data += 4;
+ *len += 4;
+
+ /* If the ptr didn't advance, something went wrong */
+ if ((newp - cp) <= 0) {
+ printf("Sorry, I could not convert \"%s\"\n",
+ cp);
+ return 1;
+ }
+
+ while (*newp == ' ')
+ newp++;
+ }
+
+ if (*newp != '>') {
+ printf("Unexpected character '%c'\n", *newp);
+ return 1;
+ }
+ } else if (*newp == '[') {
+ /*
+ * Byte stream. Convert the values.
+ */
+ newp++;
+ while ((stridx < count) && (*newp != ']')) {
+ while (*newp == ' ')
+ newp++;
+ if (*newp == '\0') {
+ newp = newval[++stridx];
+ continue;
+ }
+ if (!isxdigit(*newp))
+ break;
+ tmp = hextoul(newp, &newp);
+ *data++ = tmp & 0xFF;
+ *len = *len + 1;
+ }
+ if (*newp != ']') {
+ printf("Unexpected character '%c'\n", *newp);
+ return 1;
+ }
+ } else {
+ /*
+ * Assume it is one or more strings. Copy it into our
+ * data area for convenience (including the
+ * terminating '\0's).
+ */
+ while (stridx < count) {
+ size_t length = strlen(newp) + 1;
+ strcpy(data, newp);
+ data += length;
+ *len += length;
+ newp = newval[++stridx];
+ }
+ }
+ return 0;
+}
+
+/****************************************************************************/
+
+/*
+ * Heuristic to guess if this is a string or concatenated strings.
+ */
+
+static int is_printable_string(const void *data, int len)
+{
+ const char *s = data;
+ const char *ss, *se;
+
+ /* zero length is not */
+ if (len == 0)
+ return 0;
+
+ /* must terminate with zero */
+ if (s[len - 1] != '\0')
+ return 0;
+
+ se = s + len;
+
+ while (s < se) {
+ ss = s;
+ while (s < se && *s && isprint((unsigned char)*s))
+ s++;
+
+ /* not zero, or not done yet */
+ if (*s != '\0' || s == ss)
+ return 0;
+
+ s++;
+ }
+
+ return 1;
+}
+
+/*
+ * Print the property in the best format, a heuristic guess. Print as
+ * a string, concatenated strings, a byte, word, double word, or (if all
+ * else fails) it is printed as a stream of bytes.
+ */
+static void print_data(const void *data, int len)
+{
+ int j;
+ const char *env_max_dump;
+ ulong max_dump = ULONG_MAX;
+
+ /* no data, don't print */
+ if (len == 0)
+ return;
+
+ env_max_dump = env_get("fdt_max_dump");
+ if (env_max_dump)
+ max_dump = hextoul(env_max_dump, NULL);
+
+ /*
+ * It is a string, but it may have multiple strings (embedded '\0's).
+ */
+ if (is_printable_string(data, len)) {
+ puts("\"");
+ j = 0;
+ while (j < len) {
+ if (j > 0)
+ puts("\", \"");
+ puts(data);
+ j += strlen(data) + 1;
+ data += strlen(data) + 1;
+ }
+ puts("\"");
+ return;
+ }
+
+ if ((len %4) == 0) {
+ if (len > max_dump)
+ printf("* 0x%p [0x%08x]", data, len);
+ else {
+ const __be32 *p;
+
+ printf("<");
+ for (j = 0, p = data; j < len/4; j++)
+ printf("0x%08x%s", fdt32_to_cpu(p[j]),
+ j < (len/4 - 1) ? " " : "");
+ printf(">");
+ }
+ } else { /* anything else... hexdump */
+ if (len > max_dump)
+ printf("* 0x%p [0x%08x]", data, len);
+ else {
+ const u8 *s;
+
+ printf("[");
+ for (j = 0, s = data; j < len; j++)
+ printf("%02x%s", s[j], j < len - 1 ? " " : "");
+ printf("]");
+ }
+ }
+}
+
+/****************************************************************************/
+
+/*
+ * Recursively print (a portion of) the working_fdt. The depth parameter
+ * determines how deeply nested the fdt is printed.
+ */
+static int fdt_print(const char *pathp, char *prop, int depth)
+{
+ static char tabs[MAX_LEVEL+1] =
+ "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
+ "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+ const void *nodep; /* property node pointer */
+ int nodeoffset; /* node offset from libfdt */
+ int nextoffset; /* next node offset from libfdt */
+ uint32_t tag; /* tag */
+ int len; /* length of the property */
+ int level = 0; /* keep track of nesting level */
+ const struct fdt_property *fdt_prop;
+
+ nodeoffset = fdt_path_offset (working_fdt, pathp);
+ if (nodeoffset < 0) {
+ /*
+ * Not found or something else bad happened.
+ */
+ printf ("libfdt fdt_path_offset() returned %s\n",
+ fdt_strerror(nodeoffset));
+ return 1;
+ }
+ /*
+ * The user passed in a property as well as node path.
+ * Print only the given property and then return.
+ */
+ if (prop) {
+ nodep = fdt_getprop (working_fdt, nodeoffset, prop, &len);
+ if (len == 0) {
+ /* no property value */
+ printf("%s %s\n", pathp, prop);
+ return 0;
+ } else if (nodep && len > 0) {
+ printf("%s = ", prop);
+ print_data (nodep, len);
+ printf("\n");
+ return 0;
+ } else {
+ printf ("libfdt fdt_getprop(): %s\n",
+ fdt_strerror(len));
+ return 1;
+ }
+ }
+
+ /*
+ * The user passed in a node path and no property,
+ * print the node and all subnodes.
+ */
+ while(level >= 0) {
+ tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset);
+ switch(tag) {
+ case FDT_BEGIN_NODE:
+ pathp = fdt_get_name(working_fdt, nodeoffset, NULL);
+ if (level <= depth) {
+ if (pathp == NULL)
+ pathp = "/* NULL pointer error */";
+ if (*pathp == '\0')
+ pathp = "/"; /* root is nameless */
+ printf("%s%s {\n",
+ &tabs[MAX_LEVEL - level], pathp);
+ }
+ level++;
+ if (level >= MAX_LEVEL) {
+ printf("Nested too deep, aborting.\n");
+ return 1;
+ }
+ break;
+ case FDT_END_NODE:
+ level--;
+ if (level <= depth)
+ printf("%s};\n", &tabs[MAX_LEVEL - level]);
+ if (level == 0) {
+ level = -1; /* exit the loop */
+ }
+ break;
+ case FDT_PROP:
+ fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset,
+ sizeof(*fdt_prop));
+ pathp = fdt_string(working_fdt,
+ fdt32_to_cpu(fdt_prop->nameoff));
+ len = fdt32_to_cpu(fdt_prop->len);
+ nodep = fdt_prop->data;
+ if (len < 0) {
+ printf ("libfdt fdt_getprop(): %s\n",
+ fdt_strerror(len));
+ return 1;
+ } else if (len == 0) {
+ /* the property has no value */
+ if (level <= depth)
+ printf("%s%s;\n",
+ &tabs[MAX_LEVEL - level],
+ pathp);
+ } else {
+ if (level <= depth) {
+ printf("%s%s = ",
+ &tabs[MAX_LEVEL - level],
+ pathp);
+ print_data (nodep, len);
+ printf(";\n");
+ }
+ }
+ break;
+ case FDT_NOP:
+ printf("%s/* NOP */\n", &tabs[MAX_LEVEL - level]);
+ break;
+ case FDT_END:
+ return 1;
+ default:
+ if (level <= depth)
+ printf("Unknown tag 0x%08X\n", tag);
+ return 1;
+ }
+ nodeoffset = nextoffset;
+ }
+ return 0;
+}
+
+/********************************************************************/
+U_BOOT_LONGHELP(fdt,
+ "addr [-c] [-q] <addr> [<size>] - Set the [control] fdt location to <addr>\n"
+#ifdef CONFIG_OF_LIBFDT_OVERLAY
+ "fdt apply <addr> - Apply overlay to the DT\n"
+#endif
+#ifdef CONFIG_OF_BOARD_SETUP
+ "fdt boardsetup - Do board-specific set up\n"
+#endif
+#ifdef CONFIG_OF_SYSTEM_SETUP
+ "fdt systemsetup - Do system-specific set up\n"
+#endif
+ "fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n"
+ "fdt resize [<extrasize>] - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed\n"
+ "fdt print <path> [<prop>] - Recursive print starting at <path>\n"
+ "fdt list <path> [<prop>] - Print one level starting at <path>\n"
+ "fdt get value <var> <path> <prop> [<index>] - Get <property> and store in <var>\n"
+ " In case of stringlist property, use optional <index>\n"
+ " to select string within the stringlist. Default is 0.\n"
+ "fdt get name <var> <path> <index> - Get name of node <index> and store in <var>\n"
+ "fdt get addr <var> <path> <prop> - Get start address of <property> and store in <var>\n"
+ "fdt get size <var> <path> [<prop>] - Get size of [<property>] or num nodes and store in <var>\n"
+ "fdt set <path> <prop> [<val>] - Set <property> [to <val>]\n"
+ "fdt mknode <path> <node> - Create a new node after <path>\n"
+ "fdt rm <path> [<prop>] - Delete the node or <property>\n"
+ "fdt header [get <var> <member>] - Display header info\n"
+ " get - get header member <member> and store it in <var>\n"
+ "fdt bootcpu <id> - Set boot cpuid\n"
+ "fdt memory <addr> <size> - Add/Update memory node\n"
+ "fdt rsvmem print - Show current mem reserves\n"
+ "fdt rsvmem add <addr> <size> - Add a mem reserve\n"
+ "fdt rsvmem delete <index> - Delete a mem reserves\n"
+ "fdt chosen [<start> <size>] - Add/update the /chosen branch in the tree\n"
+ " <start>/<size> - initrd start addr/size\n"
+#if defined(CONFIG_FIT_SIGNATURE)
+ "fdt checksign [<addr>] - check FIT signature\n"
+ " <addr> - address of key blob\n"
+ " default gd->fdt_blob\n"
+#endif
+ "NOTE: Dereference aliases by omitting the leading '/', "
+ "e.g. fdt print ethernet0.");
+
+U_BOOT_CMD(
+ fdt, 255, 0, do_fdt,
+ "flattened device tree utility commands", fdt_help_text
+);
diff --git a/cmd/flash.c b/cmd/flash.c
new file mode 100644
index 00000000000..76aa387ba59
--- /dev/null
+++ b/cmd/flash.c
@@ -0,0 +1,700 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+/*
+ * FLASH support
+ */
+#include <command.h>
+#include <log.h>
+#include <vsprintf.h>
+#include <linux/string.h>
+#include <u-boot/uuid.h>
+
+#if defined(CONFIG_CMD_MTDPARTS)
+#include <jffs2/jffs2.h>
+
+/* partition handling routines */
+int mtdparts_init(void);
+int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
+int find_dev_and_part(const char *id, struct mtd_device **dev,
+ u8 *part_num, struct part_info **part);
+#endif
+
+#ifdef CONFIG_MTD_NOR_FLASH
+#include <flash.h>
+#include <mtd/cfi_flash.h>
+
+/*
+ * The user interface starts numbering for Flash banks with 1
+ * for historical reasons.
+ */
+
+/*
+ * this routine looks for an abbreviated flash range specification.
+ * the syntax is B:SF[-SL], where B is the bank number, SF is the first
+ * sector to erase, and SL is the last sector to erase (defaults to SF).
+ * bank numbers start at 1 to be consistent with other specs, sector numbers
+ * start at zero.
+ *
+ * returns: 1 - correct spec; *pinfo, *psf and *psl are
+ * set appropriately
+ * 0 - doesn't look like an abbreviated spec
+ * -1 - looks like an abbreviated spec, but got
+ * a parsing error, a number out of range,
+ * or an invalid flash bank.
+ */
+static int
+abbrev_spec(char *str, flash_info_t **pinfo, int *psf, int *psl)
+{
+ flash_info_t *fp;
+ int bank, first, last;
+ char *p, *ep;
+
+ p = strchr(str, ':');
+ if (!p)
+ return 0;
+ *p++ = '\0';
+
+ bank = dectoul(str, &ep);
+ if (ep == str || *ep != '\0' ||
+ bank < 1 || bank > CFI_FLASH_BANKS)
+ return -1;
+
+ fp = &flash_info[bank - 1];
+ if (fp->flash_id == FLASH_UNKNOWN)
+ return -1;
+
+ str = p;
+ p = strchr(str, '-');
+ if (p)
+ *p++ = '\0';
+
+ first = dectoul(str, &ep);
+ if (ep == str || *ep != '\0' || first >= fp->sector_count)
+ return -1;
+
+ if (p) {
+ last = dectoul(p, &ep);
+ if (ep == p || *ep != '\0' ||
+ last < first || last >= fp->sector_count)
+ return -1;
+ } else {
+ last = first;
+ }
+
+ *pinfo = fp;
+ *psf = first;
+ *psl = last;
+
+ return 1;
+}
+
+/*
+ * Take *addr in Flash and adjust it to fall on the end of its sector
+ */
+int flash_sect_roundb(ulong *addr)
+{
+ flash_info_t *info;
+ ulong bank, sector_end_addr;
+ char found;
+ int i;
+
+ /* find the end addr of the sector where the *addr is */
+ found = 0;
+ for (bank = 0; bank < CFI_FLASH_BANKS && !found; ++bank) {
+ info = &flash_info[bank];
+ for (i = 0; i < info->sector_count && !found; ++i) {
+ /* get the end address of the sector */
+ if (i == info->sector_count - 1) {
+ sector_end_addr = info->start[0] +
+ info->size - 1;
+ } else {
+ sector_end_addr = info->start[i + 1] - 1;
+ }
+
+ if (*addr <= sector_end_addr && *addr >= info->start[i]) {
+ found = 1;
+ /* adjust *addr if necessary */
+ if (*addr < sector_end_addr)
+ *addr = sector_end_addr;
+ } /* sector */
+ } /* bank */
+ }
+ if (!found) {
+ /* error, address not in flash */
+ printf("Error: end address (0x%08lx) not in flash!\n", *addr);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * This function computes the start and end addresses for both
+ * erase and protect commands. The range of the addresses on which
+ * either of the commands is to operate can be given in two forms:
+ * 1. <cmd> start end - operate on <'start', 'end')
+ * 2. <cmd> start +length - operate on <'start', start + length)
+ * If the second form is used and the end address doesn't fall on the
+ * sector boundary, than it will be adjusted to the next sector boundary.
+ * If it isn't in the flash, the function will fail (return -1).
+ * Input:
+ * arg1, arg2: address specification (i.e. both command arguments)
+ * Output:
+ * addr_first, addr_last: computed address range
+ * Return:
+ * 1: success
+ * -1: failure (bad format, bad address).
+ */
+static int
+addr_spec(char *arg1, char *arg2, ulong *addr_first, ulong *addr_last)
+{
+ char *ep;
+ char len_used; /* indicates if the "start +length" form used */
+
+ *addr_first = hextoul(arg1, &ep);
+ if (ep == arg1 || *ep != '\0')
+ return -1;
+
+ len_used = 0;
+ if (arg2 && *arg2 == '+') {
+ len_used = 1;
+ ++arg2;
+ }
+
+ *addr_last = hextoul(arg2, &ep);
+ if (ep == arg2 || *ep != '\0')
+ return -1;
+
+ if (len_used) {
+ /*
+ * *addr_last has the length, compute correct *addr_last
+ * XXX watch out for the integer overflow! Right now it is
+ * checked for in both the callers.
+ */
+ *addr_last = *addr_first + *addr_last - 1;
+
+ /*
+ * It may happen that *addr_last doesn't fall on the sector
+ * boundary. We want to round such an address to the next
+ * sector boundary, so that the commands don't fail later on.
+ */
+
+ if (flash_sect_roundb(addr_last) > 0)
+ return -1;
+ } /* "start +length" from used */
+
+ return 1;
+}
+
+static int
+flash_fill_sect_ranges(ulong addr_first, ulong addr_last,
+ int *s_first, int *s_last,
+ int *s_count)
+{
+ flash_info_t *info;
+ ulong bank;
+ int rcode = 0;
+
+ *s_count = 0;
+
+ for (bank = 0; bank < CFI_FLASH_BANKS; ++bank) {
+ s_first[bank] = -1; /* first sector to erase */
+ s_last[bank] = -1; /* last sector to erase */
+ }
+
+ for (bank = 0, info = &flash_info[0];
+ (bank < CFI_FLASH_BANKS) && (addr_first <= addr_last);
+ ++bank, ++info) {
+ ulong b_end;
+ int sect;
+ short s_end;
+
+ if (info->flash_id == FLASH_UNKNOWN)
+ continue;
+
+ b_end = info->start[0] + info->size - 1; /* bank end addr */
+ s_end = info->sector_count - 1; /* last sector */
+
+ for (sect = 0; sect < info->sector_count; ++sect) {
+ ulong end; /* last address in current sect */
+
+ end = (sect == s_end) ? b_end : info->start[sect + 1] - 1;
+
+ if (addr_first > end)
+ continue;
+ if (addr_last < info->start[sect])
+ continue;
+
+ if (addr_first == info->start[sect])
+ s_first[bank] = sect;
+
+ if (addr_last == end)
+ s_last[bank] = sect;
+ }
+ if (s_first[bank] >= 0) {
+ if (s_last[bank] < 0) {
+ if (addr_last > b_end) {
+ s_last[bank] = s_end;
+ } else {
+ puts("Error: end address not on sector boundary\n");
+ rcode = 1;
+ break;
+ }
+ }
+ if (s_last[bank] < s_first[bank]) {
+ puts("Error: end sector precedes start sector\n");
+ rcode = 1;
+ break;
+ }
+ sect = s_last[bank];
+ addr_first = (sect == s_end) ? b_end + 1 : info->start[sect + 1];
+ (*s_count) += s_last[bank] - s_first[bank] + 1;
+ } else if (addr_first >= info->start[0] && addr_first < b_end) {
+ puts("Error: start address not on sector boundary\n");
+ rcode = 1;
+ break;
+ } else if (s_last[bank] >= 0) {
+ puts("Error: cannot span across banks when they are"
+ " mapped in reverse order\n");
+ rcode = 1;
+ break;
+ }
+ }
+
+ return rcode;
+}
+#endif /* CONFIG_MTD_NOR_FLASH */
+
+static int do_flinfo(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+#ifdef CONFIG_MTD_NOR_FLASH
+ ulong bank;
+#endif
+
+#ifdef CONFIG_MTD_NOR_FLASH
+ if (argc == 1) { /* print info for all FLASH banks */
+ for (bank = 0; bank < CFI_FLASH_BANKS; ++bank) {
+ printf("\nBank # %ld: ", bank + 1);
+
+ flash_print_info(&flash_info[bank]);
+ }
+ return 0;
+ }
+
+ bank = hextoul(argv[1], NULL);
+ if (bank < 1 || bank > CFI_FLASH_BANKS) {
+ printf("Only FLASH Banks # 1 ... # %d supported\n",
+ CFI_FLASH_BANKS);
+ return 1;
+ }
+ printf("\nBank # %ld: ", bank);
+ flash_print_info(&flash_info[bank - 1]);
+#endif /* CONFIG_MTD_NOR_FLASH */
+ return 0;
+}
+
+static int do_flerase(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+#ifdef CONFIG_MTD_NOR_FLASH
+ flash_info_t *info = NULL;
+ ulong bank, addr_first, addr_last;
+ int n, sect_first = 0, sect_last = 0;
+#if defined(CONFIG_CMD_MTDPARTS)
+ struct mtd_device *dev;
+ struct part_info *part;
+ u8 dev_type, dev_num, pnum;
+#endif
+ int rcode = 0;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (strcmp(argv[1], "all") == 0) {
+ for (bank = 1; bank <= CFI_FLASH_BANKS; ++bank) {
+ printf("Erase Flash Bank # %ld ", bank);
+ info = &flash_info[bank - 1];
+ rcode = flash_erase(info, 0, info->sector_count - 1);
+ }
+ return rcode;
+ }
+
+ n = abbrev_spec(argv[1], &info, &sect_first, &sect_last);
+ if (n) {
+ if (n < 0) {
+ puts("Bad sector specification\n");
+ return 1;
+ }
+ printf("Erase Flash Sectors %d-%d in Bank # %zu ",
+ sect_first, sect_last, (info - flash_info) + 1);
+ rcode = flash_erase(info, sect_first, sect_last);
+ return rcode;
+ }
+
+#if defined(CONFIG_CMD_MTDPARTS)
+ /* erase <part-id> - erase partition */
+ if (argc == 2 && mtd_id_parse(argv[1], NULL, &dev_type, &dev_num) == 0) {
+ mtdparts_init();
+ if (find_dev_and_part(argv[1], &dev, &pnum, &part) == 0) {
+ if (dev->id->type == MTD_DEV_TYPE_NOR) {
+ bank = dev->id->num;
+ info = &flash_info[bank];
+ addr_first = part->offset + info->start[0];
+ addr_last = addr_first + part->size - 1;
+
+ printf("Erase Flash Partition %s, bank %ld, 0x%08lx - 0x%08lx ",
+ argv[1], bank, addr_first,
+ addr_last);
+
+ rcode = flash_sect_erase(addr_first, addr_last);
+ return rcode;
+ }
+
+ printf("cannot erase, not a NOR device\n");
+ return 1;
+ }
+ }
+#endif
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ if (strcmp(argv[1], "bank") == 0) {
+ bank = hextoul(argv[2], NULL);
+ if (bank < 1 || bank > CFI_FLASH_BANKS) {
+ printf("Only FLASH Banks # 1 ... # %d supported\n",
+ CFI_FLASH_BANKS);
+ return 1;
+ }
+ printf("Erase Flash Bank # %ld ", bank);
+ info = &flash_info[bank - 1];
+ rcode = flash_erase(info, 0, info->sector_count - 1);
+ return rcode;
+ }
+
+ if (addr_spec(argv[1], argv[2], &addr_first, &addr_last) < 0) {
+ printf("Bad address format\n");
+ return 1;
+ }
+
+ if (addr_first >= addr_last)
+ return CMD_RET_USAGE;
+
+ rcode = flash_sect_erase(addr_first, addr_last);
+ return rcode;
+#else
+ return 0;
+#endif /* CONFIG_MTD_NOR_FLASH */
+}
+
+#ifdef CONFIG_MTD_NOR_FLASH
+int flash_sect_erase(ulong addr_first, ulong addr_last)
+{
+ flash_info_t *info;
+ ulong bank;
+ int s_first[CFI_FLASH_BANKS], s_last[CFI_FLASH_BANKS];
+ int erased = 0;
+ int planned;
+ int rcode = 0;
+
+ rcode = flash_fill_sect_ranges(addr_first, addr_last, s_first, s_last, &planned);
+
+ if (planned && rcode == 0) {
+ for (bank = 0, info = &flash_info[0];
+ bank < CFI_FLASH_BANKS && rcode == 0;
+ ++bank, ++info) {
+ if (s_first[bank] >= 0) {
+ erased += s_last[bank] - s_first[bank] + 1;
+ debug("Erase Flash from 0x%08lx to 0x%08lx in Bank # %ld ",
+ info->start[s_first[bank]],
+ (s_last[bank] == info->sector_count) ?
+ info->start[0] + info->size - 1 :
+ info->start[s_last[bank] + 1] - 1,
+ bank + 1);
+ rcode = flash_erase(info, s_first[bank],
+ s_last[bank]);
+ }
+ }
+ if (rcode == 0)
+ printf("Erased %d sectors\n", erased);
+ } else if (rcode == 0) {
+ puts("Error: start and/or end address not on sector boundary\n");
+ rcode = 1;
+ }
+ return rcode;
+}
+#endif /* CONFIG_MTD_NOR_FLASH */
+
+static int do_protect(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int rcode = 0;
+#ifdef CONFIG_MTD_NOR_FLASH
+ flash_info_t *info = NULL;
+ ulong bank;
+ int i, n, sect_first = 0, sect_last = 0;
+#if defined(CONFIG_CMD_MTDPARTS)
+ struct mtd_device *dev;
+ struct part_info *part;
+ u8 dev_type, dev_num, pnum;
+#endif
+#endif /* CONFIG_MTD_NOR_FLASH */
+#if defined(CONFIG_MTD_NOR_FLASH)
+ int p;
+ ulong addr_first, addr_last;
+#endif
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+#if defined(CONFIG_MTD_NOR_FLASH)
+ if (strcmp(argv[1], "off") == 0)
+ p = 0;
+ else if (strcmp(argv[1], "on") == 0)
+ p = 1;
+ else
+ return CMD_RET_USAGE;
+#endif
+
+#ifdef CONFIG_MTD_NOR_FLASH
+ if (strcmp(argv[2], "all") == 0) {
+ for (bank = 1; bank <= CFI_FLASH_BANKS; ++bank) {
+ info = &flash_info[bank - 1];
+ if (info->flash_id == FLASH_UNKNOWN)
+ continue;
+
+ printf("%sProtect Flash Bank # %ld\n",
+ p ? "" : "Un-", bank);
+
+ for (i = 0; i < info->sector_count; ++i) {
+#if defined(CONFIG_SYS_FLASH_PROTECTION)
+ if (flash_real_protect(info, i, p))
+ rcode = 1;
+ putc('.');
+#else
+ info->protect[i] = p;
+#endif /* CONFIG_SYS_FLASH_PROTECTION */
+ }
+#if defined(CONFIG_SYS_FLASH_PROTECTION)
+ if (!rcode)
+ puts(" done\n");
+#endif /* CONFIG_SYS_FLASH_PROTECTION */
+ }
+ return rcode;
+ }
+ n = abbrev_spec(argv[2], &info, &sect_first, &sect_last);
+ if (n) {
+ if (n < 0) {
+ puts("Bad sector specification\n");
+ return 1;
+ }
+ printf("%sProtect Flash Sectors %d-%d in Bank # %zu\n",
+ p ? "" : "Un-", sect_first, sect_last,
+ (info - flash_info) + 1);
+ for (i = sect_first; i <= sect_last; i++) {
+#if defined(CONFIG_SYS_FLASH_PROTECTION)
+ if (flash_real_protect(info, i, p))
+ rcode = 1;
+ putc('.');
+#else
+ info->protect[i] = p;
+#endif /* CONFIG_SYS_FLASH_PROTECTION */
+ }
+
+#if defined(CONFIG_SYS_FLASH_PROTECTION)
+ if (!rcode)
+ puts(" done\n");
+#endif /* CONFIG_SYS_FLASH_PROTECTION */
+
+ return rcode;
+ }
+
+#if defined(CONFIG_CMD_MTDPARTS)
+ /* protect on/off <part-id> */
+ if (argc == 3 && mtd_id_parse(argv[2], NULL, &dev_type, &dev_num) == 0) {
+ mtdparts_init();
+ if (find_dev_and_part(argv[2], &dev, &pnum, &part) == 0) {
+ if (dev->id->type == MTD_DEV_TYPE_NOR) {
+ bank = dev->id->num;
+ info = &flash_info[bank];
+ addr_first = part->offset + info->start[0];
+ addr_last = addr_first + part->size - 1;
+
+ printf("%sProtect Flash Partition %s, "
+ "bank %ld, 0x%08lx - 0x%08lx\n",
+ p ? "" : "Un", argv[1],
+ bank, addr_first, addr_last);
+
+ rcode = flash_sect_protect(p, addr_first,
+ addr_last);
+ return rcode;
+ }
+
+ printf("cannot %sprotect, not a NOR device\n",
+ p ? "" : "un");
+ return 1;
+ }
+ }
+#endif
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ if (strcmp(argv[2], "bank") == 0) {
+ bank = hextoul(argv[3], NULL);
+ if (bank < 1 || bank > CFI_FLASH_BANKS) {
+ printf("Only FLASH Banks # 1 ... # %d supported\n",
+ CFI_FLASH_BANKS);
+ return 1;
+ }
+ printf("%sProtect Flash Bank # %ld\n",
+ p ? "" : "Un-", bank);
+ info = &flash_info[bank - 1];
+
+ if (info->flash_id == FLASH_UNKNOWN) {
+ puts("missing or unknown FLASH type\n");
+ return 1;
+ }
+ for (i = 0; i < info->sector_count; ++i) {
+#if defined(CONFIG_SYS_FLASH_PROTECTION)
+ if (flash_real_protect(info, i, p))
+ rcode = 1;
+ putc('.');
+#else
+ info->protect[i] = p;
+#endif /* CONFIG_SYS_FLASH_PROTECTION */
+ }
+
+#if defined(CONFIG_SYS_FLASH_PROTECTION)
+ if (!rcode)
+ puts(" done\n");
+#endif /* CONFIG_SYS_FLASH_PROTECTION */
+
+ return rcode;
+ }
+
+ if (addr_spec(argv[2], argv[3], &addr_first, &addr_last) < 0) {
+ printf("Bad address format\n");
+ return 1;
+ }
+
+ if (addr_first >= addr_last)
+ return CMD_RET_USAGE;
+
+ rcode = flash_sect_protect(p, addr_first, addr_last);
+#endif /* CONFIG_MTD_NOR_FLASH */
+ return rcode;
+}
+
+#ifdef CONFIG_MTD_NOR_FLASH
+int flash_sect_protect(int p, ulong addr_first, ulong addr_last)
+{
+ flash_info_t *info;
+ ulong bank;
+ int s_first[CFI_FLASH_BANKS], s_last[CFI_FLASH_BANKS];
+ int protected, i;
+ int planned;
+ int rcode;
+
+ rcode = flash_fill_sect_ranges(addr_first, addr_last, s_first, s_last, &planned);
+
+ protected = 0;
+
+ if (planned && rcode == 0) {
+ for (bank = 0, info = &flash_info[0];
+ bank < CFI_FLASH_BANKS; ++bank, ++info) {
+ if (info->flash_id == FLASH_UNKNOWN)
+ continue;
+
+ if (s_first[bank] >= 0 && s_first[bank] <= s_last[bank]) {
+ debug("%sProtecting sectors %d..%d in bank %ld\n",
+ p ? "" : "Un-", s_first[bank],
+ s_last[bank], bank + 1);
+ protected += s_last[bank] - s_first[bank] + 1;
+ for (i = s_first[bank]; i <= s_last[bank]; ++i) {
+#if defined(CONFIG_SYS_FLASH_PROTECTION)
+ if (flash_real_protect(info, i, p))
+ rcode = 1;
+ putc('.');
+#else
+ info->protect[i] = p;
+#endif /* CONFIG_SYS_FLASH_PROTECTION */
+ }
+ }
+ }
+#if defined(CONFIG_SYS_FLASH_PROTECTION)
+ puts(" done\n");
+#endif /* CONFIG_SYS_FLASH_PROTECTION */
+
+ printf("%sProtected %d sectors\n",
+ p ? "" : "Un-", protected);
+ } else if (rcode == 0) {
+ puts("Error: start and/or end address not on sector boundary\n");
+ rcode = 1;
+ }
+ return rcode;
+}
+#endif /* CONFIG_MTD_NOR_FLASH */
+
+/**************************************************/
+#if defined(CONFIG_CMD_MTDPARTS)
+# define TMP_ERASE "erase <part-id>\n - erase partition\n"
+# define TMP_PROT_ON "protect on <part-id>\n - protect partition\n"
+# define TMP_PROT_OFF "protect off <part-id>\n - make partition writable\n"
+#else
+# define TMP_ERASE /* empty */
+# define TMP_PROT_ON /* empty */
+# define TMP_PROT_OFF /* empty */
+#endif
+
+U_BOOT_CMD(
+ flinfo, 2, 1, do_flinfo,
+ "print FLASH memory information",
+ "\n - print information for all FLASH memory banks\n"
+ "flinfo N\n - print information for FLASH memory bank # N"
+);
+
+U_BOOT_CMD(
+ erase, 3, 0, do_flerase,
+ "erase FLASH memory",
+ "start end\n"
+ " - erase FLASH from addr 'start' to addr 'end'\n"
+ "erase start +len\n"
+ " - erase FLASH from addr 'start' to the end of sect w/addr 'start'+'len'-1\n"
+ "erase N:SF[-SL]\n - erase sectors SF-SL in FLASH bank # N\n"
+ "erase bank N\n - erase FLASH bank # N\n"
+ TMP_ERASE
+ "erase all\n - erase all FLASH banks"
+);
+
+U_BOOT_CMD(
+ protect, 4, 0, do_protect,
+ "enable or disable FLASH write protection",
+ "on start end\n"
+ " - protect FLASH from addr 'start' to addr 'end'\n"
+ "protect on start +len\n"
+ " - protect FLASH from addr 'start' to end of sect w/addr 'start'+'len'-1\n"
+ "protect on N:SF[-SL]\n"
+ " - protect sectors SF-SL in FLASH bank # N\n"
+ "protect on bank N\n - protect FLASH bank # N\n"
+ TMP_PROT_ON
+ "protect on all\n - protect all FLASH banks\n"
+ "protect off start end\n"
+ " - make FLASH from addr 'start' to addr 'end' writable\n"
+ "protect off start +len\n"
+ " - make FLASH from addr 'start' to end of sect w/addr 'start'+'len'-1 wrtable\n"
+ "protect off N:SF[-SL]\n"
+ " - make sectors SF-SL writable in FLASH bank # N\n"
+ "protect off bank N\n - make FLASH bank # N writable\n"
+ TMP_PROT_OFF
+ "protect off all\n - make all FLASH banks writable"
+);
+
+#undef TMP_ERASE
+#undef TMP_PROT_ON
+#undef TMP_PROT_OFF
diff --git a/cmd/font.c b/cmd/font.c
new file mode 100644
index 00000000000..36e41203654
--- /dev/null
+++ b/cmd/font.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * video commands
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <video.h>
+#include <video_console.h>
+
+static int do_font_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+
+ if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
+ return CMD_RET_FAILURE;
+ vidconsole_list_fonts(dev);
+
+ return 0;
+}
+
+static int do_font_select(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ const char *name;
+ uint size = 0;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
+ return CMD_RET_FAILURE;
+ name = argv[1];
+ if (argc == 3)
+ size = dectoul(argv[2], NULL);
+ ret = vidconsole_select_font(dev, name, size);
+ if (ret) {
+ printf("Failed (error %d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+static int do_font_size(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *font_name;
+ struct udevice *dev;
+ uint size;
+ int ret;
+
+ if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
+ return CMD_RET_FAILURE;
+ ret = vidconsole_get_font_size(dev, &font_name, &size);
+ if (ret) {
+ printf("Failed (error %d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc < 2) {
+ printf("%d\n", size);
+ } else {
+ size = dectoul(argv[1], NULL);
+
+ ret = vidconsole_select_font(dev, font_name, size);
+ if (ret) {
+ printf("Failed (error %d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(font,
+ "list - list available fonts\n"
+ "font select <name> [<size>] - select font to use\n"
+ "font size <size> - select font size to");
+
+U_BOOT_CMD_WITH_SUBCMDS(font, "Fonts", font_help_text,
+ U_BOOT_SUBCMD_MKENT(list, 1, 1, do_font_list),
+ U_BOOT_SUBCMD_MKENT(select, 3, 1, do_font_select),
+ U_BOOT_SUBCMD_MKENT(size, 2, 1, do_font_size));
diff --git a/cmd/fpga.c b/cmd/fpga.c
new file mode 100644
index 00000000000..d51c380d7b3
--- /dev/null
+++ b/cmd/fpga.c
@@ -0,0 +1,452 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000, 2001
+ * Rich Ireland, Enterasys Networks, rireland@enterasys.com.
+ */
+
+/*
+ * FPGA support
+ */
+#include <command.h>
+#include <env.h>
+#include <fpga.h>
+#include <fs.h>
+#include <gzip.h>
+#include <image.h>
+#include <log.h>
+#include <malloc.h>
+
+static long do_fpga_get_device(char *arg)
+{
+ long dev = FPGA_INVALID_DEVICE;
+ char *devstr = env_get("fpga");
+
+ if (devstr)
+ /* Should be strtol to handle -1 cases */
+ dev = simple_strtol(devstr, NULL, 16);
+
+ if (dev == FPGA_INVALID_DEVICE && arg)
+ dev = simple_strtol(arg, NULL, 16);
+
+ log_debug("device = %ld\n", dev);
+
+ return dev;
+}
+
+static int do_fpga_check_params(long *dev, long *fpga_data, size_t *data_size,
+ struct cmd_tbl *cmdtp, int argc,
+ char *const argv[])
+{
+ size_t local_data_size;
+ long local_fpga_data;
+
+ log_debug("%d, %d\n", argc, cmdtp->maxargs);
+
+ if (argc != cmdtp->maxargs) {
+ log_err("Incorrect number of parameters passed\n");
+ return CMD_RET_FAILURE;
+ }
+
+ *dev = do_fpga_get_device(argv[0]);
+
+ local_fpga_data = simple_strtol(argv[1], NULL, 16);
+ if (!local_fpga_data) {
+ log_err("Zero fpga_data address\n");
+ return CMD_RET_FAILURE;
+ }
+ *fpga_data = local_fpga_data;
+
+ local_data_size = hextoul(argv[2], NULL);
+ if (!local_data_size) {
+ log_err("Zero size\n");
+ return CMD_RET_FAILURE;
+ }
+ *data_size = local_data_size;
+
+ return 0;
+}
+
+#if defined(CONFIG_CMD_FPGA_LOAD_SECURE)
+static int do_fpga_loads(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct fpga_secure_info fpga_sec_info;
+ const int pos_userkey = 5;
+ size_t data_size = 0;
+ long fpga_data, dev;
+ int ret;
+
+ memset(&fpga_sec_info, 0, sizeof(fpga_sec_info));
+
+ if (argc < pos_userkey) {
+ log_err("Too few parameters passed\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc == pos_userkey + 1)
+ fpga_sec_info.userkey_addr = (u8 *)(uintptr_t)
+ simple_strtoull(argv[pos_userkey],
+ NULL, 16);
+ else
+ /*
+ * If 6th parameter is not passed then do_fpga_check_params
+ * will get 5 instead of expected 6 which means that function
+ * return CMD_RET_FAILURE. Increase number of params +1 to pass
+ * this.
+ */
+ argc++;
+
+ ret = do_fpga_check_params(&dev, &fpga_data, &data_size,
+ cmdtp, argc, argv);
+ if (ret)
+ return ret;
+
+ fpga_sec_info.encflag = (u8)hextoul(argv[4], NULL);
+ fpga_sec_info.authflag = (u8)hextoul(argv[3], NULL);
+
+ if (fpga_sec_info.authflag >= FPGA_NO_ENC_OR_NO_AUTH &&
+ fpga_sec_info.encflag >= FPGA_NO_ENC_OR_NO_AUTH) {
+ log_err("Use <fpga load> for NonSecure bitstream\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (fpga_sec_info.encflag == FPGA_ENC_USR_KEY &&
+ !fpga_sec_info.userkey_addr) {
+ log_err("User key not provided\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return fpga_loads(dev, (void *)fpga_data, data_size, &fpga_sec_info);
+}
+#endif
+
+#if defined(CONFIG_CMD_FPGA_LOADFS)
+static int do_fpga_loadfs(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ size_t data_size = 0;
+ long fpga_data, dev;
+ int ret;
+ fpga_fs_info fpga_fsinfo;
+
+ ret = do_fpga_check_params(&dev, &fpga_data, &data_size,
+ cmdtp, argc, argv);
+ if (ret)
+ return ret;
+
+ fpga_fsinfo.fstype = FS_TYPE_ANY;
+ fpga_fsinfo.blocksize = (unsigned int)hextoul(argv[3], NULL);
+ fpga_fsinfo.interface = argv[4];
+ fpga_fsinfo.dev_part = argv[5];
+ fpga_fsinfo.filename = argv[6];
+
+ return fpga_fsload(dev, (void *)fpga_data, data_size, &fpga_fsinfo);
+}
+#endif
+
+static int do_fpga_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ long dev = do_fpga_get_device(argv[0]);
+
+ return fpga_info(dev);
+}
+
+static int do_fpga_dump(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ size_t data_size = 0;
+ long fpga_data, dev;
+ int ret;
+
+ ret = do_fpga_check_params(&dev, &fpga_data, &data_size,
+ cmdtp, argc, argv);
+ if (ret)
+ return ret;
+
+ return fpga_dump(dev, (void *)fpga_data, data_size);
+}
+
+static int do_fpga_load(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ size_t data_size = 0;
+ long fpga_data, dev;
+ int ret;
+
+ ret = do_fpga_check_params(&dev, &fpga_data, &data_size,
+ cmdtp, argc, argv);
+ if (ret)
+ return ret;
+
+ return fpga_load(dev, (void *)fpga_data, data_size, BIT_FULL, 0);
+}
+
+#if defined(CONFIG_CMD_FPGA_LOADB)
+static int do_fpga_loadb(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ size_t data_size = 0;
+ long fpga_data, dev;
+ int ret;
+
+ ret = do_fpga_check_params(&dev, &fpga_data, &data_size,
+ cmdtp, argc, argv);
+ if (ret)
+ return ret;
+
+ return fpga_loadbitstream(dev, (void *)fpga_data, data_size, BIT_FULL);
+}
+#endif
+#if defined(CONFIG_CMD_FPGA_LOADP)
+static int do_fpga_loadp(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ size_t data_size = 0;
+ long fpga_data, dev;
+ int ret;
+
+ ret = do_fpga_check_params(&dev, &fpga_data, &data_size,
+ cmdtp, argc, argv);
+ if (ret)
+ return ret;
+
+ return fpga_load(dev, (void *)fpga_data, data_size, BIT_PARTIAL, 0);
+}
+#endif
+
+#if defined(CONFIG_CMD_FPGA_LOADBP)
+static int do_fpga_loadbp(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ size_t data_size = 0;
+ long fpga_data, dev;
+ int ret;
+
+ ret = do_fpga_check_params(&dev, &fpga_data, &data_size,
+ cmdtp, argc, argv);
+ if (ret)
+ return ret;
+
+ return fpga_loadbitstream(dev, (void *)fpga_data, data_size,
+ BIT_PARTIAL);
+}
+#endif
+
+#if defined(CONFIG_CMD_FPGA_LOADMK)
+static int do_fpga_loadmk(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ size_t data_size = 0;
+ void *fpga_data = NULL;
+#if defined(CONFIG_FIT)
+ const char *fit_uname = NULL;
+ ulong fit_addr;
+#endif
+ ulong dev = do_fpga_get_device(argv[0]);
+ char *datastr = env_get("fpgadata");
+
+ log_debug("argc %x, dev %lx, datastr %s\n", argc, dev, datastr);
+
+ if (dev == FPGA_INVALID_DEVICE) {
+ log_err("Invalid fpga device\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc == 0 && !datastr) {
+ log_err("No datastr passed\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc == 2) {
+ datastr = argv[1];
+ log_debug("Full command with two args\n");
+ } else if (argc == 1 && !datastr) {
+ log_debug("Dev is setup - fpgadata passed\n");
+ datastr = argv[0];
+ }
+
+#if defined(CONFIG_FIT)
+ if (fit_parse_subimage(datastr, (ulong)fpga_data,
+ &fit_addr, &fit_uname)) {
+ fpga_data = (void *)fit_addr;
+ log_debug("* fpga: subimage '%s' from FIT image ",
+ fit_uname);
+ log_debug("at 0x%08lx\n", fit_addr);
+ } else
+#endif
+ {
+ fpga_data = (void *)hextoul(datastr, NULL);
+ log_debug("* fpga: cmdline image address = 0x%08lx\n",
+ (ulong)fpga_data);
+ }
+ log_debug("fpga_data = 0x%lx\n", (ulong)fpga_data);
+ if (!fpga_data) {
+ log_err("Zero fpga_data address\n");
+ return CMD_RET_FAILURE;
+ }
+
+ switch (genimg_get_format(fpga_data)) {
+#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
+ case IMAGE_FORMAT_LEGACY:
+ {
+ struct legacy_img_hdr *hdr = (struct legacy_img_hdr *)fpga_data;
+ ulong data;
+ u8 comp;
+
+ comp = image_get_comp(hdr);
+ if (comp == IH_COMP_GZIP) {
+#if defined(CONFIG_GZIP)
+ ulong image_buf = image_get_data(hdr);
+ ulong image_size = ~0UL;
+
+ data = image_get_load(hdr);
+
+ if (gunzip((void *)data, ~0U, (void *)image_buf,
+ &image_size) != 0) {
+ log_err("Gunzip error\n");
+ return CMD_RET_FAILURE;
+ }
+ data_size = image_size;
+#else
+ log_err("Gunzip image is not supported\n");
+ return CMD_RET_FAILURE;
+#endif
+ } else {
+ data = (ulong)image_get_data(hdr);
+ data_size = image_get_data_size(hdr);
+ }
+ return fpga_load(dev, (void *)data, data_size,
+ BIT_FULL, 0);
+ }
+#endif
+#if defined(CONFIG_FIT)
+ case IMAGE_FORMAT_FIT:
+ {
+ const void *fit_hdr = (const void *)fpga_data;
+ int err;
+ const void *fit_data;
+
+ if (!fit_uname) {
+ log_err("No FIT subimage unit name\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) {
+ log_err("Bad FIT image format\n");
+ return CMD_RET_FAILURE;
+ }
+
+ err = fit_get_data_node(fit_hdr, fit_uname, &fit_data,
+ &data_size);
+ if (err) {
+ printf("Could not load '%s' subimage (err %d)\n",
+ fit_uname, err);
+ return CMD_RET_FAILURE;
+ }
+
+ return fpga_load(dev, fit_data, data_size, BIT_FULL, 0);
+ }
+#endif
+ default:
+ log_err("Unknown image type\n");
+ return CMD_RET_FAILURE;
+ }
+}
+#endif
+
+static struct cmd_tbl fpga_commands[] = {
+ U_BOOT_CMD_MKENT(info, 1, 1, do_fpga_info, "", ""),
+ U_BOOT_CMD_MKENT(dump, 3, 1, do_fpga_dump, "", ""),
+ U_BOOT_CMD_MKENT(load, 3, 1, do_fpga_load, "", ""),
+#if defined(CONFIG_CMD_FPGA_LOADB)
+ U_BOOT_CMD_MKENT(loadb, 3, 1, do_fpga_loadb, "", ""),
+#endif
+#if defined(CONFIG_CMD_FPGA_LOADP)
+ U_BOOT_CMD_MKENT(loadp, 3, 1, do_fpga_loadp, "", ""),
+#endif
+#if defined(CONFIG_CMD_FPGA_LOADBP)
+ U_BOOT_CMD_MKENT(loadbp, 3, 1, do_fpga_loadbp, "", ""),
+#endif
+#if defined(CONFIG_CMD_FPGA_LOADFS)
+ U_BOOT_CMD_MKENT(loadfs, 7, 1, do_fpga_loadfs, "", ""),
+#endif
+#if defined(CONFIG_CMD_FPGA_LOADMK)
+ U_BOOT_CMD_MKENT(loadmk, 2, 1, do_fpga_loadmk, "", ""),
+#endif
+#if defined(CONFIG_CMD_FPGA_LOAD_SECURE)
+ U_BOOT_CMD_MKENT(loads, 6, 1, do_fpga_loads, "", ""),
+#endif
+};
+
+static int do_fpga_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *fpga_cmd;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ fpga_cmd = find_cmd_tbl(argv[1], fpga_commands,
+ ARRAY_SIZE(fpga_commands));
+ if (!fpga_cmd) {
+ log_err("Non existing command\n");
+ return CMD_RET_FAILURE;
+ }
+
+ argc -= 2;
+ argv += 2;
+
+ if (argc > fpga_cmd->maxargs) {
+ log_err("Too many parameters passed\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = fpga_cmd->cmd(fpga_cmd, flag, argc, argv);
+
+ return cmd_process_error(fpga_cmd, ret);
+}
+
+#if defined(CONFIG_CMD_FPGA_LOADFS) || defined(CONFIG_CMD_FPGA_LOAD_SECURE)
+U_BOOT_CMD(fpga, 9, 1, do_fpga_wrapper,
+#else
+U_BOOT_CMD(fpga, 6, 1, do_fpga_wrapper,
+#endif
+ "loadable FPGA image support",
+ "info [dev] List known device information\n"
+ "fpga dump <dev> <address> <size> Load device to memory buffer\n"
+ "fpga load <dev> <address> <size> Load device from memory buffer\n"
+#if defined(CONFIG_CMD_FPGA_LOADP)
+ "fpga loadb <dev> <address> <size> Load device from bitstream buffer\n"
+#endif
+#if defined(CONFIG_CMD_FPGA_LOADP)
+ "fpga loadp <dev> <address> <size> Load device from memory buffer\n"
+ " with partial bitstream\n"
+#endif
+#if defined(CONFIG_CMD_FPGA_LOADBP)
+ "fpga loadbp <dev> <address> <size> Load device from bitstream buffer\n"
+ " with partial bitstream\n"
+#endif
+#if defined(CONFIG_CMD_FPGA_LOADFS)
+ "fpga loadfs <dev> <address> <size> <blocksize> <interface> [<dev[:part]>] <filename>\n"
+ " Load device from filesystem (FAT by default)\n"
+#endif
+#if defined(CONFIG_CMD_FPGA_LOADMK)
+ "fpga loadmk <dev> <address> Load device generated with mkimage\n"
+#if defined(CONFIG_FIT)
+ " NOTE: loadmk operating on FIT must include subimage unit\n"
+ " name in the form of addr:<subimg_uname>\n"
+#endif
+#endif
+#if defined(CONFIG_CMD_FPGA_LOAD_SECURE)
+ "fpga loads <dev> <address> <size> <authflag> <encflag> [Userkey address]\n"
+ " Load device from memory buffer with secure bistream\n"
+ " (authenticated/encrypted/both)\n"
+ " -authflag: 0 for OCM, 1 for DDR, 2 for no authentication\n"
+ " (specifies where to perform authentication)\n"
+ " -encflag: 0 for device key, 1 for user key, 2 for no encryption\n"
+ " -Userkey address: address where user key is stored\n"
+ " NOTE: secure bitstream has to be created using Xilinx bootgen tool\n"
+#endif
+);
diff --git a/cmd/fpgad.c b/cmd/fpgad.c
new file mode 100644
index 00000000000..b4bfaa12165
--- /dev/null
+++ b/cmd/fpgad.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2013
+ * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
+ *
+ * based on cmd_mem.c
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <console.h>
+#include <display_options.h>
+#include <vsprintf.h>
+
+#include <gdsys_fpga.h>
+
+static uint dp_last_fpga;
+static uint dp_last_addr;
+static uint dp_last_length = 0x40;
+
+/*
+ * FPGA Memory Display
+ *
+ * Syntax:
+ * fpgad {fpga} {addr} {len}
+ */
+#define DISP_LINE_LEN 16
+int do_fpga_md(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ unsigned int k;
+ unsigned int fpga;
+ ulong addr, length;
+ int rc = 0;
+ u16 linebuf[DISP_LINE_LEN/sizeof(u16)];
+ ulong nbytes;
+
+ /*
+ * We use the last specified parameters, unless new ones are
+ * entered.
+ */
+ fpga = dp_last_fpga;
+ addr = dp_last_addr;
+ length = dp_last_length;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ /*
+ * FPGA is specified since argc > 2
+ */
+ fpga = hextoul(argv[1], NULL);
+
+ /*
+ * Address is specified since argc > 2
+ */
+ addr = hextoul(argv[2], NULL);
+
+ /*
+ * If another parameter, it is the length to display.
+ * Length is the number of objects, not number of bytes.
+ */
+ if (argc > 3)
+ length = hextoul(argv[3], NULL);
+ }
+
+ nbytes = length * sizeof(u16);
+ do {
+ ulong linebytes = (nbytes > DISP_LINE_LEN) ?
+ DISP_LINE_LEN : nbytes;
+
+ for (k = 0; k < linebytes / sizeof(u16); ++k)
+ fpga_get_reg(fpga,
+ (u16 *)fpga_ptr[fpga] + addr
+ / sizeof(u16) + k,
+ addr + k * sizeof(u16),
+ &linebuf[k]);
+ print_buffer(addr, (void *)linebuf, sizeof(u16),
+ linebytes / sizeof(u16),
+ DISP_LINE_LEN / sizeof(u16));
+
+ nbytes -= linebytes;
+ addr += linebytes;
+ if (ctrlc()) {
+ rc = 1;
+ break;
+ }
+ } while (nbytes > 0);
+
+ dp_last_fpga = fpga;
+ dp_last_addr = addr;
+ dp_last_length = length;
+ return rc;
+}
+
+U_BOOT_CMD(
+ fpgad, 4, 1, do_fpga_md,
+ "fpga register display",
+ "fpga address [# of objects]"
+);
diff --git a/cmd/fs.c b/cmd/fs.c
new file mode 100644
index 00000000000..7f1ab8f0fd2
--- /dev/null
+++ b/cmd/fs.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Inspired by cmd_ext_common.c, cmd_fat.c.
+ */
+
+#include <command.h>
+#include <fs.h>
+
+static int do_size_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_size(cmdtp, flag, argc, argv, FS_TYPE_ANY);
+}
+
+U_BOOT_CMD(
+ size, 4, 0, do_size_wrapper,
+ "determine a file's size",
+ "<interface> <dev[:part]> <filename>\n"
+ " - Find file 'filename' from 'dev' on 'interface'\n"
+ " determine its size, and store in the 'filesize' variable."
+);
+
+static int do_load_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_load(cmdtp, flag, argc, argv, FS_TYPE_ANY);
+}
+
+U_BOOT_CMD(
+ load, 7, 0, do_load_wrapper,
+ "load binary file from a filesystem",
+ "<interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]\n"
+ " - Load binary file 'filename' from partition 'part' on device\n"
+ " type 'interface' instance 'dev' to address 'addr' in memory.\n"
+ " 'bytes' gives the size to load in bytes.\n"
+ " If 'bytes' is 0 or omitted, the file is read until the end.\n"
+ " 'pos' gives the file byte position to start reading from.\n"
+ " If 'pos' is 0 or omitted, the file is read from the start."
+);
+
+static int do_save_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_save(cmdtp, flag, argc, argv, FS_TYPE_ANY);
+}
+
+U_BOOT_CMD(
+ save, 7, 0, do_save_wrapper,
+ "save file to a filesystem",
+ "<interface> <dev[:part]> <addr> <filename> bytes [pos]\n"
+ " - Save binary file 'filename' to partition 'part' on device\n"
+ " type 'interface' instance 'dev' from addr 'addr' in memory.\n"
+ " 'bytes' gives the size to save in bytes and is mandatory.\n"
+ " 'pos' gives the file byte position to start writing to.\n"
+ " If 'pos' is 0 or omitted, the file is written from the start."
+);
+
+static int do_ls_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_ls(cmdtp, flag, argc, argv, FS_TYPE_ANY);
+}
+
+U_BOOT_CMD(
+ ls, 4, 1, do_ls_wrapper,
+ "list files in a directory (default /)",
+ "<interface> [<dev[:part]> [directory]]\n"
+ " - List files in directory 'directory' of partition 'part' on\n"
+ " device type 'interface' instance 'dev'."
+);
+
+static int do_ln_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_ln(cmdtp, flag, argc, argv, FS_TYPE_ANY);
+}
+
+U_BOOT_CMD(
+ ln, 5, 1, do_ln_wrapper,
+ "Create a symbolic link",
+ "<interface> <dev[:part]> target linkname\n"
+ " - create a symbolic link to 'target' with the name 'linkname' on\n"
+ " device type 'interface' instance 'dev'."
+);
+
+static int do_mkdir_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_mkdir(cmdtp, flag, argc, argv, FS_TYPE_ANY);
+}
+
+U_BOOT_CMD(
+ mkdir, 4, 1, do_mkdir_wrapper,
+ "create a directory",
+ "<interface> [<dev[:part]>] <directory>\n"
+ " - Create a directory 'directory' of partition 'part' on\n"
+ " device type 'interface' instance 'dev'."
+);
+
+static int do_rm_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_rm(cmdtp, flag, argc, argv, FS_TYPE_ANY);
+}
+
+U_BOOT_CMD(
+ rm, 4, 1, do_rm_wrapper,
+ "delete a file",
+ "<interface> [<dev[:part]>] <filename>\n"
+ " - delete a file with the name 'filename' on\n"
+ " device type 'interface' instance 'dev'."
+);
+
+static int do_fstype_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_fs_type(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(
+ fstype, 4, 1, do_fstype_wrapper,
+ "Look up a filesystem type",
+ "<interface> <dev>:<part>\n"
+ "- print filesystem type\n"
+ "fstype <interface> <dev>:<part> <varname>\n"
+ "- set environment variable to filesystem type\n"
+);
+
+static int do_fstypes_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ return do_fs_types(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(
+ fstypes, 1, 1, do_fstypes_wrapper,
+ "List supported filesystem types", ""
+);
+
+static int do_mv_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_mv(cmdtp, flag, argc, argv, FS_TYPE_ANY);
+}
+
+U_BOOT_CMD(
+ mv, 5, 1, do_mv_wrapper,
+ "rename/move a file/directory",
+ "<interface> [<dev[:part]>] <old_path> <new_path>\n"
+ " - renames/moves a file/directory in 'dev' on 'interface' from\n"
+ " 'old_path' to 'new_path'"
+);
diff --git a/cmd/fs_uuid.c b/cmd/fs_uuid.c
new file mode 100644
index 00000000000..5f7770d09ac
--- /dev/null
+++ b/cmd/fs_uuid.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * cmd_fs_uuid.c -- fsuuid command
+ *
+ * Copyright (C) 2014, Bachmann electronic GmbH
+ */
+
+#include <command.h>
+#include <fs.h>
+
+static int do_fs_uuid_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_fs_uuid(cmdtp, flag, argc, argv, FS_TYPE_ANY);
+}
+
+U_BOOT_CMD(
+ fsuuid, 4, 1, do_fs_uuid_wrapper,
+ "Look up a filesystem UUID",
+ "<interface> <dev>:<part>\n"
+ " - print filesystem UUID\n"
+ "fsuuid <interface> <dev>:<part> <varname>\n"
+ " - set environment variable to filesystem UUID\n"
+);
diff --git a/cmd/fuse.c b/cmd/fuse.c
new file mode 100644
index 00000000000..e2206cdf0d5
--- /dev/null
+++ b/cmd/fuse.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2009-2013 ADVANSEE
+ * Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
+ *
+ * Based on the mpc512x iim code:
+ * Copyright 2008 Silicon Turnkey Express, Inc.
+ * Martha Marx <mmarx@silicontkx.com>
+ */
+
+#include <command.h>
+#include <console.h>
+#include <fuse.h>
+#include <mapmem.h>
+#include <vsprintf.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+
+static int confirm_prog(void)
+{
+ puts("Warning: Programming fuses is an irreversible operation!\n"
+ " This may brick your system.\n"
+ " Use this command only if you are sure of "
+ "what you are doing!\n"
+ "\nReally perform this fuse programming? <y/N>\n");
+
+ if (confirm_yesno())
+ return 1;
+
+ puts("Fuse programming aborted\n");
+ return 0;
+}
+
+static int do_fuse(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *op = cmd_arg1(argc, argv);
+ int confirmed = argc >= 3 && !strcmp(argv[2], "-y");
+ u32 bank, word, cnt, val, cmp;
+ ulong addr;
+ void *buf, *start;
+ int ret, i;
+
+ argc -= 2 + confirmed;
+ argv += 2 + confirmed;
+
+ if (IS_ENABLED(CONFIG_CMD_FUSE_WRITEBUFF) && !strcmp(op, "writebuff")) {
+ if (argc == 1)
+ addr = simple_strtoul(argv[0], NULL, 16);
+ else
+ return CMD_RET_USAGE;
+ } else {
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ bank = simple_strtoul(argv[0], NULL, 0);
+ word = simple_strtoul(argv[1], NULL, 0);
+ }
+
+ if (!strcmp(op, "read")) {
+ if (argc == 2)
+ cnt = 1;
+ else if (argc == 3)
+ cnt = simple_strtoul(argv[2], NULL, 0);
+ else
+ return CMD_RET_USAGE;
+
+ printf("Reading bank %u:\n", bank);
+ for (i = 0; i < cnt; i++, word++) {
+ if (!(i % 4))
+ printf("\nWord 0x%.8x:", word);
+
+ ret = fuse_read(bank, word, &val);
+ if (ret)
+ goto err;
+
+ printf(" %.8x", val);
+ }
+ putc('\n');
+ } else if (!strcmp(op, "readm")) {
+ if (argc == 3)
+ cnt = 1;
+ else if (argc == 4)
+ cnt = simple_strtoul(argv[3], NULL, 0);
+ else
+ return CMD_RET_USAGE;
+
+ addr = simple_strtoul(argv[2], NULL, 16);
+
+ start = map_sysmem(addr, 4);
+ buf = start;
+
+ printf("Reading bank %u len %u to 0x%lx\n", bank, cnt, addr);
+ for (i = 0; i < cnt; i++, word++) {
+ ret = fuse_read(bank, word, &val);
+ if (ret)
+ goto err;
+
+ *((u32 *)buf) = val;
+ buf += 4;
+ }
+
+ unmap_sysmem(start);
+ } else if (!strcmp(op, "cmp")) {
+ if (argc == 3)
+ cmp = simple_strtoul(argv[2], NULL, 0);
+ else
+ return CMD_RET_USAGE;
+
+ printf("Comparing bank %u:\n", bank);
+ printf("\nWord 0x%.8x:", word);
+ printf("\nValue 0x%.8x:", cmp);
+
+ ret = fuse_read(bank, word, &val);
+ if (ret)
+ goto err;
+
+ printf("0x%.8x\n", val);
+ if (val != cmp) {
+ printf("failed\n");
+ return CMD_RET_FAILURE;
+ }
+ printf("passed\n");
+ } else if (!strcmp(op, "sense")) {
+ if (argc == 2)
+ cnt = 1;
+ else if (argc == 3)
+ cnt = simple_strtoul(argv[2], NULL, 0);
+ else
+ return CMD_RET_USAGE;
+
+ printf("Sensing bank %u:\n", bank);
+ for (i = 0; i < cnt; i++, word++) {
+ if (!(i % 4))
+ printf("\nWord 0x%.8x:", word);
+
+ ret = fuse_sense(bank, word, &val);
+ if (ret)
+ goto err;
+
+ printf(" %.8x", val);
+ }
+ putc('\n');
+ } else if (!strcmp(op, "prog")) {
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ for (i = 2; i < argc; i++, word++) {
+ val = simple_strtoul(argv[i], NULL, 16);
+
+ printf("Programming bank %u word 0x%.8x to 0x%.8x...\n",
+ bank, word, val);
+ if (!confirmed && !confirm_prog())
+ return CMD_RET_FAILURE;
+ ret = fuse_prog(bank, word, val);
+ if (ret)
+ goto err;
+ }
+ } else if (!strcmp(op, "override")) {
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ for (i = 2; i < argc; i++, word++) {
+ val = simple_strtoul(argv[i], NULL, 16);
+
+ printf("Overriding bank %u word 0x%.8x with "
+ "0x%.8x...\n", bank, word, val);
+ ret = fuse_override(bank, word, val);
+ if (ret)
+ goto err;
+ }
+ } else if (IS_ENABLED(CONFIG_CMD_FUSE_WRITEBUFF) && !strcmp(op, "writebuff")) {
+ printf("Programming fuses using a structured buffer in memory "
+ "starting at addr 0x%lx\n", addr);
+ if (!confirmed && !confirm_prog())
+ return CMD_RET_FAILURE;
+
+ ret = fuse_writebuff(addr);
+ if (ret)
+ goto err;
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ return 0;
+
+err:
+ puts("ERROR\n");
+ return CMD_RET_FAILURE;
+}
+
+U_BOOT_CMD(
+ fuse, CONFIG_SYS_MAXARGS, 0, do_fuse,
+ "Fuse sub-system",
+ "read <bank> <word> [<cnt>] - read 1 or 'cnt' fuse words,\n"
+ " starting at 'word'\n"
+ "fuse cmp <bank> <word> <hexval> - compare 'hexval' to fuse\n"
+ " at 'word'\n"
+ "fuse readm <bank> <word> <addr> [<cnt>] - read 1 or 'cnt' fuse words,\n"
+ " starting at 'word' into memory at 'addr'\n"
+ "fuse sense <bank> <word> [<cnt>] - sense 1 or 'cnt' fuse words,\n"
+ " starting at 'word'\n"
+ "fuse prog [-y] <bank> <word> <hexval> [<hexval>...] - program 1 or\n"
+ " several fuse words, starting at 'word' (PERMANENT)\n"
+ "fuse override <bank> <word> <hexval> [<hexval>...] - override 1 or\n"
+ " several fuse words, starting at 'word'\n"
+#ifdef CONFIG_CMD_FUSE_WRITEBUFF
+ "fuse writebuff [-y] <addr> - program fuse data\n"
+ " using a structured buffer in memory starting at 'addr'\n"
+#endif /* CONFIG_CMD_FUSE_WRITEBUFF */
+);
diff --git a/cmd/fwu_mdata.c b/cmd/fwu_mdata.c
new file mode 100644
index 00000000000..5b5a2e4d1cd
--- /dev/null
+++ b/cmd/fwu_mdata.c
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2022, Linaro Limited
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <fwu.h>
+#include <fwu_mdata.h>
+#include <hexdump.h>
+#include <log.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <linux/types.h>
+
+static void print_mdata(struct fwu_data *data)
+{
+ int i, j;
+ struct fwu_image_entry *img_entry;
+ struct fwu_image_bank_info *img_info;
+
+ printf("\tFWU Metadata\n");
+ printf("crc32: %#x\n", data->crc32);
+ printf("version: %#x\n", data->version);
+ printf("size: %#x\n", data->metadata_size);
+ printf("active_index: %#x\n", data->active_index);
+ printf("previous_active_index: %#x\n", data->previous_active_index);
+
+ if (data->version == 2) {
+ for (i = 0; i < 4; i++)
+ printf("bank_state[%d]: %#x\n",
+ i, data->bank_state[i]);
+ }
+
+ printf("\tImage Info\n");
+ for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
+ img_entry = &data->fwu_images[i];
+ printf("\nImage Type Guid: %pUL\n",
+ &img_entry->image_type_guid);
+ printf("Location Guid: %pUL\n", &img_entry->location_guid);
+ for (j = 0; j < CONFIG_FWU_NUM_BANKS; j++) {
+ img_info = &img_entry->img_bank_info[j];
+ printf("Image Guid: %pUL\n", &img_info->image_guid);
+ printf("Image Acceptance: %s\n",
+ img_info->accepted == 0x1 ? "yes" : "no");
+ }
+ }
+
+ if (data->version == 2) {
+ struct fwu_mdata *mdata = data->fwu_mdata;
+ struct fwu_fw_store_desc *desc;
+ void *end;
+ u32 diff;
+
+ /*
+ * fwu_mdata defines only header that's why taking it as array
+ * which exactly point to image description location
+ */
+ desc = (struct fwu_fw_store_desc *)&mdata[1];
+
+ /* Number of entries is taken from for loop - variable i */
+ end = &desc->img_entry[i];
+ debug("mdata %p, desc %p, end %p\n", mdata, desc, end);
+
+ diff = data->metadata_size - ((void *)end - (void *)mdata);
+ if (diff) {
+ printf("Custom fields covered by CRC len: 0x%x\n", diff);
+ print_hex_dump_bytes("CUSTOM ", DUMP_PREFIX_OFFSET,
+ end, diff);
+ }
+ }
+}
+
+int do_fwu_mdata_read(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct fwu_data *data = fwu_get_data();
+
+ print_mdata(data);
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+ fwu_mdata_read, 1, 1, do_fwu_mdata_read,
+ "Read and print FWU metadata",
+ ""
+);
diff --git a/cmd/gettime.c b/cmd/gettime.c
new file mode 100644
index 00000000000..fc307efce8c
--- /dev/null
+++ b/cmd/gettime.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ *
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * (C) Copyright 2001
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+/*
+ * Get Timer overflows after 2^32 / CONFIG_SYS_HZ (32Khz) = 131072 sec
+ */
+#include <command.h>
+#include <time.h>
+
+static int do_gettime(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ unsigned long int val = get_timer(0);
+
+#ifdef CONFIG_SYS_HZ
+ printf("Timer val: %lu\n", val);
+ printf("Seconds : %lu\n", val / CONFIG_SYS_HZ);
+ printf("Remainder : %lu\n", val % CONFIG_SYS_HZ);
+ printf("sys_hz = %lu\n", (unsigned long int)CONFIG_SYS_HZ);
+#else
+ printf("CONFIG_SYS_HZ not defined");
+ printf("Timer Val %lu", val);
+#endif
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ gettime, 1, 1, do_gettime,
+ "get timer val elapsed",
+ "get time elapsed from uboot start"
+);
diff --git a/cmd/gpio.c b/cmd/gpio.c
new file mode 100644
index 00000000000..7a43dc6ab18
--- /dev/null
+++ b/cmd/gpio.c
@@ -0,0 +1,317 @@
+/*
+ * Control GPIO pins on the fly
+ *
+ * Copyright (c) 2008-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <command.h>
+#include <errno.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#ifdef CONFIG_CMD_GPIO_READ
+#include <env.h>
+#endif
+#include <asm/gpio.h>
+#include <linux/err.h>
+#include <dm/device_compat.h>
+
+__weak int name_to_gpio(const char *name)
+{
+ return dectoul(name, NULL);
+}
+
+enum gpio_cmd {
+ GPIOC_INPUT,
+ GPIOC_SET,
+ GPIOC_CLEAR,
+ GPIOC_TOGGLE,
+#ifdef CONFIG_CMD_GPIO_READ
+ GPIOC_READ,
+#endif
+};
+
+#if defined(CONFIG_DM_GPIO) && !defined(gpio_status)
+
+/* A few flags used by show_gpio() */
+enum {
+ FLAG_SHOW_ALL = 1 << 0,
+ FLAG_SHOW_BANK = 1 << 1,
+ FLAG_SHOW_NEWLINE = 1 << 2,
+};
+
+static void gpio_get_description(struct udevice *dev, const char *bank_name,
+ int offset, int *flagsp, bool show_all)
+{
+ char buf[80];
+ int ret;
+
+ ret = gpio_get_function(dev, offset, NULL);
+ if (ret < 0)
+ goto err;
+ if (!show_all && !(*flagsp & FLAG_SHOW_ALL) && ret == GPIOF_UNUSED)
+ return;
+ if ((*flagsp & FLAG_SHOW_BANK) && bank_name) {
+ if (*flagsp & FLAG_SHOW_NEWLINE) {
+ putc('\n');
+ *flagsp &= ~FLAG_SHOW_NEWLINE;
+ }
+ printf("Bank %s:\n", bank_name);
+ *flagsp &= ~FLAG_SHOW_BANK;
+ }
+
+ ret = gpio_get_status(dev, offset, buf, sizeof(buf));
+ if (ret)
+ goto err;
+
+ printf("%s\n", buf);
+ return;
+err:
+ if (ret != -ENOENT)
+ printf("Error %d\n", ret);
+}
+
+static int do_gpio_status(bool all, const char *gpio_name)
+{
+ struct udevice *dev;
+ int banklen;
+ int flags;
+ int ret, err = 0;
+
+ flags = 0;
+ if (gpio_name && !*gpio_name)
+ gpio_name = NULL;
+ for (ret = uclass_first_device_check(UCLASS_GPIO, &dev);
+ dev;
+ ret = uclass_next_device_check(&dev)) {
+ const char *bank_name;
+ int num_bits;
+
+ if (ret) {
+ printf("GPIO device %s probe error %i\n",
+ dev->name, ret);
+ err = ret;
+ continue;
+ }
+
+ flags |= FLAG_SHOW_BANK;
+ if (all)
+ flags |= FLAG_SHOW_ALL;
+ bank_name = gpio_get_bank_info(dev, &num_bits);
+ if (!num_bits) {
+ debug("GPIO device %s has no bits\n", dev->name);
+ continue;
+ }
+ banklen = bank_name ? strlen(bank_name) : 0;
+
+ if (!gpio_name || !bank_name ||
+ !strncasecmp(gpio_name, bank_name, banklen)) {
+ const char *p;
+ int offset;
+
+ p = gpio_name + banklen;
+ if (gpio_name && *p) {
+ offset = dectoul(p, NULL);
+ gpio_get_description(dev, bank_name, offset,
+ &flags, true);
+ } else {
+ for (offset = 0; offset < num_bits; offset++) {
+ gpio_get_description(dev, bank_name,
+ offset, &flags, false);
+ }
+ }
+ }
+ /* Add a newline between bank names */
+ if (!(flags & FLAG_SHOW_BANK))
+ flags |= FLAG_SHOW_NEWLINE;
+ }
+
+ return err;
+}
+#endif
+
+static int do_gpio(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ unsigned int gpio;
+ enum gpio_cmd sub_cmd;
+ int value;
+ const char *str_cmd, *str_gpio = NULL;
+#ifdef CONFIG_CMD_GPIO_READ
+ const char *str_var = NULL;
+#endif
+ int ret;
+#ifdef CONFIG_DM_GPIO
+ bool all = false;
+#endif
+
+ if (argc < 2)
+ show_usage:
+ return CMD_RET_USAGE;
+ str_cmd = argv[1];
+ argc -= 2;
+ argv += 2;
+#ifdef CONFIG_DM_GPIO
+ if (argc > 0 && !strncmp(str_cmd, "status", 2) && !strcmp(*argv, "-a")) {
+ all = true;
+ argc--;
+ argv++;
+ }
+#endif
+#ifdef CONFIG_CMD_GPIO_READ
+ if (argc > 0 && !strncmp(str_cmd, "read", 2)) {
+ if (argc < 2)
+ goto show_usage;
+ str_var = *argv;
+ argc--;
+ argv++;
+ }
+#endif
+ if (argc > 0)
+ str_gpio = *argv;
+ if (!strncmp(str_cmd, "status", 2)) {
+ /* Support deprecated gpio_status() */
+#ifdef gpio_status
+ gpio_status();
+ return 0;
+#elif defined(CONFIG_DM_GPIO)
+ return cmd_process_error(cmdtp, do_gpio_status(all, str_gpio));
+#else
+ goto show_usage;
+#endif
+ }
+
+ if (!str_gpio)
+ goto show_usage;
+
+ /* parse the behavior */
+ switch (*str_cmd) {
+ case 'i':
+ sub_cmd = GPIOC_INPUT;
+ break;
+ case 's':
+ sub_cmd = GPIOC_SET;
+ break;
+ case 'c':
+ sub_cmd = GPIOC_CLEAR;
+ break;
+ case 't':
+ sub_cmd = GPIOC_TOGGLE;
+ break;
+#ifdef CONFIG_CMD_GPIO_READ
+ case 'r':
+ sub_cmd = GPIOC_READ;
+ break;
+#endif
+ default:
+ goto show_usage;
+ }
+
+#if defined(CONFIG_DM_GPIO)
+ /*
+ * TODO(sjg@chromium.org): For now we must fit into the existing GPIO
+ * framework, so we look up the name here and convert it to a GPIO number.
+ * Once all GPIO drivers are converted to driver model, we can change the
+ * code here to use the GPIO uclass interface instead of the numbered
+ * GPIO compatibility layer.
+ */
+ ret = gpio_lookup_name(str_gpio, NULL, NULL, &gpio);
+ if (ret) {
+ printf("GPIO: '%s' not found\n", str_gpio);
+ return cmd_process_error(cmdtp, ret);
+ }
+#else
+ /* turn the gpio name into a gpio number */
+ gpio = name_to_gpio(str_gpio);
+ if (gpio < 0)
+ goto show_usage;
+#endif
+ /* grab the pin before we tweak it */
+ ret = gpio_request(gpio, "cmd_gpio");
+ if (ret && ret != -EBUSY) {
+ printf("gpio: requesting pin %u failed\n", gpio);
+ return -1;
+ }
+
+ /* finally, let's do it: set direction and exec command */
+ if (sub_cmd == GPIOC_INPUT
+#ifdef CONFIG_CMD_GPIO_READ
+ || sub_cmd == GPIOC_READ
+#endif
+ ) {
+ gpio_direction_input(gpio);
+ value = gpio_get_value(gpio);
+ } else {
+ switch (sub_cmd) {
+ case GPIOC_SET:
+ value = 1;
+ break;
+ case GPIOC_CLEAR:
+ value = 0;
+ break;
+ case GPIOC_TOGGLE:
+ value = gpio_get_value(gpio);
+ if (!IS_ERR_VALUE(value))
+ value = !value;
+ break;
+ default:
+ goto show_usage;
+ }
+ gpio_direction_output(gpio, value);
+ }
+ printf("gpio: pin %s (gpio %u) value is ", str_gpio, gpio);
+
+ if (IS_ERR_VALUE(value)) {
+ printf("unknown (ret=%d)\n", value);
+ goto err;
+ } else {
+ printf("%d\n", value);
+#ifdef CONFIG_CMD_GPIO_READ
+ if (sub_cmd == GPIOC_READ)
+ env_set_ulong(str_var, (ulong)value);
+#endif
+ }
+
+ if (sub_cmd != GPIOC_INPUT && !IS_ERR_VALUE(value)
+#ifdef CONFIG_CMD_GPIO_READ
+ && sub_cmd != GPIOC_READ
+#endif
+ ) {
+ int nval = gpio_get_value(gpio);
+
+ if (IS_ERR_VALUE(nval)) {
+ printf(" Warning: no access to GPIO output value\n");
+ goto err;
+ } else if (nval != value) {
+ printf(" Warning: value of pin is still %d\n", nval);
+ goto err;
+ }
+ }
+
+ if (ret != -EBUSY)
+ gpio_free(gpio);
+
+ /*
+ * Whilst wrong, the legacy gpio input command returns the pin
+ * value, or CMD_RET_FAILURE (which is indistinguishable from a
+ * valid pin value).
+ */
+ return (sub_cmd == GPIOC_INPUT) ? value : CMD_RET_SUCCESS;
+
+err:
+ if (ret != -EBUSY)
+ gpio_free(gpio);
+ return CMD_RET_FAILURE;
+}
+
+U_BOOT_CMD(gpio, 4, 0, do_gpio,
+ "query and control gpio pins",
+ "<input|set|clear|toggle> <pin>\n"
+ " - input/set/clear/toggle the specified pin\n"
+#ifdef CONFIG_CMD_GPIO_READ
+ "gpio read <name> <pin>\n"
+ " - set environment variable 'name' to the specified pin\n"
+#endif
+ "gpio status [-a] [<bank> | <pin>] - show [all/claimed] GPIOs");
diff --git a/cmd/gpt.c b/cmd/gpt.c
new file mode 100644
index 00000000000..e18e5036a06
--- /dev/null
+++ b/cmd/gpt.c
@@ -0,0 +1,1240 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * cmd_gpt.c -- GPT (GUID Partition Table) handling command
+ *
+ * Copyright (C) 2015
+ * Lukasz Majewski <l.majewski@majess.pl>
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * author: Lukasz Majewski <l.majewski@samsung.com>
+ * author: Piotr Wilczek <p.wilczek@samsung.com>
+ */
+
+#include <blk.h>
+#include <env.h>
+#include <log.h>
+#include <malloc.h>
+#include <command.h>
+#include <part.h>
+#include <part_efi.h>
+#include <part.h>
+#include <exports.h>
+#include <u-boot/uuid.h>
+#include <linux/ctype.h>
+#include <div64.h>
+#include <memalign.h>
+#include <linux/compat.h>
+#include <linux/err.h>
+#include <linux/sizes.h>
+#include <stdlib.h>
+
+static LIST_HEAD(disk_partitions);
+
+/**
+ * extract_env(): Expand env name from string format '&{env_name}'
+ * and return pointer to the env (if the env is set)
+ *
+ * @param str - pointer to string
+ * @param env - pointer to pointer to extracted env
+ *
+ * Return: - zero on successful expand and env is set
+ */
+static int extract_env(const char *str, char **env)
+{
+ int ret = -1;
+ char *e, *s;
+#ifdef CONFIG_RANDOM_UUID
+ char uuid_str[UUID_STR_LEN + 1];
+#endif
+
+ if (!str || strlen(str) < 4)
+ return -1;
+
+ if (!((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}')))
+ return -1;
+
+ s = strdup(str);
+ if (s == NULL)
+ return -1;
+
+ memset(s + strlen(s) - 1, '\0', 1);
+ memmove(s, s + 2, strlen(s) - 1);
+
+ e = env_get(s);
+ if (e == NULL) {
+#ifdef CONFIG_RANDOM_UUID
+ debug("%s unset. ", str);
+ gen_rand_uuid_str(uuid_str, UUID_STR_FORMAT_GUID);
+ env_set(s, uuid_str);
+
+ e = env_get(s);
+ if (e) {
+ debug("Set to random.\n");
+ ret = 0;
+ } else {
+ debug("Can't get random UUID.\n");
+ }
+#else
+ debug("%s unset.\n", str);
+#endif
+ } else {
+ debug("%s get from environment.\n", str);
+ ret = 0;
+ }
+
+ *env = e;
+ free(s);
+
+ return ret;
+}
+
+/**
+ * extract_val(): Extract value from a key=value pair list (comma separated).
+ * Only value for the given key is returend.
+ * Function allocates memory for the value, remember to free!
+ *
+ * @param str - pointer to string with key=values pairs
+ * @param key - pointer to the key to search for
+ *
+ * Return: - pointer to allocated string with the value
+ */
+static char *extract_val(const char *str, const char *key)
+{
+ char *v, *k;
+ char *s, *strcopy;
+ char *new = NULL;
+
+ strcopy = strdup(str);
+ if (strcopy == NULL)
+ return NULL;
+
+ s = strcopy;
+ while (s) {
+ v = strsep(&s, ",");
+ if (!v)
+ break;
+ k = strsep(&v, "=");
+ if (!k)
+ break;
+ k += strspn(k, " \t");
+ if (strcmp(k, key) == 0) {
+ new = strdup(v);
+ break;
+ }
+ }
+
+ free(strcopy);
+
+ return new;
+}
+
+/**
+ * found_key(): Found key without value in parameter list (comma separated).
+ *
+ * @param str - pointer to string with key
+ * @param key - pointer to the key to search for
+ *
+ * Return: - true on found key
+ */
+static bool found_key(const char *str, const char *key)
+{
+ char *k;
+ char *s, *strcopy;
+ bool result = false;
+
+ strcopy = strdup(str);
+ if (!strcopy)
+ return NULL;
+
+ s = strcopy;
+ while (s) {
+ k = strsep(&s, ",");
+ if (!k)
+ break;
+ k += strspn(k, " \t");
+ if (strcmp(k, key) == 0) {
+ result = true;
+ break;
+ }
+ }
+
+ free(strcopy);
+
+ return result;
+}
+
+/**
+ * calc_parts_list_len() - get size of partition table description
+ *
+ * @numparts: number of partitions
+ * Return: string size including terminating NUL
+ */
+static int calc_parts_list_len(int numparts)
+{
+ /* number of hexadecimal digits of the lbaint_t representation */
+ const int lbaint_size = 2 * sizeof(lbaint_t);
+ int partlistlen;
+
+ /* media description including terminating NUL */
+ partlistlen = strlen("uuid_disk=;") + UUID_STR_LEN + 1;
+ /* per-partition descriptions; numparts */
+ partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN);
+ /* see part.h for definition of struct disk_partition */
+ partlistlen += numparts * (strlen("start=0x,") + lbaint_size);
+ partlistlen += numparts * (strlen("size=0x,") + lbaint_size);
+ if (IS_ENABLED(CONFIG_PARTITION_UUIDS))
+ partlistlen += numparts * (strlen("uuid=,") + UUID_STR_LEN);
+ if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID))
+ partlistlen += numparts * (strlen("type=;") + UUID_STR_LEN);
+ debug("Length of partitions_list is %d for %d partitions\n",
+ partlistlen, numparts);
+ return partlistlen;
+}
+
+#ifdef CONFIG_CMD_GPT_RENAME
+static void del_gpt_info(void)
+{
+ struct list_head *pos = &disk_partitions;
+ struct disk_part *curr;
+ while (!list_empty(pos)) {
+ curr = list_entry(pos->next, struct disk_part, list);
+ list_del(pos->next);
+ free(curr);
+ }
+}
+
+static struct disk_part *allocate_disk_part(struct disk_partition *info,
+ int partnum)
+{
+ struct disk_part *newpart;
+ newpart = calloc(1, sizeof(struct disk_part));
+ if (!newpart)
+ return ERR_PTR(-ENOMEM);
+
+ newpart->gpt_part_info.start = info->start;
+ newpart->gpt_part_info.size = info->size;
+ newpart->gpt_part_info.blksz = info->blksz;
+ strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name,
+ PART_NAME_LEN);
+ newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0';
+ strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type,
+ PART_TYPE_LEN);
+ newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0';
+ newpart->gpt_part_info.bootable = info->bootable;
+ if (IS_ENABLED(CONFIG_PARTITION_UUIDS))
+ disk_partition_set_uuid(&newpart->gpt_part_info,
+ disk_partition_uuid(info));
+ if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID))
+ disk_partition_set_type_guid(&newpart->gpt_part_info,
+ disk_partition_type_guid(info));
+ newpart->partnum = partnum;
+
+ return newpart;
+}
+
+static void prettyprint_part_size(char *sizestr, lbaint_t partsize,
+ lbaint_t blksize)
+{
+ unsigned long long partbytes, partmegabytes;
+
+ partbytes = partsize * blksize;
+ partmegabytes = lldiv(partbytes, SZ_1M);
+ snprintf(sizestr, 16, "%lluMiB", partmegabytes);
+}
+
+static void print_gpt_info(void)
+{
+ struct list_head *pos;
+ struct disk_part *curr;
+ char partstartstr[16];
+ char partsizestr[16];
+
+ list_for_each(pos, &disk_partitions) {
+ curr = list_entry(pos, struct disk_part, list);
+ prettyprint_part_size(partstartstr, curr->gpt_part_info.start,
+ curr->gpt_part_info.blksz);
+ prettyprint_part_size(partsizestr, curr->gpt_part_info.size,
+ curr->gpt_part_info.blksz);
+
+ printf("Partition %d:\n", curr->partnum);
+ printf("Start %s, size %s\n", partstartstr, partsizestr);
+ printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz,
+ curr->gpt_part_info.name);
+ printf("Type %s, bootable %d\n", curr->gpt_part_info.type,
+ curr->gpt_part_info.bootable & PART_BOOTABLE);
+ if (CONFIG_IS_ENABLED(PARTITION_UUIDS))
+ printf("UUID %s\n",
+ disk_partition_uuid(&curr->gpt_part_info));
+ if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID))
+ printf("Type GUID %s\n",
+ disk_partition_type_guid(&curr->gpt_part_info));
+ printf("\n");
+ }
+}
+
+/*
+ * create the string that upstream 'gpt write' command will accept as an
+ * argument
+ *
+ * From doc/README.gpt, Format of partitions layout:
+ * "uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
+ * name=kernel,size=60MiB,uuid=...;"
+ * The fields 'name' and 'size' are mandatory for every partition.
+ * The field 'start' is optional. The fields 'uuid' and 'uuid_disk'
+ * are optional if CONFIG_RANDOM_UUID is enabled.
+ */
+static int create_gpt_partitions_list(int numparts, const char *guid,
+ char *partitions_list)
+{
+ struct list_head *pos;
+ struct disk_part *curr;
+ char partstr[PART_NAME_LEN + 1];
+
+ if (!partitions_list)
+ return -EINVAL;
+
+ strcpy(partitions_list, "uuid_disk=");
+ strncat(partitions_list, guid, UUID_STR_LEN + 1);
+ strcat(partitions_list, ";");
+
+ list_for_each(pos, &disk_partitions) {
+ curr = list_entry(pos, struct disk_part, list);
+ strcat(partitions_list, "name=");
+ strncat(partitions_list, (const char *)curr->gpt_part_info.name,
+ PART_NAME_LEN + 1);
+ sprintf(partstr, ",start=0x%llx",
+ (unsigned long long)curr->gpt_part_info.start *
+ curr->gpt_part_info.blksz);
+ /* one extra byte for NULL */
+ strncat(partitions_list, partstr, PART_NAME_LEN + 1);
+ sprintf(partstr, ",size=0x%llx",
+ (unsigned long long)curr->gpt_part_info.size *
+ curr->gpt_part_info.blksz);
+ strncat(partitions_list, partstr, PART_NAME_LEN + 1);
+
+ if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) {
+ strcat(partitions_list, ",type=");
+ strncat(partitions_list,
+ disk_partition_type_guid(&curr->gpt_part_info),
+ UUID_STR_LEN + 1);
+ }
+ if (CONFIG_IS_ENABLED(PARTITION_UUIDS)) {
+ strcat(partitions_list, ",uuid=");
+ strncat(partitions_list,
+ disk_partition_uuid(&curr->gpt_part_info),
+ UUID_STR_LEN + 1);
+ }
+ if (curr->gpt_part_info.bootable & PART_BOOTABLE)
+ strcat(partitions_list, ",bootable");
+ strcat(partitions_list, ";");
+ }
+ return 0;
+}
+
+/*
+ * read partition info into disk_partitions list where
+ * it can be printed or modified
+ */
+static int get_gpt_info(struct blk_desc *dev_desc)
+{
+ /* start partition numbering at 1, as U-Boot does */
+ int valid_parts = 0, p, ret;
+ struct disk_partition info;
+ struct disk_part *new_disk_part;
+
+ /*
+ * Always re-read partition info from device, in case
+ * it has changed
+ */
+ INIT_LIST_HEAD(&disk_partitions);
+
+ for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
+ ret = part_get_info(dev_desc, p, &info);
+ if (ret)
+ continue;
+
+ /* Add 1 here because counter is zero-based but p1 is
+ the first partition */
+ new_disk_part = allocate_disk_part(&info, valid_parts+1);
+ if (IS_ERR(new_disk_part))
+ goto out;
+
+ list_add_tail(&new_disk_part->list, &disk_partitions);
+ valid_parts++;
+ }
+ if (valid_parts == 0) {
+ printf("** No valid partitions found **\n");
+ goto out;
+ }
+ return valid_parts;
+ out:
+ if (valid_parts >= 1)
+ del_gpt_info();
+ return -ENODEV;
+}
+
+/* a wrapper to test get_gpt_info */
+static int do_get_gpt_info(struct blk_desc *dev_desc, char * const namestr)
+{
+ int numparts;
+
+ numparts = get_gpt_info(dev_desc);
+
+ if (numparts > 0) {
+ if (namestr) {
+ char disk_guid[UUID_STR_LEN + 1];
+ char *partitions_list;
+ int partlistlen;
+ int ret = -1;
+
+ ret = get_disk_guid(dev_desc, disk_guid);
+ if (ret < 0)
+ return ret;
+
+ partlistlen = calc_parts_list_len(numparts);
+ partitions_list = malloc(partlistlen);
+ if (!partitions_list) {
+ del_gpt_info();
+ return -ENOMEM;
+ }
+ memset(partitions_list, '\0', partlistlen);
+
+ ret = create_gpt_partitions_list(numparts, disk_guid,
+ partitions_list);
+ if (ret < 0)
+ printf("Error: Could not create partition list string!\n");
+ else
+ env_set(namestr, partitions_list);
+
+ free(partitions_list);
+ } else {
+ print_gpt_info();
+ }
+ del_gpt_info();
+ return 0;
+ }
+ return numparts;
+}
+#endif
+
+/**
+ * set_gpt_info(): Fill partition information from string
+ * function allocates memory, remember to free!
+ *
+ * @param dev_desc - pointer block device descriptor
+ * @param str_part - pointer to string with partition information
+ * @param str_disk_guid - pointer to pointer to allocated string with disk guid
+ * @param partitions - pointer to pointer to allocated partitions array
+ * @param parts_count - number of partitions
+ *
+ * Return: - zero on success, otherwise error
+ *
+ */
+static int set_gpt_info(struct blk_desc *dev_desc,
+ const char *str_part,
+ char **str_disk_guid,
+ struct disk_partition **partitions,
+ u8 *parts_count)
+{
+ char *tok, *str, *s;
+ int i;
+ char *val, *p;
+ int p_count;
+ struct disk_partition *parts;
+ int errno = 0;
+ uint64_t size_ll, start_ll;
+ lbaint_t offset = 0;
+ int max_str_part = calc_parts_list_len(MAX_SEARCH_PARTITIONS);
+
+ debug("%s: lba num: 0x%x %d\n", __func__,
+ (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba);
+
+ if (str_part == NULL)
+ return -1;
+
+ str = strdup(str_part);
+ if (str == NULL)
+ return -ENOMEM;
+
+ /* extract disk guid */
+ s = str;
+ val = extract_val(str, "uuid_disk");
+ if (!val) {
+#ifdef CONFIG_RANDOM_UUID
+ *str_disk_guid = malloc(UUID_STR_LEN + 1);
+ if (*str_disk_guid == NULL)
+ return -ENOMEM;
+ gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD);
+#else
+ free(str);
+ return -2;
+#endif
+ } else {
+ val = strsep(&val, ";");
+ if (extract_env(val, &p))
+ p = val;
+ *str_disk_guid = strdup(p);
+ free(val);
+ /* Move s to first partition */
+ strsep(&s, ";");
+ }
+ if (s == NULL) {
+ printf("Error: is the partitions string NULL-terminated?\n");
+ return -EINVAL;
+ }
+ if (strnlen(s, max_str_part) == 0)
+ return -3;
+
+ i = strnlen(s, max_str_part) - 1;
+ if (s[i] == ';')
+ s[i] = '\0';
+
+ /* calculate expected number of partitions */
+ p_count = 1;
+ p = s;
+ while (*p) {
+ if (*p++ == ';')
+ p_count++;
+ }
+
+ /* allocate memory for partitions */
+ parts = calloc(sizeof(struct disk_partition), p_count);
+ if (parts == NULL)
+ return -ENOMEM;
+
+ /* retrieve partitions data from string */
+ for (i = 0; i < p_count; i++) {
+ tok = strsep(&s, ";");
+
+ if (tok == NULL)
+ break;
+
+ /* uuid */
+ val = extract_val(tok, "uuid");
+ if (!val) {
+ /* 'uuid' is optional if random uuid's are enabled */
+#ifdef CONFIG_RANDOM_UUID
+ gen_rand_uuid_str(parts[i].uuid, UUID_STR_FORMAT_STD);
+#else
+ errno = -4;
+ goto err;
+#endif
+ } else {
+ if (extract_env(val, &p))
+ p = val;
+ if (strnlen(p, max_str_part) >= sizeof(parts[i].uuid)) {
+ printf("Wrong uuid format for partition %d\n", i);
+ errno = -4;
+ goto err;
+ }
+ strncpy((char *)parts[i].uuid, p, max_str_part);
+ free(val);
+ }
+#ifdef CONFIG_PARTITION_TYPE_GUID
+ /* guid */
+ val = extract_val(tok, "type");
+ if (val) {
+ /* 'type' is optional */
+ if (extract_env(val, &p))
+ p = val;
+ if (strnlen(p, max_str_part) >= sizeof(parts[i].type_guid)) {
+ printf("Wrong type guid format for partition %d\n",
+ i);
+ errno = -4;
+ goto err;
+ }
+ strncpy((char *)parts[i].type_guid, p, max_str_part);
+ free(val);
+ }
+#endif
+ /* name */
+ val = extract_val(tok, "name");
+ if (!val) { /* name is mandatory */
+ errno = -4;
+ goto err;
+ }
+ if (extract_env(val, &p))
+ p = val;
+ if (strnlen(p, max_str_part) >= sizeof(parts[i].name)) {
+ errno = -4;
+ goto err;
+ }
+ strncpy((char *)parts[i].name, p, max_str_part);
+ free(val);
+
+ /* size */
+ val = extract_val(tok, "size");
+ if (!val) { /* 'size' is mandatory */
+ errno = -4;
+ goto err;
+ }
+ if (extract_env(val, &p))
+ p = val;
+ if ((strcmp(p, "-") == 0)) {
+ /* Let part efi module to auto extend the size */
+ parts[i].size = 0;
+ } else {
+ size_ll = ustrtoull(p, &p, 0);
+ parts[i].size = lldiv(size_ll, dev_desc->blksz);
+ }
+
+ free(val);
+
+ /* start address */
+ val = extract_val(tok, "start");
+ if (val) { /* start address is optional */
+ if (extract_env(val, &p))
+ p = val;
+ start_ll = ustrtoull(p, &p, 0);
+ parts[i].start = lldiv(start_ll, dev_desc->blksz);
+ free(val);
+ }
+
+ offset += parts[i].size + parts[i].start;
+
+ /* bootable */
+ if (found_key(tok, "bootable"))
+ parts[i].bootable = PART_BOOTABLE;
+ }
+
+ *parts_count = p_count;
+ *partitions = parts;
+ free(str);
+
+ return 0;
+err:
+ free(str);
+ free(*str_disk_guid);
+ free(parts);
+
+ return errno;
+}
+
+static int gpt_repair(struct blk_desc *blk_dev_desc)
+{
+ int ret = 0;
+
+ ret = gpt_repair_headers(blk_dev_desc);
+
+ return ret;
+}
+
+static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part)
+{
+ int ret;
+ char *str_disk_guid;
+ u8 part_count = 0;
+ struct disk_partition *partitions = NULL;
+
+ /* fill partitions */
+ ret = set_gpt_info(blk_dev_desc, str_part,
+ &str_disk_guid, &partitions, &part_count);
+ if (ret) {
+ if (ret == -1)
+ printf("No partition list provided\n");
+ if (ret == -2)
+ printf("Missing disk guid\n");
+ if ((ret == -3) || (ret == -4))
+ printf("Partition list incomplete\n");
+ return -1;
+ }
+
+ /* save partitions layout to disk */
+ ret = gpt_restore(blk_dev_desc, str_disk_guid, partitions, part_count);
+ free(str_disk_guid);
+ free(partitions);
+
+ /* initialize partition table */
+ if (blk_enabled())
+ part_init(blk_dev_desc);
+
+ return ret;
+}
+
+static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part)
+{
+ ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
+ blk_dev_desc->blksz);
+ struct disk_partition *partitions = NULL;
+ gpt_entry *gpt_pte = NULL;
+ char *str_disk_guid;
+ u8 part_count = 0;
+ int ret = 0;
+
+ /* fill partitions */
+ ret = set_gpt_info(blk_dev_desc, str_part,
+ &str_disk_guid, &partitions, &part_count);
+ if (ret) {
+ if (ret == -1) {
+ printf("No partition list provided - only basic check\n");
+ ret = gpt_verify_headers(blk_dev_desc, gpt_head,
+ &gpt_pte);
+ goto out;
+ }
+ if (ret == -2)
+ printf("Missing disk guid\n");
+ if ((ret == -3) || (ret == -4))
+ printf("Partition list incomplete\n");
+ return -1;
+ }
+
+ /* Check partition layout with provided pattern */
+ ret = gpt_verify_partitions(blk_dev_desc, partitions, part_count,
+ gpt_head, &gpt_pte);
+ free(str_disk_guid);
+ free(partitions);
+ out:
+ if (!ret)
+ free(gpt_pte);
+ return ret;
+}
+
+/**
+ * gpt_enumerate() - Enumerate partition names into environment variable.
+ *
+ * Enumerate partition names. Partition names are stored in gpt_partition_list
+ * environment variable. Each partition name is delimited by space.
+ *
+ * @desc: block device descriptor
+ *
+ * @Return: '0' on success and -ve error on failure
+ */
+static int gpt_enumerate(struct blk_desc *desc)
+{
+ struct part_driver *first_drv, *part_drv;
+ int str_len = 0, tmp_len;
+ char part_list[2048];
+ int n_drvs;
+ char *ptr;
+
+ part_list[0] = 0;
+ n_drvs = part_driver_get_count();
+ if (!n_drvs) {
+ printf("Failed to get partition driver count\n");
+ return -ENOENT;
+ }
+
+ first_drv = part_driver_get_first();
+ for (part_drv = first_drv; part_drv != first_drv + n_drvs; part_drv++) {
+ struct disk_partition pinfo;
+ int ret;
+ int i;
+
+ if (part_drv->test(desc))
+ continue;
+
+ for (i = 1; i < part_drv->max_entries; i++) {
+ ret = part_drv->get_info(desc, i, &pinfo);
+ if (ret)
+ continue;
+
+ ptr = &part_list[str_len];
+ tmp_len = strlen((const char *)pinfo.name);
+ str_len += tmp_len;
+ /* +1 for space */
+ str_len++;
+ if (str_len > sizeof(part_list)) {
+ printf("Error insufficient memory\n");
+ return -ENOMEM;
+ }
+ strcpy(ptr, (const char *)pinfo.name);
+ /* One byte for space(" ") delimiter */
+ ptr[tmp_len] = ' ';
+ }
+ if (*part_list)
+ part_list[strlen(part_list) - 1] = 0;
+ break;
+ }
+ debug("setenv gpt_partition_list %s\n", part_list);
+
+ return env_set("gpt_partition_list", part_list);
+}
+
+/**
+ * gpt_setenv_part_variables() - setup partition environmental variables
+ *
+ * Setup the gpt_partition_name, gpt_partition_entry, gpt_partition_addr
+ * and gpt_partition_size, gpt_partition_bootable environment variables.
+ *
+ * @pinfo: pointer to disk partition
+ * @i: partition entry
+ *
+ * @Return: '0' on success and -ENOENT on failure
+ */
+static int gpt_setenv_part_variables(struct disk_partition *pinfo, int i)
+{
+ int ret;
+
+ ret = env_set_hex("gpt_partition_addr", pinfo->start);
+ if (ret)
+ goto fail;
+
+ ret = env_set_hex("gpt_partition_size", pinfo->size);
+ if (ret)
+ goto fail;
+
+ ret = env_set_hex("gpt_partition_entry", i);
+ if (ret)
+ goto fail;
+
+ ret = env_set("gpt_partition_name", (const char *)pinfo->name);
+ if (ret)
+ goto fail;
+
+ ret = env_set_ulong("gpt_partition_bootable", !!(pinfo->bootable & PART_BOOTABLE));
+ if (ret)
+ goto fail;
+
+ return 0;
+
+fail:
+ return -ENOENT;
+}
+
+/**
+ * gpt_setenv() - Dynamically setup environment variables.
+ *
+ * Dynamically setup environment variables for name, index, offset and size
+ * for partition in GPT table after running "gpt setenv" for a partition name.
+ *
+ * @desc: block device descriptor
+ * @name: partition name
+ *
+ * @Return: '0' on success and -ve err on failure
+ */
+static int gpt_setenv(struct blk_desc *desc, const char *name)
+{
+ struct part_driver *first_drv, *part_drv;
+ int n_drvs;
+ int ret = -1;
+
+ n_drvs = part_driver_get_count();
+ if (!n_drvs) {
+ printf("Failed to get partition driver count\n");
+ goto fail;
+ }
+
+ first_drv = part_driver_get_first();
+ for (part_drv = first_drv; part_drv != first_drv + n_drvs; part_drv++) {
+ struct disk_partition pinfo;
+ int i;
+
+ for (i = 1; i < part_drv->max_entries; i++) {
+ ret = part_drv->get_info(desc, i, &pinfo);
+ if (ret)
+ continue;
+
+ if (!strcmp(name, (const char *)pinfo.name)) {
+ /* match found, setup environment variables */
+ ret = gpt_setenv_part_variables(&pinfo, i);
+ if (ret)
+ goto fail;
+
+ return 0;
+ }
+ }
+ }
+
+fail:
+ return ret;
+}
+
+static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr)
+{
+ int ret;
+ char disk_guid[UUID_STR_LEN + 1];
+
+ ret = get_disk_guid(dev_desc, disk_guid);
+ if (ret < 0)
+ return CMD_RET_FAILURE;
+
+ if (namestr)
+ env_set(namestr, disk_guid);
+ else
+ printf("%s\n", disk_guid);
+
+ return ret;
+}
+
+#ifdef CONFIG_CMD_GPT_RENAME
+static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm,
+ char *name1, char *name2)
+{
+ struct list_head *pos;
+ struct disk_part *curr;
+ struct disk_partition *new_partitions = NULL;
+ char disk_guid[UUID_STR_LEN + 1];
+ char *partitions_list, *str_disk_guid = NULL;
+ u8 part_count = 0;
+ int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0;
+
+ if (!subcomm || !name1 || !name2 ||
+ (strcmp(subcomm, "swap") && strcmp(subcomm, "rename") &&
+ strcmp(subcomm, "transpose")))
+ return -EINVAL;
+
+ ret = get_disk_guid(dev_desc, disk_guid);
+ if (ret < 0)
+ return ret;
+ /*
+ * Allocates disk_partitions, requiring matching call to del_gpt_info()
+ * if successful.
+ */
+ numparts = get_gpt_info(dev_desc);
+ if (numparts <= 0)
+ return numparts ? numparts : -ENODEV;
+
+ partlistlen = calc_parts_list_len(numparts);
+ partitions_list = malloc(partlistlen);
+ if (!partitions_list) {
+ del_gpt_info();
+ return -ENOMEM;
+ }
+ memset(partitions_list, '\0', partlistlen);
+
+ ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list);
+ if (ret < 0) {
+ free(partitions_list);
+ return ret;
+ }
+ /*
+ * Uncomment the following line to print a string that 'gpt write'
+ * or 'gpt verify' will accept as input.
+ */
+ debug("OLD partitions_list is %s with %u chars\n", partitions_list,
+ (unsigned)strlen(partitions_list));
+
+ /* set_gpt_info allocates new_partitions and str_disk_guid */
+ ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid,
+ &new_partitions, &part_count);
+ if (ret < 0)
+ goto out;
+
+ if (!strcmp(subcomm, "swap")) {
+ if ((strlen(name1) >= PART_NAME_LEN) || (strlen(name2) >= PART_NAME_LEN)) {
+ printf("Names longer than %d characters are truncated.\n",
+ PART_NAME_LEN - 1);
+ ret = -EINVAL;
+ goto out;
+ }
+ list_for_each(pos, &disk_partitions) {
+ curr = list_entry(pos, struct disk_part, list);
+ if (!strcmp((char *)curr->gpt_part_info.name, name1)) {
+ strcpy((char *)curr->gpt_part_info.name, name2);
+ ctr1++;
+ } else if (!strcmp((char *)curr->gpt_part_info.name, name2)) {
+ strcpy((char *)curr->gpt_part_info.name, name1);
+ ctr2++;
+ }
+ }
+ if ((ctr1 + ctr2 < 2) || (ctr1 != ctr2)) {
+ printf("Cannot swap partition names except in pairs.\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ } else if (!strcmp(subcomm, "transpose")) {
+ int idx1, idx2;
+ struct disk_partition* first = NULL;
+ struct disk_partition* second= NULL;
+ struct disk_partition tmp_part;
+
+ idx1 = simple_strtoul(name1, NULL, 10);
+ idx2 = simple_strtoul(name2, NULL, 10);
+ if (idx1 == idx2) {
+ printf("Cannot swap partition with itself\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ list_for_each(pos, &disk_partitions) {
+ curr = list_entry(pos, struct disk_part, list);
+ if (curr->partnum == idx1)
+ first = &curr->gpt_part_info;
+ else if (curr->partnum == idx2)
+ second = &curr->gpt_part_info;
+ }
+ if (!first) {
+ printf("Illegal partition number %s\n", name1);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!second) {
+ printf("Illegal partition number %s\n", name2);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ tmp_part = *first;
+ *first = *second;
+ *second = tmp_part;
+ } else { /* rename */
+ if (strlen(name2) >= PART_NAME_LEN) {
+ printf("Names longer than %d characters are truncated.\n",
+ PART_NAME_LEN - 1);
+ ret = -EINVAL;
+ goto out;
+ }
+ partnum = (int)simple_strtol(name1, NULL, 10);
+ if ((partnum < 0) || (partnum > numparts)) {
+ printf("Illegal partition number %s\n", name1);
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = part_get_info(dev_desc, partnum, new_partitions);
+ if (ret < 0)
+ goto out;
+
+ /* U-Boot partition numbering starts at 1 */
+ list_for_each(pos, &disk_partitions) {
+ curr = list_entry(pos, struct disk_part, list);
+ if (i == partnum) {
+ strcpy((char *)curr->gpt_part_info.name, name2);
+ break;
+ }
+ i++;
+ }
+ }
+
+ ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list);
+ if (ret < 0)
+ goto out;
+ debug("NEW partitions_list is %s with %u chars\n", partitions_list,
+ (unsigned)strlen(partitions_list));
+
+ ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid,
+ &new_partitions, &part_count);
+ /*
+ * Even though valid pointers are here passed into set_gpt_info(),
+ * it mallocs again, and there's no way to tell which failed.
+ */
+ if (ret < 0)
+ goto out;
+
+ debug("Writing new partition table\n");
+ ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts);
+ if (ret < 0) {
+ printf("Writing new partition table failed\n");
+ goto out;
+ }
+
+ debug("Reading back new partition table\n");
+ /*
+ * Empty the existing disk_partitions list, as otherwise the memory in
+ * the original list is unreachable.
+ */
+ del_gpt_info();
+ numparts = get_gpt_info(dev_desc);
+ if (numparts <= 0) {
+ ret = numparts ? numparts : -ENODEV;
+ goto out;
+ }
+ printf("new partition table with %d partitions is:\n", numparts);
+ print_gpt_info();
+ out:
+ del_gpt_info();
+#ifdef CONFIG_RANDOM_UUID
+ free(str_disk_guid);
+#endif
+ free(new_partitions);
+ free(partitions_list);
+ return ret;
+}
+
+/**
+ * gpt_set_bootable() - Set bootable flags for partitions
+ *
+ * Sets the bootable flag for any partition names in the comma separated list of
+ * partition names. Any partitions not in the list have their bootable flag
+ * cleared
+ *
+ * @desc: block device descriptor
+ * @name: Comma separated list of partition names
+ *
+ * @Return: '0' on success and -ve error on failure
+ */
+static int gpt_set_bootable(struct blk_desc *blk_dev_desc, char *const part_list)
+{
+ char *name;
+ char disk_guid[UUID_STR_LEN + 1];
+ struct list_head *pos;
+ struct disk_part *curr;
+ struct disk_partition *partitions = NULL;
+ int part_count = 0;
+ int ret = get_disk_guid(blk_dev_desc, disk_guid);
+
+ if (ret < 0)
+ return ret;
+
+ ret = get_gpt_info(blk_dev_desc);
+ if (ret <= 0)
+ goto out;
+
+ part_count = ret;
+ partitions = malloc(sizeof(*partitions) * part_count);
+ if (!partitions) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Copy partitions and clear bootable flag */
+ part_count = 0;
+ list_for_each(pos, &disk_partitions) {
+ curr = list_entry(pos, struct disk_part, list);
+ partitions[part_count] = curr->gpt_part_info;
+ partitions[part_count].bootable &= ~PART_BOOTABLE;
+ part_count++;
+ }
+
+ name = strtok(part_list, ",");
+ while (name) {
+ bool found = false;
+
+ for (int i = 0; i < part_count; i++) {
+ if (strcmp((char *)partitions[i].name, name) == 0) {
+ partitions[i].bootable |= PART_BOOTABLE;
+ found = true;
+ }
+ }
+
+ if (!found) {
+ printf("Warning: No partition matching '%s' found\n",
+ name);
+ }
+
+ name = strtok(NULL, ",");
+ }
+
+ ret = gpt_restore(blk_dev_desc, disk_guid, partitions, part_count);
+
+out:
+ del_gpt_info();
+
+ if (partitions)
+ free(partitions);
+
+ return ret;
+}
+#endif
+
+/**
+ * do_gpt(): Perform GPT operations
+ *
+ * @param cmdtp - command name
+ * @param flag
+ * @param argc
+ * @param argv
+ *
+ * Return: zero on success; otherwise error
+ */
+static int do_gpt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int ret = CMD_RET_SUCCESS;
+ int dev = 0;
+ char *ep;
+ struct blk_desc *blk_dev_desc = NULL;
+
+#ifndef CONFIG_CMD_GPT_RENAME
+ if (argc < 4 || argc > 5)
+#else
+ if (argc < 4 || argc > 6)
+#endif
+ return CMD_RET_USAGE;
+
+ dev = (int)dectoul(argv[3], &ep);
+ if (!ep || ep[0] != '\0') {
+ printf("'%s' is not a number\n", argv[3]);
+ return CMD_RET_USAGE;
+ }
+ blk_dev_desc = blk_get_dev(argv[2], dev);
+ if (!blk_dev_desc) {
+ printf("%s: %s dev %d NOT available\n",
+ __func__, argv[2], dev);
+ return CMD_RET_FAILURE;
+ }
+
+ if (strcmp(argv[1], "repair") == 0) {
+ printf("Repairing GPT: ");
+ ret = gpt_repair(blk_dev_desc);
+ } else if ((strcmp(argv[1], "write") == 0) && (argc == 5)) {
+ printf("Writing GPT: ");
+ ret = gpt_default(blk_dev_desc, argv[4]);
+ } else if ((strcmp(argv[1], "verify") == 0)) {
+ ret = gpt_verify(blk_dev_desc, argv[4]);
+ printf("Verify GPT: ");
+ } else if ((strcmp(argv[1], "setenv") == 0)) {
+ ret = gpt_setenv(blk_dev_desc, argv[4]);
+ } else if ((strcmp(argv[1], "enumerate") == 0)) {
+ ret = gpt_enumerate(blk_dev_desc);
+ } else if (strcmp(argv[1], "guid") == 0) {
+ ret = do_disk_guid(blk_dev_desc, argv[4]);
+#ifdef CONFIG_CMD_GPT_RENAME
+ } else if (strcmp(argv[1], "read") == 0) {
+ ret = do_get_gpt_info(blk_dev_desc, (argc == 5) ? argv[4] : NULL);
+ } else if ((strcmp(argv[1], "swap") == 0) ||
+ (strcmp(argv[1], "rename") == 0) ||
+ (strcmp(argv[1], "transpose") == 0)) {
+ ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]);
+ } else if ((strcmp(argv[1], "set-bootable") == 0)) {
+ ret = gpt_set_bootable(blk_dev_desc, argv[4]);
+#endif
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ if (ret) {
+ printf("error!\n");
+ return CMD_RET_FAILURE;
+ }
+
+ printf("success!\n");
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt,
+ "GUID Partition Table",
+ "<command> <interface> <dev> <partitions_list>\n"
+ " - GUID partition table restoration and validity check\n"
+ " Restore or verify GPT information on a device connected\n"
+ " to interface\n"
+ " Example usage:\n"
+ " gpt repair mmc 0\n"
+ " - repair the GPT on the device\n"
+ " gpt write mmc 0 $partitions\n"
+ " - write the GPT to device\n"
+ " gpt verify mmc 0 $partitions\n"
+ " - verify the GPT on device against $partitions\n"
+ " gpt setenv mmc 0 $name\n"
+ " - setup environment variables for partition $name:\n"
+ " gpt_partition_addr, gpt_partition_size,\n"
+ " gpt_partition_name, gpt_partition_entry,\n"
+ " gpt_partition_bootable\n"
+ " gpt enumerate mmc 0\n"
+ " - store list of partitions to gpt_partition_list environment variable\n"
+ " gpt guid <interface> <dev>\n"
+ " - print disk GUID\n"
+ " gpt guid <interface> <dev> <varname>\n"
+ " - set environment variable to disk GUID\n"
+ " Example usage:\n"
+ " gpt guid mmc 0\n"
+ " gpt guid mmc 0 varname\n"
+#ifdef CONFIG_CMD_GPT_RENAME
+ "gpt partition renaming commands:\n"
+ " gpt read <interface> <dev> [<varname>]\n"
+ " - read GPT into a data structure for manipulation\n"
+ " - read GPT partitions into environment variable\n"
+ " gpt swap <interface> <dev> <name1> <name2>\n"
+ " - change all partitions named name1 to name2\n"
+ " and vice-versa\n"
+ " gpt transpose <interface> <dev> <part1> <part2>\n"
+ " - Swap the order of the entries for part1 and part2 in the partition table\n"
+ " gpt rename <interface> <dev> <part> <name>\n"
+ " - rename the specified partition\n"
+ " gpt set-bootable <interface> <dev> <list>\n"
+ " - make partition names in list bootable\n"
+ " Example usage:\n"
+ " gpt swap mmc 0 foo bar\n"
+ " gpt rename mmc 0 3 foo\n"
+ " gpt set-bootable mmc 0 boot_a,boot_b\n"
+ " gpt transpose mmc 0 1 2\n"
+#endif
+);
diff --git a/cmd/hash.c b/cmd/hash.c
new file mode 100644
index 00000000000..96d0e443a5b
--- /dev/null
+++ b/cmd/hash.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2012 The Chromium OS Authors.
+ *
+ * (C) Copyright 2011
+ * Joe Hershberger, National Instruments, joe.hershberger@ni.com
+ *
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <env.h>
+#include <hash.h>
+#include <linux/ctype.h>
+
+#if IS_ENABLED(CONFIG_HASH_VERIFY)
+#define HARGS 6
+#else
+#define HARGS 5
+#endif
+
+static int do_hash(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *s;
+ int flags = HASH_FLAG_ENV;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+#if IS_ENABLED(CONFIG_HASH_VERIFY)
+ if (!strcmp(argv[1], "-v")) {
+ flags |= HASH_FLAG_VERIFY;
+ argc--;
+ argv++;
+ }
+#endif
+ /* Move forward to 'algorithm' parameter */
+ argc--;
+ argv++;
+ for (s = *argv; *s; s++)
+ *s = tolower(*s);
+ return hash_command(*argv, flags, cmdtp, flag, argc - 1, argv + 1);
+}
+
+U_BOOT_CMD(
+ hash, HARGS, 1, do_hash,
+ "compute hash message digest",
+ "algorithm address count [[*]hash_dest]\n"
+ " - compute message digest [save to env var / *address]"
+#if IS_ENABLED(CONFIG_HASH_VERIFY)
+ "\nhash -v algorithm address count [*]hash\n"
+ " - verify message digest of memory area to immediate value, \n"
+ " env var or *address"
+#endif
+);
diff --git a/cmd/help.c b/cmd/help.c
new file mode 100644
index 00000000000..1be83ba607d
--- /dev/null
+++ b/cmd/help.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2000-2009
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+
+static int do_help(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *start = ll_entry_start(struct cmd_tbl, cmd);
+ const int len = ll_entry_count(struct cmd_tbl, cmd);
+ return _do_help(start, len, cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(
+ help, CONFIG_SYS_MAXARGS, 1, do_help,
+ "print command description/usage",
+ "\n"
+ " - print brief description of all commands\n"
+ "help command ...\n"
+ " - print detailed usage of 'command'"
+);
+
+/*
+ * This does not use the U_BOOT_CMD macro as ? can't be used in symbol names
+ * nor can we rely on the CONFIG_SYS_LONGHELP helper macro
+ */
+ll_entry_declare(struct cmd_tbl, question_mark, cmd) = {
+ "?", CONFIG_SYS_MAXARGS, cmd_always_repeatable, do_help,
+ "alias for 'help'",
+#ifdef CONFIG_SYS_LONGHELP
+ ""
+#endif /* CONFIG_SYS_LONGHELP */
+};
diff --git a/cmd/history.c b/cmd/history.c
new file mode 100644
index 00000000000..8972986ca9d
--- /dev/null
+++ b/cmd/history.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <command.h>
+#include <cli.h>
+
+static int do_history(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ cread_print_hist_list();
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ history, CONFIG_SYS_MAXARGS, 1, do_history,
+ "print command history",
+ ""
+);
diff --git a/cmd/host.c b/cmd/host.c
new file mode 100644
index 00000000000..e03576b4d2d
--- /dev/null
+++ b/cmd/host.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2012, Google Inc.
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <fs.h>
+#include <part.h>
+#include <sandbox_host.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <linux/errno.h>
+#include <linux/log2.h>
+
+static int do_host_load(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_load(cmdtp, flag, argc, argv, FS_TYPE_SANDBOX);
+}
+
+static int do_host_ls(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_ls(cmdtp, flag, argc, argv, FS_TYPE_SANDBOX);
+}
+
+static int do_host_size(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_size(cmdtp, flag, argc, argv, FS_TYPE_SANDBOX);
+}
+
+static int do_host_save(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_save(cmdtp, flag, argc, argv, FS_TYPE_SANDBOX);
+}
+
+static int do_host_bind(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool removable = false;
+ struct udevice *dev;
+ const char *label;
+ char *file;
+ unsigned long blksz = DEFAULT_BLKSZ;
+ int ret;
+
+ /* Skip 'bind' */
+ argc--;
+ argv++;
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (!strcmp(argv[0], "-r")) {
+ removable = true;
+ argc--;
+ argv++;
+ }
+
+ if (argc < 2 || argc > 3)
+ return CMD_RET_USAGE;
+ label = argv[0];
+ file = argv[1];
+ if (argc > 2) {
+ blksz = dectoul(argv[2], NULL);
+ if (blksz < DEFAULT_BLKSZ || !is_power_of_2(blksz)) {
+ printf("blksz must be >= 512 and power of 2\n");
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ ret = host_create_attach_file(label, file, removable, blksz, &dev);
+ if (ret) {
+ printf("Cannot create device / bind file\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+/**
+ * parse_host_label() - Parse a device label or sequence number
+ *
+ * This shows an error if it returns NULL
+ *
+ * @label: String containing the label or sequence number
+ * Returns: Associated device, or NULL if not found
+ */
+static struct udevice *parse_host_label(const char *label)
+{
+ struct udevice *dev;
+
+ dev = host_find_by_label(label);
+ if (!dev) {
+ int devnum;
+ char *ep;
+
+ devnum = hextoul(label, &ep);
+ if (*ep ||
+ uclass_find_device_by_seq(UCLASS_HOST, devnum, &dev)) {
+ printf("No such device '%s'\n", label);
+ return NULL;
+ }
+ }
+
+ return dev;
+}
+
+static int do_host_unbind(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ const char *label;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ label = argv[1];
+ dev = parse_host_label(label);
+ if (!dev)
+ return CMD_RET_FAILURE;
+
+ ret = host_detach_file(dev);
+ if (ret) {
+ printf("Cannot detach file (err=%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ ret = device_unbind(dev);
+ if (ret) {
+ printf("Cannot attach file\n");
+ ret = device_unbind(dev);
+ if (ret)
+ printf("Cannot unbind device '%s'\n", dev->name);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static void show_host_dev(struct udevice *dev)
+{
+ struct host_sb_plat *plat = dev_get_plat(dev);
+ struct blk_desc *desc;
+ struct udevice *blk;
+ int ret;
+
+ printf("%3d ", dev_seq(dev));
+ if (!plat->fd) {
+ printf("Not bound to a backing file\n");
+ return;
+ }
+ ret = blk_get_from_parent(dev, &blk);
+ if (ret) /* cannot happen */
+ return;
+
+ desc = dev_get_uclass_plat(blk);
+ printf("%12lu %6lu %-15s %s\n", (unsigned long)desc->lba, desc->blksz,
+ plat->label, plat->filename);
+}
+
+static int do_host_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+
+ if (argc < 1)
+ return CMD_RET_USAGE;
+
+ dev = NULL;
+ if (argc >= 2) {
+ dev = parse_host_label(argv[1]);
+ if (!dev)
+ return CMD_RET_FAILURE;
+ }
+
+ printf("%3s %12s %6s %-15s %s\n",
+ "dev", "blocks", "blksz", "label", "path");
+ if (dev) {
+ show_host_dev(dev);
+ } else {
+ struct uclass *uc;
+
+ uclass_id_foreach_dev(UCLASS_HOST, dev, uc)
+ show_host_dev(dev);
+ }
+
+ return 0;
+}
+
+static int do_host_dev(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ const char *label;
+
+ if (argc < 1 || argc > 3)
+ return CMD_RET_USAGE;
+
+ if (argc == 1) {
+ struct host_sb_plat *plat;
+
+ dev = host_get_cur_dev();
+ if (!dev) {
+ printf("No current host device\n");
+ return CMD_RET_FAILURE;
+ }
+ plat = dev_get_plat(dev);
+ printf("Current host device: %d: %s\n", dev_seq(dev),
+ plat->label);
+ return 0;
+ }
+
+ label = argv[1];
+ dev = parse_host_label(argv[1]);
+ if (!dev)
+ return CMD_RET_FAILURE;
+
+ host_set_cur_dev(dev);
+
+ return 0;
+}
+
+static struct cmd_tbl cmd_host_sub[] = {
+ U_BOOT_CMD_MKENT(load, 7, 0, do_host_load, "", ""),
+ U_BOOT_CMD_MKENT(ls, 3, 0, do_host_ls, "", ""),
+ U_BOOT_CMD_MKENT(save, 6, 0, do_host_save, "", ""),
+ U_BOOT_CMD_MKENT(size, 3, 0, do_host_size, "", ""),
+ U_BOOT_CMD_MKENT(bind, 4, 0, do_host_bind, "", ""),
+ U_BOOT_CMD_MKENT(unbind, 4, 0, do_host_unbind, "", ""),
+ U_BOOT_CMD_MKENT(info, 3, 0, do_host_info, "", ""),
+ U_BOOT_CMD_MKENT(dev, 0, 1, do_host_dev, "", ""),
+};
+
+static int do_host(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *c;
+
+ /* Skip past 'host' */
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], cmd_host_sub, ARRAY_SIZE(cmd_host_sub));
+
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+ else
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+ host, 8, 1, do_host,
+ "Miscellaneous host commands",
+ "load hostfs - <addr> <filename> [<bytes> <offset>] - "
+ "load a file from host\n"
+ "host ls hostfs - <filename> - list files on host\n"
+ "host save hostfs - <addr> <filename> <bytes> [<offset>] - "
+ "save a file to host\n"
+ "host size hostfs - <filename> - determine size of file on host\n"
+ "host bind [-r] <label> <filename> [<blksz>] - bind \"host\" device to file,\n"
+ " and optionally set the device's logical block size\n"
+ " -r = mark as removable\n"
+ "host unbind <label> - unbind file from \"host\" device\n"
+ "host info [<label>] - show device binding & info\n"
+ "host dev [<label>] - set or retrieve the current host device\n"
+ "host commands use the \"hostfs\" device. The \"host\" device is used\n"
+ "with standard IO commands such as fatls or ext2load"
+);
diff --git a/cmd/i2c.c b/cmd/i2c.c
new file mode 100644
index 00000000000..e021067e68a
--- /dev/null
+++ b/cmd/i2c.c
@@ -0,0 +1,1984 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2009
+ * Sergey Kubushyn, himself, ksi@koi8.net
+ *
+ * Changes for unified multibus/multiadapter I2C support.
+ *
+ * (C) Copyright 2001
+ * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
+ */
+
+/*
+ * I2C Functions similar to the standard memory functions.
+ *
+ * There are several parameters in many of the commands that bear further
+ * explanations:
+ *
+ * {i2c_chip} is the I2C chip address (the first byte sent on the bus).
+ * Each I2C chip on the bus has a unique address. On the I2C data bus,
+ * the address is the upper seven bits and the LSB is the "read/write"
+ * bit. Note that the {i2c_chip} address specified on the command
+ * line is not shifted up: e.g. a typical EEPROM memory chip may have
+ * an I2C address of 0x50, but the data put on the bus will be 0xA0
+ * for write and 0xA1 for read. This "non shifted" address notation
+ * matches at least half of the data sheets :-/.
+ *
+ * {addr} is the address (or offset) within the chip. Small memory
+ * chips have 8 bit addresses. Large memory chips have 16 bit
+ * addresses. Other memory chips have 9, 10, or 11 bit addresses.
+ * Many non-memory chips have multiple registers and {addr} is used
+ * as the register index. Some non-memory chips have only one register
+ * and therefore don't need any {addr} parameter.
+ *
+ * The default {addr} parameter is one byte (.1) which works well for
+ * memories and registers with 8 bits of address space.
+ *
+ * You can specify the length of the {addr} field with the optional .0,
+ * .1, or .2 modifier (similar to the .b, .w, .l modifier). If you are
+ * manipulating a single register device which doesn't use an address
+ * field, use "0.0" for the address and the ".0" length field will
+ * suppress the address in the I2C data stream. This also works for
+ * successive reads using the I2C auto-incrementing memory pointer.
+ *
+ * If you are manipulating a large memory with 2-byte addresses, use
+ * the .2 address modifier, e.g. 210.2 addresses location 528 (decimal).
+ *
+ * Then there are the unfortunate memory chips that spill the most
+ * significant 1, 2, or 3 bits of address into the chip address byte.
+ * This effectively makes one chip (logically) look like 2, 4, or
+ * 8 chips. This is handled (awkwardly) by #defining
+ * CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW and using the .1 modifier on the
+ * {addr} field (since .1 is the default, it doesn't actually have to
+ * be specified). Examples: given a memory chip at I2C chip address
+ * 0x50, the following would happen...
+ * i2c md 50 0 10 display 16 bytes starting at 0x000
+ * On the bus: <S> A0 00 <E> <S> A1 <rd> ... <rd>
+ * i2c md 50 100 10 display 16 bytes starting at 0x100
+ * On the bus: <S> A2 00 <E> <S> A3 <rd> ... <rd>
+ * i2c md 50 210 10 display 16 bytes starting at 0x210
+ * On the bus: <S> A4 10 <E> <S> A5 <rd> ... <rd>
+ * This is awfully ugly. It would be nice if someone would think up
+ * a better way of handling this.
+ *
+ * Adapted from cmd_mem.c which is copyright Wolfgang Denk (wd@denx.de).
+ */
+
+#include <bootretry.h>
+#include <cli.h>
+#include <command.h>
+#include <console.h>
+#include <dm.h>
+#include <edid.h>
+#include <errno.h>
+#include <i2c.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <u-boot/crc.h>
+
+/* Display values from last command.
+ * Memory modify remembered values are different from display memory.
+ */
+static uint i2c_dp_last_chip;
+static uint i2c_dp_last_addr;
+static uint i2c_dp_last_alen;
+static uint i2c_dp_last_length = 0x10;
+
+static uint i2c_mm_last_chip;
+static uint i2c_mm_last_addr;
+static uint i2c_mm_last_alen;
+
+/* If only one I2C bus is present, the list of devices to ignore when
+ * the probe command is issued is represented by a 1D array of addresses.
+ * When multiple buses are present, the list is an array of bus-address
+ * pairs. The following macros take care of this */
+
+#if defined(CFG_SYS_I2C_NOPROBES)
+#if CONFIG_IS_ENABLED(SYS_I2C_LEGACY)
+static struct
+{
+ uchar bus;
+ uchar addr;
+} i2c_no_probes[] = CFG_SYS_I2C_NOPROBES;
+#define GET_BUS_NUM i2c_get_bus_num()
+#define COMPARE_BUS(b,i) (i2c_no_probes[(i)].bus == (b))
+#define COMPARE_ADDR(a,i) (i2c_no_probes[(i)].addr == (a))
+#define NO_PROBE_ADDR(i) i2c_no_probes[(i)].addr
+#else /* single bus */
+static uchar i2c_no_probes[] = CFG_SYS_I2C_NOPROBES;
+#define GET_BUS_NUM 0
+#define COMPARE_BUS(b,i) ((b) == 0) /* Make compiler happy */
+#define COMPARE_ADDR(a,i) (i2c_no_probes[(i)] == (a))
+#define NO_PROBE_ADDR(i) i2c_no_probes[(i)]
+#endif /* CONFIG_IS_ENABLED(SYS_I2C_LEGACY) */
+#endif
+
+#define DISP_LINE_LEN 16
+
+/*
+ * Default for driver model is to use the chip's existing address length.
+ * For legacy code, this is not stored, so we need to use a suitable
+ * default.
+ */
+#if CONFIG_IS_ENABLED(DM_I2C)
+#define DEFAULT_ADDR_LEN (-1)
+#else
+#define DEFAULT_ADDR_LEN 1
+#endif
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+static struct udevice *i2c_cur_bus;
+
+static int cmd_i2c_set_bus_num(unsigned int busnum)
+{
+ struct udevice *bus;
+ int ret;
+
+ ret = uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus);
+ if (ret) {
+ debug("%s: No bus %d\n", __func__, busnum);
+ return ret;
+ }
+ i2c_cur_bus = bus;
+
+ return 0;
+}
+
+static int i2c_get_cur_bus(struct udevice **busp)
+{
+#ifdef CONFIG_I2C_SET_DEFAULT_BUS_NUM
+ if (!i2c_cur_bus) {
+ if (cmd_i2c_set_bus_num(CONFIG_I2C_DEFAULT_BUS_NUMBER)) {
+ printf("Default I2C bus %d not found\n",
+ CONFIG_I2C_DEFAULT_BUS_NUMBER);
+ return -ENODEV;
+ }
+ }
+#endif
+
+ if (!i2c_cur_bus) {
+ puts("No I2C bus selected\n");
+ return -ENODEV;
+ }
+ *busp = i2c_cur_bus;
+
+ return 0;
+}
+
+static int i2c_get_cur_bus_chip(uint chip_addr, struct udevice **devp)
+{
+ struct udevice *bus;
+ int ret;
+
+ ret = i2c_get_cur_bus(&bus);
+ if (ret)
+ return ret;
+
+ return i2c_get_chip(bus, chip_addr, 1, devp);
+}
+
+#endif
+
+/**
+ * i2c_init_board() - Board-specific I2C bus init
+ *
+ * This function is the default no-op implementation of I2C bus
+ * initialization. This function can be overridden by board-specific
+ * implementation if needed.
+ */
+__weak
+void i2c_init_board(void)
+{
+}
+
+/**
+ * get_alen() - Small parser helper function to get address length
+ *
+ * Returns the address length.
+ */
+static uint get_alen(char *arg, int default_len)
+{
+ int j;
+ int alen;
+
+ alen = default_len;
+ for (j = 0; j < 8; j++) {
+ if (arg[j] == '.') {
+ alen = arg[j+1] - '0';
+ break;
+ } else if (arg[j] == '\0')
+ break;
+ }
+ return alen;
+}
+
+enum i2c_err_op {
+ I2C_ERR_READ,
+ I2C_ERR_WRITE,
+};
+
+static int i2c_report_err(int ret, enum i2c_err_op op)
+{
+ printf("Error %s the chip: %d\n",
+ op == I2C_ERR_READ ? "reading" : "writing", ret);
+
+ return CMD_RET_FAILURE;
+}
+
+/**
+ * do_i2c_read() - Handle the "i2c read" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ *
+ * Syntax:
+ * i2c read {i2c_chip} {devaddr}{.0, .1, .2} {len} {memaddr}
+ */
+static int do_i2c_read(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ uint chip;
+ uint devaddr, length;
+ int alen;
+ u_char *memaddr;
+ int ret;
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *dev;
+#endif
+
+ if (argc != 5)
+ return CMD_RET_USAGE;
+
+ /*
+ * I2C chip address
+ */
+ chip = hextoul(argv[1], NULL);
+
+ /*
+ * I2C data address within the chip. This can be 1 or
+ * 2 bytes long. Some day it might be 3 bytes long :-).
+ */
+ devaddr = hextoul(argv[2], NULL);
+ alen = get_alen(argv[2], DEFAULT_ADDR_LEN);
+ if (alen > 3)
+ return CMD_RET_USAGE;
+
+ /*
+ * Length is the number of objects, not number of bytes.
+ */
+ length = hextoul(argv[3], NULL);
+
+ /*
+ * memaddr is the address where to store things in memory
+ */
+ memaddr = (u_char *)hextoul(argv[4], NULL);
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = i2c_get_cur_bus_chip(chip, &dev);
+ if (!ret && alen != -1)
+ ret = i2c_set_chip_offset_len(dev, alen);
+ if (!ret)
+ ret = dm_i2c_read(dev, devaddr, memaddr, length);
+#else
+ ret = i2c_read(chip, devaddr, alen, memaddr, length);
+#endif
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_READ);
+
+ return 0;
+}
+
+static int do_i2c_write(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ uint chip;
+ uint devaddr;
+ int length;
+ int alen;
+ u_char *memaddr;
+ int ret;
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *dev;
+ struct dm_i2c_chip *i2c_chip;
+#endif
+
+ if ((argc < 5) || (argc > 6))
+ return cmd_usage(cmdtp);
+
+ /*
+ * memaddr is the address where to store things in memory
+ */
+ memaddr = (u_char *)hextoul(argv[1], NULL);
+
+ /*
+ * I2C chip address
+ */
+ chip = hextoul(argv[2], NULL);
+
+ /*
+ * I2C data address within the chip. This can be 1 or
+ * 2 bytes long. Some day it might be 3 bytes long :-).
+ */
+ devaddr = hextoul(argv[3], NULL);
+ alen = get_alen(argv[3], DEFAULT_ADDR_LEN);
+ if (alen > 3)
+ return cmd_usage(cmdtp);
+
+ /*
+ * Length is the number of bytes.
+ */
+ length = hextoul(argv[4], NULL);
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = i2c_get_cur_bus_chip(chip, &dev);
+ if (!ret && alen != -1)
+ ret = i2c_set_chip_offset_len(dev, alen);
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_WRITE);
+ i2c_chip = dev_get_parent_plat(dev);
+ if (!i2c_chip)
+ return i2c_report_err(ret, I2C_ERR_WRITE);
+#endif
+
+ if (argc == 6 && !strcmp(argv[5], "-s")) {
+ /*
+ * Write all bytes in a single I2C transaction. If the target
+ * device is an EEPROM, it is your responsibility to not cross
+ * a page boundary. No write delay upon completion, take this
+ * into account if linking commands.
+ */
+#if CONFIG_IS_ENABLED(DM_I2C)
+ i2c_chip->flags &= ~DM_I2C_CHIP_WR_ADDRESS;
+ ret = dm_i2c_write(dev, devaddr, memaddr, length);
+#else
+ ret = i2c_write(chip, devaddr, alen, memaddr, length);
+#endif
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_WRITE);
+ } else {
+ /*
+ * Repeated addressing - perform <length> separate
+ * write transactions of one byte each
+ */
+ while (length-- > 0) {
+#if CONFIG_IS_ENABLED(DM_I2C)
+ i2c_chip->flags |= DM_I2C_CHIP_WR_ADDRESS;
+ ret = dm_i2c_write(dev, devaddr++, memaddr++, 1);
+#else
+ ret = i2c_write(chip, devaddr++, alen, memaddr++, 1);
+#endif
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_WRITE);
+/*
+ * No write delay with FRAM devices.
+ */
+#if !defined(CONFIG_SYS_I2C_FRAM)
+ udelay(11000);
+#endif
+ }
+ }
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+static int do_i2c_flags(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ uint flags;
+ int chip;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ chip = hextoul(argv[1], NULL);
+ ret = i2c_get_cur_bus_chip(chip, &dev);
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_READ);
+
+ if (argc > 2) {
+ flags = hextoul(argv[2], NULL);
+ ret = i2c_set_chip_flags(dev, flags);
+ } else {
+ ret = i2c_get_chip_flags(dev, &flags);
+ if (!ret)
+ printf("%x\n", flags);
+ }
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_READ);
+
+ return 0;
+}
+
+static int do_i2c_olen(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ uint olen;
+ int chip;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ chip = hextoul(argv[1], NULL);
+ ret = i2c_get_cur_bus_chip(chip, &dev);
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_READ);
+
+ if (argc > 2) {
+ olen = hextoul(argv[2], NULL);
+ ret = i2c_set_chip_offset_len(dev, olen);
+ } else {
+ ret = i2c_get_chip_offset_len(dev);
+ if (ret >= 0) {
+ printf("%x\n", ret);
+ ret = 0;
+ }
+ }
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_READ);
+
+ return 0;
+}
+#endif
+
+/**
+ * do_i2c_md() - Handle the "i2c md" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ *
+ * Syntax:
+ * i2c md {i2c_chip} {addr}{.0, .1, .2} {len}
+ */
+static int do_i2c_md(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ uint chip;
+ uint addr, length;
+ int alen;
+ int j;
+ uint nbytes, linebytes;
+ int ret;
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *dev;
+#endif
+
+ /* We use the last specified parameters, unless new ones are
+ * entered.
+ */
+ chip = i2c_dp_last_chip;
+ addr = i2c_dp_last_addr;
+ alen = i2c_dp_last_alen;
+ length = i2c_dp_last_length;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ /*
+ * New command specified.
+ */
+
+ /*
+ * I2C chip address
+ */
+ chip = hextoul(argv[1], NULL);
+
+ /*
+ * I2C data address within the chip. This can be 1 or
+ * 2 bytes long. Some day it might be 3 bytes long :-).
+ */
+ addr = hextoul(argv[2], NULL);
+ alen = get_alen(argv[2], DEFAULT_ADDR_LEN);
+ if (alen > 3)
+ return CMD_RET_USAGE;
+
+ /*
+ * If another parameter, it is the length to display.
+ * Length is the number of objects, not number of bytes.
+ */
+ if (argc > 3)
+ length = hextoul(argv[3], NULL);
+ }
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = i2c_get_cur_bus_chip(chip, &dev);
+ if (!ret && alen != -1)
+ ret = i2c_set_chip_offset_len(dev, alen);
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_READ);
+#endif
+
+ /*
+ * Print the lines.
+ *
+ * We buffer all read data, so we can make sure data is read only
+ * once.
+ */
+ nbytes = length;
+ do {
+ unsigned char linebuf[DISP_LINE_LEN];
+ unsigned char *cp;
+
+ linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = dm_i2c_read(dev, addr, linebuf, linebytes);
+#else
+ ret = i2c_read(chip, addr, alen, linebuf, linebytes);
+#endif
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_READ);
+ else {
+ printf("%04x:", addr);
+ cp = linebuf;
+ for (j=0; j<linebytes; j++) {
+ printf(" %02x", *cp++);
+ addr++;
+ }
+ puts (" ");
+ cp = linebuf;
+ for (j=0; j<linebytes; j++) {
+ if ((*cp < 0x20) || (*cp > 0x7e))
+ puts (".");
+ else
+ printf("%c", *cp);
+ cp++;
+ }
+ putc ('\n');
+ }
+ nbytes -= linebytes;
+ } while (nbytes > 0);
+
+ i2c_dp_last_chip = chip;
+ i2c_dp_last_addr = addr;
+ i2c_dp_last_alen = alen;
+ i2c_dp_last_length = length;
+
+ return 0;
+}
+
+/**
+ * do_i2c_mw() - Handle the "i2c mw" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ *
+ * Syntax:
+ * i2c mw {i2c_chip} {addr}{.0, .1, .2} {data} [{count}]
+ */
+static int do_i2c_mw(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ uint chip;
+ ulong addr;
+ int alen;
+ uchar byte;
+ int count;
+ int ret;
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *dev;
+#endif
+
+ if ((argc < 4) || (argc > 5))
+ return CMD_RET_USAGE;
+
+ /*
+ * Chip is always specified.
+ */
+ chip = hextoul(argv[1], NULL);
+
+ /*
+ * Address is always specified.
+ */
+ addr = hextoul(argv[2], NULL);
+ alen = get_alen(argv[2], DEFAULT_ADDR_LEN);
+ if (alen > 3)
+ return CMD_RET_USAGE;
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = i2c_get_cur_bus_chip(chip, &dev);
+ if (!ret && alen != -1)
+ ret = i2c_set_chip_offset_len(dev, alen);
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_WRITE);
+#endif
+ /*
+ * Value to write is always specified.
+ */
+ byte = hextoul(argv[3], NULL);
+
+ /*
+ * Optional count
+ */
+ if (argc == 5)
+ count = hextoul(argv[4], NULL);
+ else
+ count = 1;
+
+ while (count-- > 0) {
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = dm_i2c_write(dev, addr++, &byte, 1);
+#else
+ ret = i2c_write(chip, addr++, alen, &byte, 1);
+#endif
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_WRITE);
+ /*
+ * Wait for the write to complete. The write can take
+ * up to 10mSec (we allow a little more time).
+ */
+/*
+ * No write delay with FRAM devices.
+ */
+#if !defined(CONFIG_SYS_I2C_FRAM)
+ udelay(11000);
+#endif
+ }
+
+ return 0;
+}
+
+/**
+ * do_i2c_crc() - Handle the "i2c crc32" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Calculate a CRC on memory
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ *
+ * Syntax:
+ * i2c crc32 {i2c_chip} {addr}{.0, .1, .2} {count}
+ */
+static int do_i2c_crc(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ uint chip;
+ ulong addr;
+ int alen;
+ int count;
+ uchar byte;
+ ulong crc;
+ ulong err;
+ int ret = 0;
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *dev;
+#endif
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ /*
+ * Chip is always specified.
+ */
+ chip = hextoul(argv[1], NULL);
+
+ /*
+ * Address is always specified.
+ */
+ addr = hextoul(argv[2], NULL);
+ alen = get_alen(argv[2], DEFAULT_ADDR_LEN);
+ if (alen > 3)
+ return CMD_RET_USAGE;
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = i2c_get_cur_bus_chip(chip, &dev);
+ if (!ret && alen != -1)
+ ret = i2c_set_chip_offset_len(dev, alen);
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_READ);
+#endif
+ /*
+ * Count is always specified
+ */
+ count = hextoul(argv[3], NULL);
+
+ printf ("CRC32 for %08lx ... %08lx ==> ", addr, addr + count - 1);
+ /*
+ * CRC a byte at a time. This is going to be slooow, but hey, the
+ * memories are small and slow too so hopefully nobody notices.
+ */
+ crc = 0;
+ err = 0;
+ while (count-- > 0) {
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = dm_i2c_read(dev, addr, &byte, 1);
+#else
+ ret = i2c_read(chip, addr, alen, &byte, 1);
+#endif
+ if (ret)
+ err++;
+ crc = crc32(crc, &byte, 1);
+ addr++;
+ }
+ if (err > 0)
+ i2c_report_err(ret, I2C_ERR_READ);
+ else
+ printf ("%08lx\n", crc);
+
+ return 0;
+}
+
+/**
+ * mod_i2c_mem() - Handle the "i2c mm" and "i2c nm" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Modify memory.
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ *
+ * Syntax:
+ * i2c mm{.b, .w, .l} {i2c_chip} {addr}{.0, .1, .2}
+ * i2c nm{.b, .w, .l} {i2c_chip} {addr}{.0, .1, .2}
+ */
+static int mod_i2c_mem(struct cmd_tbl *cmdtp, int incrflag, int flag, int argc,
+ char *const argv[])
+{
+ uint chip;
+ ulong addr;
+ int alen;
+ ulong data;
+ int size = 1;
+ int nbytes;
+ int ret;
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *dev;
+#endif
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ bootretry_reset_cmd_timeout(); /* got a good command to get here */
+ /*
+ * We use the last specified parameters, unless new ones are
+ * entered.
+ */
+ chip = i2c_mm_last_chip;
+ addr = i2c_mm_last_addr;
+ alen = i2c_mm_last_alen;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ /*
+ * New command specified. Check for a size specification.
+ * Defaults to byte if no or incorrect specification.
+ */
+ size = cmd_get_data_size(argv[0], 1);
+
+ /*
+ * Chip is always specified.
+ */
+ chip = hextoul(argv[1], NULL);
+
+ /*
+ * Address is always specified.
+ */
+ addr = hextoul(argv[2], NULL);
+ alen = get_alen(argv[2], DEFAULT_ADDR_LEN);
+ if (alen > 3)
+ return CMD_RET_USAGE;
+ }
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = i2c_get_cur_bus_chip(chip, &dev);
+ if (!ret && alen != -1)
+ ret = i2c_set_chip_offset_len(dev, alen);
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_WRITE);
+#endif
+
+ /*
+ * Print the address, followed by value. Then accept input for
+ * the next value. A non-converted value exits.
+ */
+ do {
+ printf("%08lx:", addr);
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = dm_i2c_read(dev, addr, (uchar *)&data, size);
+#else
+ ret = i2c_read(chip, addr, alen, (uchar *)&data, size);
+#endif
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_READ);
+
+ data = cpu_to_be32(data);
+ if (size == 1)
+ printf(" %02lx", (data >> 24) & 0x000000FF);
+ else if (size == 2)
+ printf(" %04lx", (data >> 16) & 0x0000FFFF);
+ else
+ printf(" %08lx", data);
+
+ nbytes = cli_readline(" ? ");
+ if (nbytes == 0) {
+ /*
+ * <CR> pressed as only input, don't modify current
+ * location and move to next.
+ */
+ if (incrflag)
+ addr += size;
+ nbytes = size;
+ /* good enough to not time out */
+ bootretry_reset_cmd_timeout();
+ }
+#ifdef CONFIG_BOOT_RETRY_TIME
+ else if (nbytes == -2)
+ break; /* timed out, exit the command */
+#endif
+ else {
+ char *endp;
+
+ data = hextoul(console_buffer, &endp);
+ if (size == 1)
+ data = data << 24;
+ else if (size == 2)
+ data = data << 16;
+ data = be32_to_cpu(data);
+ nbytes = endp - console_buffer;
+ if (nbytes) {
+ /*
+ * good enough to not time out
+ */
+ bootretry_reset_cmd_timeout();
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = dm_i2c_write(dev, addr, (uchar *)&data,
+ size);
+#else
+ ret = i2c_write(chip, addr, alen,
+ (uchar *)&data, size);
+#endif
+ if (ret)
+ return i2c_report_err(ret,
+ I2C_ERR_WRITE);
+#if CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS > 0
+ udelay(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS * 1000);
+#endif
+ if (incrflag)
+ addr += size;
+ }
+ }
+ } while (nbytes);
+
+ i2c_mm_last_chip = chip;
+ i2c_mm_last_addr = addr;
+ i2c_mm_last_alen = alen;
+
+ return 0;
+}
+
+/**
+ * do_i2c_probe() - Handle the "i2c probe" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ *
+ * Syntax:
+ * i2c probe {addr}
+ *
+ * Returns zero (success) if one or more I2C devices was found
+ */
+static int do_i2c_probe(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int j;
+ int addr = -1;
+ int found = 0;
+#if defined(CFG_SYS_I2C_NOPROBES)
+ int k, skip;
+ unsigned int bus = GET_BUS_NUM;
+#endif /* NOPROBES */
+ int ret;
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *cur_bus, *dev;
+
+ if (i2c_get_cur_bus(&cur_bus))
+ return CMD_RET_FAILURE;
+#endif
+
+ if (argc == 2)
+ addr = simple_strtol(argv[1], 0, 16);
+
+ puts ("Valid chip addresses:");
+ for (j = 0; j < 128; j++) {
+ if ((0 <= addr) && (j != addr))
+ continue;
+
+#if defined(CFG_SYS_I2C_NOPROBES)
+ skip = 0;
+ for (k = 0; k < ARRAY_SIZE(i2c_no_probes); k++) {
+ if (COMPARE_BUS(bus, k) && COMPARE_ADDR(j, k)) {
+ skip = 1;
+ break;
+ }
+ }
+ if (skip)
+ continue;
+#endif
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = dm_i2c_probe(cur_bus, j, 0, &dev);
+#else
+ ret = i2c_probe(j);
+#endif
+ if (ret == 0) {
+ printf(" %02X", j);
+ found++;
+ }
+ }
+ putc ('\n');
+
+#if defined(CFG_SYS_I2C_NOPROBES)
+ puts ("Excluded chip addresses:");
+ for (k = 0; k < ARRAY_SIZE(i2c_no_probes); k++) {
+ if (COMPARE_BUS(bus,k))
+ printf(" %02X", NO_PROBE_ADDR(k));
+ }
+ putc ('\n');
+#endif
+
+ return (0 == found);
+}
+
+/**
+ * do_i2c_loop() - Handle the "i2c loop" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ *
+ * Syntax:
+ * i2c loop {i2c_chip} {addr}{.0, .1, .2} [{length}] [{delay}]
+ * {length} - Number of bytes to read
+ * {delay} - A DECIMAL number and defaults to 1000 uSec
+ */
+static int do_i2c_loop(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ uint chip;
+ int alen;
+ uint addr;
+ uint length;
+ u_char bytes[16];
+ int delay;
+ int ret;
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *dev;
+#endif
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ /*
+ * Chip is always specified.
+ */
+ chip = hextoul(argv[1], NULL);
+
+ /*
+ * Address is always specified.
+ */
+ addr = hextoul(argv[2], NULL);
+ alen = get_alen(argv[2], DEFAULT_ADDR_LEN);
+ if (alen > 3)
+ return CMD_RET_USAGE;
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = i2c_get_cur_bus_chip(chip, &dev);
+ if (!ret && alen != -1)
+ ret = i2c_set_chip_offset_len(dev, alen);
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_WRITE);
+#endif
+
+ /*
+ * Length is the number of objects, not number of bytes.
+ */
+ length = 1;
+ length = hextoul(argv[3], NULL);
+ if (length > sizeof(bytes))
+ length = sizeof(bytes);
+
+ /*
+ * The delay time (uSec) is optional.
+ */
+ delay = 1000;
+ if (argc > 3)
+ delay = dectoul(argv[4], NULL);
+ /*
+ * Run the loop...
+ */
+ while (1) {
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = dm_i2c_read(dev, addr, bytes, length);
+#else
+ ret = i2c_read(chip, addr, alen, bytes, length);
+#endif
+ if (ret)
+ i2c_report_err(ret, I2C_ERR_READ);
+ udelay(delay);
+ }
+
+ /* NOTREACHED */
+ return 0;
+}
+
+/*
+ * The SDRAM command is separately configured because many
+ * (most?) embedded boards don't use SDRAM DIMMs.
+ *
+ * FIXME: Document and probably move elsewhere!
+ */
+#if defined(CONFIG_CMD_SDRAM)
+static void print_ddr2_tcyc (u_char const b)
+{
+ printf ("%d.", (b >> 4) & 0x0F);
+ switch (b & 0x0F) {
+ case 0x0:
+ case 0x1:
+ case 0x2:
+ case 0x3:
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ case 0x8:
+ case 0x9:
+ printf ("%d ns\n", b & 0x0F);
+ break;
+ case 0xA:
+ puts ("25 ns\n");
+ break;
+ case 0xB:
+ puts ("33 ns\n");
+ break;
+ case 0xC:
+ puts ("66 ns\n");
+ break;
+ case 0xD:
+ puts ("75 ns\n");
+ break;
+ default:
+ puts ("?? ns\n");
+ break;
+ }
+}
+
+static void decode_bits (u_char const b, char const *str[], int const do_once)
+{
+ u_char mask;
+
+ for (mask = 0x80; mask != 0x00; mask >>= 1, ++str) {
+ if (b & mask) {
+ puts (*str);
+ if (do_once)
+ return;
+ }
+ }
+}
+
+/*
+ * Syntax:
+ * i2c sdram {i2c_chip}
+ */
+static int do_sdram(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ enum { unknown, EDO, SDRAM, DDR, DDR2, DDR3, DDR4 } type;
+
+ uint chip;
+ u_char data[128];
+ u_char cksum;
+ int j, ret;
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *dev;
+#endif
+
+ static const char *decode_CAS_DDR2[] = {
+ " TBD", " 6", " 5", " 4", " 3", " 2", " TBD", " TBD"
+ };
+
+ static const char *decode_CAS_default[] = {
+ " TBD", " 7", " 6", " 5", " 4", " 3", " 2", " 1"
+ };
+
+ static const char *decode_CS_WE_default[] = {
+ " TBD", " 6", " 5", " 4", " 3", " 2", " 1", " 0"
+ };
+
+ static const char *decode_byte21_default[] = {
+ " TBD (bit 7)\n",
+ " Redundant row address\n",
+ " Differential clock input\n",
+ " Registerd DQMB inputs\n",
+ " Buffered DQMB inputs\n",
+ " On-card PLL\n",
+ " Registered address/control lines\n",
+ " Buffered address/control lines\n"
+ };
+
+ static const char *decode_byte22_DDR2[] = {
+ " TBD (bit 7)\n",
+ " TBD (bit 6)\n",
+ " TBD (bit 5)\n",
+ " TBD (bit 4)\n",
+ " TBD (bit 3)\n",
+ " Supports partial array self refresh\n",
+ " Supports 50 ohm ODT\n",
+ " Supports weak driver\n"
+ };
+
+ static const char *decode_row_density_DDR2[] = {
+ "512 MiB", "256 MiB", "128 MiB", "16 GiB",
+ "8 GiB", "4 GiB", "2 GiB", "1 GiB"
+ };
+
+ static const char *decode_row_density_default[] = {
+ "512 MiB", "256 MiB", "128 MiB", "64 MiB",
+ "32 MiB", "16 MiB", "8 MiB", "4 MiB"
+ };
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /*
+ * Chip is always specified.
+ */
+ chip = hextoul(argv[1], NULL);
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = i2c_get_cur_bus_chip(chip, &dev);
+ if (!ret)
+ ret = dm_i2c_read(dev, 0, data, sizeof(data));
+#else
+ ret = i2c_read(chip, 0, 1, data, sizeof(data));
+#endif
+ if (ret) {
+ puts ("No SDRAM Serial Presence Detect found.\n");
+ return 1;
+ }
+
+ cksum = 0;
+ for (j = 0; j < 63; j++) {
+ cksum += data[j];
+ }
+ if (cksum != data[63]) {
+ printf ("WARNING: Configuration data checksum failure:\n"
+ " is 0x%02x, calculated 0x%02x\n", data[63], cksum);
+ }
+ printf ("SPD data revision %d.%d\n",
+ (data[62] >> 4) & 0x0F, data[62] & 0x0F);
+ printf ("Bytes used 0x%02X\n", data[0]);
+ printf ("Serial memory size 0x%02X\n", 1 << data[1]);
+
+ puts ("Memory type ");
+ switch (data[2]) {
+ case 2:
+ type = EDO;
+ puts ("EDO\n");
+ break;
+ case 4:
+ type = SDRAM;
+ puts ("SDRAM\n");
+ break;
+ case 7:
+ type = DDR;
+ puts("DDR\n");
+ break;
+ case 8:
+ type = DDR2;
+ puts ("DDR2\n");
+ break;
+ case 11:
+ type = DDR3;
+ puts("DDR3\n");
+ break;
+ case 12:
+ type = DDR4;
+ puts("DDR4\n");
+ break;
+ default:
+ type = unknown;
+ puts ("unknown\n");
+ break;
+ }
+
+ puts ("Row address bits ");
+ if ((data[3] & 0x00F0) == 0)
+ printf ("%d\n", data[3] & 0x0F);
+ else
+ printf ("%d/%d\n", data[3] & 0x0F, (data[3] >> 4) & 0x0F);
+
+ puts ("Column address bits ");
+ if ((data[4] & 0x00F0) == 0)
+ printf ("%d\n", data[4] & 0x0F);
+ else
+ printf ("%d/%d\n", data[4] & 0x0F, (data[4] >> 4) & 0x0F);
+
+ switch (type) {
+ case DDR2:
+ printf ("Number of ranks %d\n",
+ (data[5] & 0x07) + 1);
+ break;
+ default:
+ printf ("Module rows %d\n", data[5]);
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ printf ("Module data width %d bits\n", data[6]);
+ break;
+ default:
+ printf ("Module data width %d bits\n",
+ (data[7] << 8) | data[6]);
+ break;
+ }
+
+ puts ("Interface signal levels ");
+ switch(data[8]) {
+ case 0: puts ("TTL 5.0 V\n"); break;
+ case 1: puts ("LVTTL\n"); break;
+ case 2: puts ("HSTL 1.5 V\n"); break;
+ case 3: puts ("SSTL 3.3 V\n"); break;
+ case 4: puts ("SSTL 2.5 V\n"); break;
+ case 5: puts ("SSTL 1.8 V\n"); break;
+ default: puts ("unknown\n"); break;
+ }
+
+ switch (type) {
+ case DDR2:
+ printf ("SDRAM cycle time ");
+ print_ddr2_tcyc (data[9]);
+ break;
+ default:
+ printf ("SDRAM cycle time %d.%d ns\n",
+ (data[9] >> 4) & 0x0F, data[9] & 0x0F);
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ printf ("SDRAM access time 0.%d%d ns\n",
+ (data[10] >> 4) & 0x0F, data[10] & 0x0F);
+ break;
+ default:
+ printf ("SDRAM access time %d.%d ns\n",
+ (data[10] >> 4) & 0x0F, data[10] & 0x0F);
+ break;
+ }
+
+ puts ("EDC configuration ");
+ switch (data[11]) {
+ case 0: puts ("None\n"); break;
+ case 1: puts ("Parity\n"); break;
+ case 2: puts ("ECC\n"); break;
+ default: puts ("unknown\n"); break;
+ }
+
+ if ((data[12] & 0x80) == 0)
+ puts ("No self refresh, rate ");
+ else
+ puts ("Self refresh, rate ");
+
+ switch(data[12] & 0x7F) {
+ case 0: puts ("15.625 us\n"); break;
+ case 1: puts ("3.9 us\n"); break;
+ case 2: puts ("7.8 us\n"); break;
+ case 3: puts ("31.3 us\n"); break;
+ case 4: puts ("62.5 us\n"); break;
+ case 5: puts ("125 us\n"); break;
+ default: puts ("unknown\n"); break;
+ }
+
+ switch (type) {
+ case DDR2:
+ printf ("SDRAM width (primary) %d\n", data[13]);
+ break;
+ default:
+ printf ("SDRAM width (primary) %d\n", data[13] & 0x7F);
+ if ((data[13] & 0x80) != 0) {
+ printf (" (second bank) %d\n",
+ 2 * (data[13] & 0x7F));
+ }
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ if (data[14] != 0)
+ printf ("EDC width %d\n", data[14]);
+ break;
+ default:
+ if (data[14] != 0) {
+ printf ("EDC width %d\n",
+ data[14] & 0x7F);
+
+ if ((data[14] & 0x80) != 0) {
+ printf (" (second bank) %d\n",
+ 2 * (data[14] & 0x7F));
+ }
+ }
+ break;
+ }
+
+ if (DDR2 != type) {
+ printf ("Min clock delay, back-to-back random column addresses "
+ "%d\n", data[15]);
+ }
+
+ puts ("Burst length(s) ");
+ if (data[16] & 0x80) puts (" Page");
+ if (data[16] & 0x08) puts (" 8");
+ if (data[16] & 0x04) puts (" 4");
+ if (data[16] & 0x02) puts (" 2");
+ if (data[16] & 0x01) puts (" 1");
+ putc ('\n');
+ printf ("Number of banks %d\n", data[17]);
+
+ switch (type) {
+ case DDR2:
+ puts ("CAS latency(s) ");
+ decode_bits (data[18], decode_CAS_DDR2, 0);
+ putc ('\n');
+ break;
+ default:
+ puts ("CAS latency(s) ");
+ decode_bits (data[18], decode_CAS_default, 0);
+ putc ('\n');
+ break;
+ }
+
+ if (DDR2 != type) {
+ puts ("CS latency(s) ");
+ decode_bits (data[19], decode_CS_WE_default, 0);
+ putc ('\n');
+ }
+
+ if (DDR2 != type) {
+ puts ("WE latency(s) ");
+ decode_bits (data[20], decode_CS_WE_default, 0);
+ putc ('\n');
+ }
+
+ switch (type) {
+ case DDR2:
+ puts ("Module attributes:\n");
+ if (data[21] & 0x80)
+ puts (" TBD (bit 7)\n");
+ if (data[21] & 0x40)
+ puts (" Analysis probe installed\n");
+ if (data[21] & 0x20)
+ puts (" TBD (bit 5)\n");
+ if (data[21] & 0x10)
+ puts (" FET switch external enable\n");
+ printf (" %d PLLs on DIMM\n", (data[21] >> 2) & 0x03);
+ if (data[20] & 0x11) {
+ printf (" %d active registers on DIMM\n",
+ (data[21] & 0x03) + 1);
+ }
+ break;
+ default:
+ puts ("Module attributes:\n");
+ if (!data[21])
+ puts (" (none)\n");
+ else
+ decode_bits (data[21], decode_byte21_default, 0);
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ decode_bits (data[22], decode_byte22_DDR2, 0);
+ break;
+ default:
+ puts ("Device attributes:\n");
+ if (data[22] & 0x80) puts (" TBD (bit 7)\n");
+ if (data[22] & 0x40) puts (" TBD (bit 6)\n");
+ if (data[22] & 0x20) puts (" Upper Vcc tolerance 5%\n");
+ else puts (" Upper Vcc tolerance 10%\n");
+ if (data[22] & 0x10) puts (" Lower Vcc tolerance 5%\n");
+ else puts (" Lower Vcc tolerance 10%\n");
+ if (data[22] & 0x08) puts (" Supports write1/read burst\n");
+ if (data[22] & 0x04) puts (" Supports precharge all\n");
+ if (data[22] & 0x02) puts (" Supports auto precharge\n");
+ if (data[22] & 0x01) puts (" Supports early RAS# precharge\n");
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ printf ("SDRAM cycle time (2nd highest CAS latency) ");
+ print_ddr2_tcyc (data[23]);
+ break;
+ default:
+ printf ("SDRAM cycle time (2nd highest CAS latency) %d."
+ "%d ns\n", (data[23] >> 4) & 0x0F, data[23] & 0x0F);
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ printf ("SDRAM access from clock (2nd highest CAS latency) 0."
+ "%d%d ns\n", (data[24] >> 4) & 0x0F, data[24] & 0x0F);
+ break;
+ default:
+ printf ("SDRAM access from clock (2nd highest CAS latency) %d."
+ "%d ns\n", (data[24] >> 4) & 0x0F, data[24] & 0x0F);
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ printf ("SDRAM cycle time (3rd highest CAS latency) ");
+ print_ddr2_tcyc (data[25]);
+ break;
+ default:
+ printf ("SDRAM cycle time (3rd highest CAS latency) %d."
+ "%d ns\n", (data[25] >> 4) & 0x0F, data[25] & 0x0F);
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ printf ("SDRAM access from clock (3rd highest CAS latency) 0."
+ "%d%d ns\n", (data[26] >> 4) & 0x0F, data[26] & 0x0F);
+ break;
+ default:
+ printf ("SDRAM access from clock (3rd highest CAS latency) %d."
+ "%d ns\n", (data[26] >> 4) & 0x0F, data[26] & 0x0F);
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ printf ("Minimum row precharge %d.%02d ns\n",
+ (data[27] >> 2) & 0x3F, 25 * (data[27] & 0x03));
+ break;
+ default:
+ printf ("Minimum row precharge %d ns\n", data[27]);
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ printf ("Row active to row active min %d.%02d ns\n",
+ (data[28] >> 2) & 0x3F, 25 * (data[28] & 0x03));
+ break;
+ default:
+ printf ("Row active to row active min %d ns\n", data[28]);
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ printf ("RAS to CAS delay min %d.%02d ns\n",
+ (data[29] >> 2) & 0x3F, 25 * (data[29] & 0x03));
+ break;
+ default:
+ printf ("RAS to CAS delay min %d ns\n", data[29]);
+ break;
+ }
+
+ printf ("Minimum RAS pulse width %d ns\n", data[30]);
+
+ switch (type) {
+ case DDR2:
+ puts ("Density of each row ");
+ decode_bits (data[31], decode_row_density_DDR2, 1);
+ putc ('\n');
+ break;
+ default:
+ puts ("Density of each row ");
+ decode_bits (data[31], decode_row_density_default, 1);
+ putc ('\n');
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ puts ("Command and Address setup ");
+ if (data[32] >= 0xA0) {
+ printf ("1.%d%d ns\n",
+ ((data[32] >> 4) & 0x0F) - 10, data[32] & 0x0F);
+ } else {
+ printf ("0.%d%d ns\n",
+ ((data[32] >> 4) & 0x0F), data[32] & 0x0F);
+ }
+ break;
+ default:
+ printf ("Command and Address setup %c%d.%d ns\n",
+ (data[32] & 0x80) ? '-' : '+',
+ (data[32] >> 4) & 0x07, data[32] & 0x0F);
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ puts ("Command and Address hold ");
+ if (data[33] >= 0xA0) {
+ printf ("1.%d%d ns\n",
+ ((data[33] >> 4) & 0x0F) - 10, data[33] & 0x0F);
+ } else {
+ printf ("0.%d%d ns\n",
+ ((data[33] >> 4) & 0x0F), data[33] & 0x0F);
+ }
+ break;
+ default:
+ printf ("Command and Address hold %c%d.%d ns\n",
+ (data[33] & 0x80) ? '-' : '+',
+ (data[33] >> 4) & 0x07, data[33] & 0x0F);
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ printf ("Data signal input setup 0.%d%d ns\n",
+ (data[34] >> 4) & 0x0F, data[34] & 0x0F);
+ break;
+ default:
+ printf ("Data signal input setup %c%d.%d ns\n",
+ (data[34] & 0x80) ? '-' : '+',
+ (data[34] >> 4) & 0x07, data[34] & 0x0F);
+ break;
+ }
+
+ switch (type) {
+ case DDR2:
+ printf ("Data signal input hold 0.%d%d ns\n",
+ (data[35] >> 4) & 0x0F, data[35] & 0x0F);
+ break;
+ default:
+ printf ("Data signal input hold %c%d.%d ns\n",
+ (data[35] & 0x80) ? '-' : '+',
+ (data[35] >> 4) & 0x07, data[35] & 0x0F);
+ break;
+ }
+
+ puts ("Manufacturer's JEDEC ID ");
+ for (j = 64; j <= 71; j++)
+ printf ("%02X ", data[j]);
+ putc ('\n');
+ printf ("Manufacturing Location %02X\n", data[72]);
+ puts ("Manufacturer's Part Number ");
+ for (j = 73; j <= 90; j++)
+ printf ("%02X ", data[j]);
+ putc ('\n');
+ printf ("Revision Code %02X %02X\n", data[91], data[92]);
+ printf ("Manufacturing Date %02X %02X\n", data[93], data[94]);
+ puts ("Assembly Serial Number ");
+ for (j = 95; j <= 98; j++)
+ printf ("%02X ", data[j]);
+ putc ('\n');
+
+ if (DDR2 != type) {
+ printf ("Speed rating PC%d\n",
+ data[126] == 0x66 ? 66 : data[126]);
+ }
+ return 0;
+}
+#endif
+
+/*
+ * Syntax:
+ * i2c edid {i2c_chip}
+ */
+#if defined(CONFIG_I2C_EDID)
+int do_edid(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ uint chip;
+ struct edid1_info edid;
+ int ret;
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *dev;
+#endif
+
+ if (argc < 2) {
+ cmd_usage(cmdtp);
+ return 1;
+ }
+
+ chip = hextoul(argv[1], NULL);
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = i2c_get_cur_bus_chip(chip, &dev);
+ if (!ret)
+ ret = dm_i2c_read(dev, 0, (uchar *)&edid, sizeof(edid));
+#else
+ ret = i2c_read(chip, 0, 1, (uchar *)&edid, sizeof(edid));
+#endif
+ if (ret)
+ return i2c_report_err(ret, I2C_ERR_READ);
+
+ if (edid_check_info(&edid)) {
+ puts("Content isn't valid EDID.\n");
+ return 1;
+ }
+
+ edid_print_info(&edid);
+ return 0;
+
+}
+#endif /* CONFIG_I2C_EDID */
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+static void show_bus(struct udevice *bus)
+{
+ struct udevice *dev;
+
+ printf("Bus %d:\t%s", dev_seq(bus), bus->name);
+ if (device_active(bus))
+ printf(" (active %d)", dev_seq(bus));
+ printf("\n");
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ struct dm_i2c_chip *chip = dev_get_parent_plat(dev);
+
+ printf(" %02x: %s, offset len %x, flags %x\n",
+ chip->chip_addr, dev->name, chip->offset_len,
+ chip->flags);
+ }
+}
+#endif
+
+/**
+ * do_i2c_show_bus() - Handle the "i2c bus" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero always.
+ */
+#if CONFIG_IS_ENABLED(SYS_I2C_LEGACY) || CONFIG_IS_ENABLED(DM_I2C)
+static int do_i2c_show_bus(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (argc == 1) {
+ /* show all busses */
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *bus;
+ struct uclass *uc;
+ int ret;
+
+ ret = uclass_get(UCLASS_I2C, &uc);
+ if (ret)
+ return CMD_RET_FAILURE;
+ uclass_foreach_dev(bus, uc)
+ show_bus(bus);
+#else
+ int i;
+
+ for (i = 0; i < CFG_SYS_NUM_I2C_BUSES; i++) {
+ printf("Bus %d:\t%s", i, I2C_ADAP_NR(i)->name);
+ printf("\n");
+ }
+#endif
+ } else {
+ int i;
+
+ /* show specific bus */
+ i = dectoul(argv[1], NULL);
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *bus;
+ int ret;
+
+ ret = uclass_get_device_by_seq(UCLASS_I2C, i, &bus);
+ if (ret) {
+ printf("Invalid bus %d: err=%d\n", i, ret);
+ return CMD_RET_FAILURE;
+ }
+ show_bus(bus);
+#else
+ if (i >= CFG_SYS_NUM_I2C_BUSES) {
+ printf("Invalid bus %d\n", i);
+ return -1;
+ }
+ printf("Bus %d:\t%s", i, I2C_ADAP_NR(i)->name);
+ printf("\n");
+#endif
+ }
+
+ return 0;
+}
+#endif
+
+/**
+ * do_i2c_bus_num() - Handle the "i2c dev" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ */
+#if CONFIG_IS_ENABLED(SYS_I2C_LEGACY) || CONFIG_IS_ENABLED(DM_I2C)
+static int do_i2c_bus_num(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret = 0;
+ int bus_no;
+
+ if (argc == 1) {
+ /* querying current setting */
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *bus;
+
+ if (!i2c_get_cur_bus(&bus))
+ bus_no = dev_seq(bus);
+ else
+ bus_no = -1;
+#else
+ bus_no = i2c_get_bus_num();
+#endif
+ printf("Current bus is %d\n", bus_no);
+ } else {
+ bus_no = dectoul(argv[1], NULL);
+#if CONFIG_IS_ENABLED(SYS_I2C_LEGACY)
+ if (bus_no >= CFG_SYS_NUM_I2C_BUSES) {
+ printf("Invalid bus %d\n", bus_no);
+ return -1;
+ }
+#endif
+ printf("Setting bus to %d\n", bus_no);
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = cmd_i2c_set_bus_num(bus_no);
+#else
+ ret = i2c_set_bus_num(bus_no);
+#endif
+ if (ret)
+ printf("Failure changing bus number (%d)\n", ret);
+ }
+
+ return ret ? CMD_RET_FAILURE : 0;
+}
+#endif /* CONFIG_IS_ENABLED(SYS_I2C_LEGACY) */
+
+/**
+ * do_i2c_bus_speed() - Handle the "i2c speed" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ */
+static int do_i2c_bus_speed(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int speed, ret=0;
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *bus;
+
+ if (i2c_get_cur_bus(&bus))
+ return 1;
+#endif
+ if (argc == 1) {
+#if CONFIG_IS_ENABLED(DM_I2C)
+ speed = dm_i2c_get_bus_speed(bus);
+#else
+ speed = i2c_get_bus_speed();
+#endif
+ /* querying current speed */
+ printf("Current bus speed=%d\n", speed);
+ } else {
+ speed = dectoul(argv[1], NULL);
+ printf("Setting bus speed to %d Hz\n", speed);
+#if CONFIG_IS_ENABLED(DM_I2C)
+ ret = dm_i2c_set_bus_speed(bus, speed);
+#else
+ ret = i2c_set_bus_speed(speed);
+#endif
+ if (ret)
+ printf("Failure changing bus speed (%d)\n", ret);
+ }
+
+ return ret ? CMD_RET_FAILURE : 0;
+}
+
+/**
+ * do_i2c_mm() - Handle the "i2c mm" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ */
+static int do_i2c_mm(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return mod_i2c_mem (cmdtp, 1, flag, argc, argv);
+}
+
+/**
+ * do_i2c_nm() - Handle the "i2c nm" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ */
+static int do_i2c_nm(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return mod_i2c_mem (cmdtp, 0, flag, argc, argv);
+}
+
+/**
+ * do_i2c_reset() - Handle the "i2c reset" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero always.
+ */
+static int do_i2c_reset(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+#if CONFIG_IS_ENABLED(DM_I2C)
+ struct udevice *bus;
+
+ if (i2c_get_cur_bus(&bus))
+ return CMD_RET_FAILURE;
+ if (i2c_deblock(bus)) {
+ printf("Error: Not supported by the driver\n");
+ return CMD_RET_FAILURE;
+ }
+#elif CONFIG_IS_ENABLED(SYS_I2C_LEGACY)
+ i2c_init(I2C_ADAP->speed, I2C_ADAP->slaveaddr);
+#endif
+ return 0;
+}
+
+static struct cmd_tbl cmd_i2c_sub[] = {
+#if CONFIG_IS_ENABLED(SYS_I2C_LEGACY) || CONFIG_IS_ENABLED(DM_I2C)
+ U_BOOT_CMD_MKENT(bus, 1, 1, do_i2c_show_bus, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(crc32, 3, 1, do_i2c_crc, "", ""),
+#if CONFIG_IS_ENABLED(SYS_I2C_LEGACY) || CONFIG_IS_ENABLED(DM_I2C)
+ U_BOOT_CMD_MKENT(dev, 1, 1, do_i2c_bus_num, "", ""),
+#endif
+#if defined(CONFIG_I2C_EDID)
+ U_BOOT_CMD_MKENT(edid, 1, 1, do_edid, "", ""),
+#endif /* CONFIG_I2C_EDID */
+ U_BOOT_CMD_MKENT(loop, 3, 1, do_i2c_loop, "", ""),
+ U_BOOT_CMD_MKENT(md, 3, 1, do_i2c_md, "", ""),
+ U_BOOT_CMD_MKENT(mm, 2, 1, do_i2c_mm, "", ""),
+ U_BOOT_CMD_MKENT(mw, 3, 1, do_i2c_mw, "", ""),
+ U_BOOT_CMD_MKENT(nm, 2, 1, do_i2c_nm, "", ""),
+ U_BOOT_CMD_MKENT(probe, 0, 1, do_i2c_probe, "", ""),
+ U_BOOT_CMD_MKENT(read, 5, 1, do_i2c_read, "", ""),
+ U_BOOT_CMD_MKENT(write, 6, 0, do_i2c_write, "", ""),
+#if CONFIG_IS_ENABLED(DM_I2C)
+ U_BOOT_CMD_MKENT(flags, 2, 1, do_i2c_flags, "", ""),
+ U_BOOT_CMD_MKENT(olen, 2, 1, do_i2c_olen, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(reset, 0, 1, do_i2c_reset, "", ""),
+#if defined(CONFIG_CMD_SDRAM)
+ U_BOOT_CMD_MKENT(sdram, 1, 1, do_sdram, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(speed, 1, 1, do_i2c_bus_speed, "", ""),
+};
+
+/**
+ * do_i2c() - Handle the "i2c" command-line command
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ */
+static int do_i2c(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct cmd_tbl *c;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* Strip off leading 'i2c' command argument */
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], &cmd_i2c_sub[0], ARRAY_SIZE(cmd_i2c_sub));
+
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+ else
+ return CMD_RET_USAGE;
+}
+
+/***************************************************/
+U_BOOT_LONGHELP(i2c,
+#if CONFIG_IS_ENABLED(SYS_I2C_LEGACY) || CONFIG_IS_ENABLED(DM_I2C)
+ "bus [muxtype:muxaddr:muxchannel] - show I2C bus info\n"
+ "i2c " /* That's the prefix for the crc32 command below. */
+#endif
+ "crc32 chip address[.0, .1, .2] count - compute CRC32 checksum\n"
+#if CONFIG_IS_ENABLED(SYS_I2C_LEGACY) || CONFIG_IS_ENABLED(DM_I2C)
+ "i2c dev [dev] - show or set current I2C bus\n"
+#endif
+#if defined(CONFIG_I2C_EDID)
+ "i2c edid chip - print EDID configuration information\n"
+#endif /* CONFIG_I2C_EDID */
+ "i2c loop chip address[.0, .1, .2] [# of objects] - looping read of device\n"
+ "i2c md chip address[.0, .1, .2] [# of objects] - read from I2C device\n"
+ "i2c mm chip address[.0, .1, .2] - write to I2C device (auto-incrementing)\n"
+ "i2c mw chip address[.0, .1, .2] value [count] - write to I2C device (fill)\n"
+ "i2c nm chip address[.0, .1, .2] - write to I2C device (constant address)\n"
+ "i2c probe [address] - test for and show device(s) on the I2C bus\n"
+ "i2c read chip address[.0, .1, .2] length memaddress - read to memory\n"
+ "i2c write memaddress chip address[.0, .1, .2] length [-s] - write memory\n"
+ " to I2C; the -s option selects bulk write in a single transaction\n"
+#if CONFIG_IS_ENABLED(DM_I2C)
+ "i2c flags chip [flags] - set or get chip flags\n"
+ "i2c olen chip [offset_length] - set or get chip offset length\n"
+#endif
+ "i2c reset - re-init the I2C Controller\n"
+#if defined(CONFIG_CMD_SDRAM)
+ "i2c sdram chip - print SDRAM configuration information\n"
+#endif
+ "i2c speed [speed] - show or set I2C bus speed");
+
+U_BOOT_CMD(
+ i2c, 7, 1, do_i2c,
+ "I2C sub-system",
+ i2c_help_text
+);
diff --git a/cmd/i3c.c b/cmd/i3c.c
new file mode 100644
index 00000000000..08957f4d447
--- /dev/null
+++ b/cmd/i3c.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Altera Corporation <www.altera.com>
+ */
+
+#include <bootretry.h>
+#include <cli.h>
+#include <command.h>
+#include <console.h>
+#include <dm.h>
+#include <dw-i3c.h>
+#include <edid.h>
+#include <errno.h>
+#include <hexdump.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <u-boot/crc.h>
+#include <linux/i3c/master.h>
+#include <linux/printk.h>
+#include <linux/types.h>
+
+static struct udevice *currdev;
+static struct udevice *prevdev;
+static struct dw_i3c_master *master;
+
+static void low_to_high_bytes(void *data, size_t size)
+{
+ u8 *byte_data = data;
+ size_t start = 0;
+ size_t end = size - 1;
+
+ while (start < end) {
+ u8 temp = byte_data[start];
+
+ byte_data[start] = byte_data[end];
+ byte_data[end] = temp;
+ start++;
+ end--;
+ }
+}
+
+static int handle_i3c_select(const char *name)
+{
+ struct uclass *uc;
+ struct udevice *dev_list;
+ int ret = uclass_get_device_by_name(UCLASS_I3C, name, &currdev);
+
+ if (ret) {
+ currdev = prevdev;
+ if (!currdev) {
+ ret = uclass_get(UCLASS_I3C, &uc);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ uclass_foreach_dev(dev_list, uc)
+ printf("%s (%s)\n", dev_list->name, dev_list->driver->name);
+
+ printf("i3c: Host controller not initialized: %s\n", name);
+ return CMD_RET_FAILURE;
+ }
+ } else {
+ master = dev_get_priv(currdev);
+ printf("i3c: Current controller: %s\n", currdev->name);
+ prevdev = currdev;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int handle_i3c_list(void)
+{
+ struct uclass *uc;
+ struct udevice *dev_list;
+ int ret = uclass_get(UCLASS_I3C, &uc);
+
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ uclass_foreach_dev(dev_list, uc)
+ printf("%s (%s)\n", dev_list->name, dev_list->driver->name);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int handle_i3c_current(void)
+{
+ if (!currdev)
+ printf("i3c: No current controller selected\n");
+ else
+ printf("i3c: Current controller: %s\n", currdev->name);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int handle_i3c_device_list(void)
+{
+ if (!master) {
+ printf("i3c: No controller active\n");
+ return CMD_RET_FAILURE;
+ }
+
+ for (int i = 0; i < master->num_i3cdevs; i++) {
+ struct i3c_device_info *info = &master->i3cdev[i]->info;
+
+ printf("Device %d:\n", i);
+ printf(" Static Address : 0x%02X\n", info->static_addr);
+ printf(" Dynamic Address : 0x%X\n", info->dyn_addr);
+ printf(" PID : %016llx\n", info->pid);
+ printf(" BCR : 0x%X\n", info->bcr);
+ printf(" DCR : 0x%X\n", info->dcr);
+ printf(" Max Read DS : 0x%X\n", info->max_read_ds);
+ printf(" Max Write DS : 0x%X\n", info->max_write_ds);
+ printf("\n");
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int handle_i3c_write(int argc, char *const argv[])
+{
+ u32 mem_addr, num_bytes, dev_num_val;
+ u8 device_num;
+ u8 *data;
+ int ret;
+
+ if (argc < 5)
+ return CMD_RET_USAGE;
+
+ if (!currdev) {
+ printf("i3c: No I3C controller selected\n");
+ return CMD_RET_FAILURE;
+ }
+
+ mem_addr = hextoul(argv[2], NULL);
+ num_bytes = hextoul(argv[3], NULL);
+ dev_num_val = hextoul(argv[4], NULL);
+
+ if (num_bytes == 0 || num_bytes > 4) {
+ printf("i3c: Length must be between 1 and 4\n");
+ return CMD_RET_USAGE;
+ }
+
+ if (dev_num_val > 0xFF) {
+ printf("i3c: Device number 0x%x exceeds valid u8 range\n", dev_num_val);
+ return CMD_RET_USAGE;
+ }
+
+ device_num = dev_num_val;
+ data = malloc(num_bytes);
+
+ if (!data) {
+ printf("i3c: Memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ memcpy(data, (void *)(uintptr_t)mem_addr, num_bytes);
+ low_to_high_bytes(data, num_bytes);
+
+ ret = dm_i3c_write(currdev, device_num, data, num_bytes);
+
+ if (ret)
+ printf("i3c: Write failed: %d\n", ret);
+
+ free(data);
+ return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+static int handle_i3c_read(int argc, char *const argv[])
+{
+ u32 mem_addr, read_len, dev_num_val;
+ u8 device_num;
+ u8 *rdata;
+ int ret;
+
+ if (argc < 5)
+ return CMD_RET_USAGE;
+
+ if (!currdev) {
+ printf("i3c: No I3C controller selected\n");
+ return CMD_RET_FAILURE;
+ }
+
+ mem_addr = hextoul(argv[2], NULL);
+ read_len = hextoul(argv[3], NULL);
+ dev_num_val = hextoul(argv[4], NULL);
+
+ if (read_len == 0) {
+ printf("i3c: Read length must be greater than 0\n");
+ return CMD_RET_USAGE;
+ }
+
+ if (dev_num_val > 0xFF) {
+ printf("i3c: Device number 0x%x exceeds valid u8 range\n", dev_num_val);
+ return CMD_RET_USAGE;
+ }
+
+ device_num = dev_num_val;
+ rdata = malloc(read_len);
+
+ if (!rdata) {
+ printf("i3c: Memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ ret = dm_i3c_read(currdev, device_num, rdata, read_len);
+
+ if (ret) {
+ printf("i3c: Read failed: %d\n", ret);
+ free(rdata);
+ return CMD_RET_FAILURE;
+ }
+
+ memcpy((void *)(uintptr_t)mem_addr, rdata, read_len);
+ print_hex_dump("i3c read: ", DUMP_PREFIX_OFFSET, 16, 1,
+ (void *)(uintptr_t)mem_addr, read_len, false);
+
+ free(rdata);
+ return CMD_RET_SUCCESS;
+}
+
+static bool is_i3c_subcommand(const char *cmd)
+{
+ return !strcmp(cmd, "write") ||
+ !strcmp(cmd, "read") ||
+ !strcmp(cmd, "device_list") ||
+ !strcmp(cmd, "list") ||
+ !strcmp(cmd, "current");
+}
+
+static int do_i3c(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ const char *subcmd = argv[1];
+
+ if (!is_i3c_subcommand(subcmd))
+ return handle_i3c_select(subcmd);
+
+ if (!currdev) {
+ printf("i3c: No I3C controller selected\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (!strcmp(subcmd, "list"))
+ return handle_i3c_list();
+ else if (!strcmp(subcmd, "current"))
+ return handle_i3c_current();
+ else if (!strcmp(subcmd, "device_list"))
+ return handle_i3c_device_list();
+ else if (!strcmp(subcmd, "write"))
+ return handle_i3c_write(argc, argv);
+ else if (!strcmp(subcmd, "read"))
+ return handle_i3c_read(argc, argv);
+
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+ i3c, 5, 1, do_i3c,
+ "access the system i3c",
+ "i3c write <mem_addr> <length> <device_number> - write from memory to device\n"
+ "i3c read <mem_addr> <length> <device_number> - read from device to memory\n"
+ "i3c device_list - List valid target devices\n"
+ "i3c <host_controller> - Select i3c controller\n"
+ "i3c list - List all available i3c controllers\n"
+ "i3c current - Show current i3c controller"
+);
diff --git a/cmd/ide.c b/cmd/ide.c
new file mode 100644
index 00000000000..ed30f946866
--- /dev/null
+++ b/cmd/ide.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000-2011
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+/*
+ * IDE support
+ */
+
+#include <blk.h>
+#include <dm.h>
+#include <config.h>
+#include <watchdog.h>
+#include <command.h>
+#include <image.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+
+#include <ata.h>
+
+#ifdef CONFIG_LED_STATUS
+# include <status_led.h>
+#endif
+
+/* Current I/O Device */
+static int curr_device;
+
+int do_ide(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ if (argc == 2) {
+ if (strncmp(argv[1], "res", 3) == 0) {
+ struct udevice *dev;
+ int ret;
+
+ puts("\nReset IDE: ");
+ ret = uclass_find_first_device(UCLASS_IDE, &dev);
+ ret = device_remove(dev, DM_REMOVE_NORMAL);
+ if (!ret)
+ ret = device_chld_unbind(dev, NULL);
+ if (ret) {
+ printf("Cannot remove IDE (err=%dE)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ ret = uclass_first_device_err(UCLASS_IDE, &dev);
+ if (ret) {
+ printf("Init failed (err=%dE)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+ }
+ }
+
+ return blk_common_cmd(argc, argv, UCLASS_IDE, &curr_device);
+}
+
+int do_diskboot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ return common_diskboot(cmdtp, "ide", argc, argv);
+}
+
+U_BOOT_CMD(ide, 5, 1, do_ide,
+ "IDE sub-system",
+ "reset - reset IDE controller\n"
+ "ide info - show available IDE devices\n"
+ "ide device [dev] - show or set current device\n"
+ "ide part [dev] - print partition table of one or all IDE devices\n"
+ "ide read addr blk# cnt\n"
+ "ide write addr blk# cnt - read/write `cnt'"
+ " blocks starting at block `blk#'\n"
+ " to/from memory address `addr'");
+
+U_BOOT_CMD(diskboot, 3, 1, do_diskboot,
+ "boot from IDE device", "loadAddr dev:part");
diff --git a/cmd/ini.c b/cmd/ini.c
new file mode 100644
index 00000000000..96399017691
--- /dev/null
+++ b/cmd/ini.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * inih -- simple .INI file parser
+ *
+ * Copyright (c) 2009, Brush Technology
+ * Copyright (c) 2012:
+ * Joe Hershberger, National Instruments, joe.hershberger@ni.com
+ * All rights reserved.
+ *
+ * Go to the project home page for more info:
+ * http://code.google.com/p/inih/
+ */
+
+#include <command.h>
+#include <env.h>
+#include <vsprintf.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_INI_MAX_LINE
+#define MAX_LINE CONFIG_INI_MAX_LINE
+#else
+#define MAX_LINE 200
+#endif
+
+#ifdef CONFIG_INI_MAX_SECTION
+#define MAX_SECTION CONFIG_INI_MAX_SECTION
+#else
+#define MAX_SECTION 50
+#endif
+
+#ifdef CONFIG_INI_MAX_NAME
+#define MAX_NAME CONFIG_INI_MAX_NAME
+#else
+#define MAX_NAME 50
+#endif
+
+/* Strip whitespace chars off end of given string, in place. Return s. */
+static char *rstrip(char *s)
+{
+ char *p = s + strlen(s);
+
+ while (p > s && isspace(*--p))
+ *p = '\0';
+ return s;
+}
+
+/* Return pointer to first non-whitespace char in given string. */
+static char *lskip(const char *s)
+{
+ while (*s && isspace(*s))
+ s++;
+ return (char *)s;
+}
+
+/* Return pointer to first char c or ';' comment in given string, or pointer to
+ null at end of string if neither found. ';' must be prefixed by a whitespace
+ character to register as a comment. */
+static char *find_char_or_comment(const char *s, char c)
+{
+ int was_whitespace = 0;
+
+ while (*s && *s != c && !(was_whitespace && *s == ';')) {
+ was_whitespace = isspace(*s);
+ s++;
+ }
+ return (char *)s;
+}
+
+/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
+static char *strncpy0(char *dest, const char *src, size_t size)
+{
+ strncpy(dest, src, size);
+ dest[size - 1] = '\0';
+ return dest;
+}
+
+/* Emulate the behavior of fgets but on memory */
+static char *memgets(char *str, int num, char **mem, size_t *memsize)
+{
+ char *end;
+ int len;
+ int newline = 1;
+
+ end = memchr(*mem, '\n', *memsize);
+ if (end == NULL) {
+ if (*memsize == 0)
+ return NULL;
+ end = *mem + *memsize;
+ newline = 0;
+ }
+ len = min((int)(end - *mem) + newline, num);
+ memcpy(str, *mem, len);
+ if (len < num)
+ str[len] = '\0';
+
+ /* prepare the mem vars for the next call */
+ *memsize -= (end - *mem) + newline;
+ *mem += (end - *mem) + newline;
+
+ return str;
+}
+
+/* Parse given INI-style file. May have [section]s, name=value pairs
+ (whitespace stripped), and comments starting with ';' (semicolon). Section
+ is "" if name=value pair parsed before any section heading. name:value
+ pairs are also supported as a concession to Python's ConfigParser.
+
+ For each name=value pair parsed, call handler function with given user
+ pointer as well as section, name, and value (data only valid for duration
+ of handler call). Handler should return nonzero on success, zero on error.
+
+ Returns 0 on success, line number of first error on parse error (doesn't
+ stop on first error).
+*/
+static int ini_parse(char *filestart, size_t filelen,
+ int (*handler)(void *, char *, char *, char *), void *user)
+{
+ /* Uses a fair bit of stack (use heap instead if you need to) */
+ char line[MAX_LINE];
+ char section[MAX_SECTION] = "";
+ char prev_name[MAX_NAME] = "";
+
+ char *curmem = filestart;
+ char *start;
+ char *end;
+ char *name;
+ char *value;
+ size_t memleft = filelen;
+ int lineno = 0;
+ int error = 0;
+
+ /* Scan through file line by line */
+ while (memgets(line, sizeof(line), &curmem, &memleft) != NULL) {
+ lineno++;
+ start = lskip(rstrip(line));
+
+ if (*start == ';' || *start == '#') {
+ /*
+ * Per Python ConfigParser, allow '#' comments at start
+ * of line
+ */
+ }
+#if CONFIG_INI_ALLOW_MULTILINE
+ else if (*prev_name && *start && start > line) {
+ /*
+ * Non-blank line with leading whitespace, treat as
+ * continuation of previous name's value (as per Python
+ * ConfigParser).
+ */
+ if (!handler(user, section, prev_name, start) && !error)
+ error = lineno;
+ }
+#endif
+ else if (*start == '[') {
+ /* A "[section]" line */
+ end = find_char_or_comment(start + 1, ']');
+ if (*end == ']') {
+ *end = '\0';
+ strncpy0(section, start + 1, sizeof(section));
+ *prev_name = '\0';
+ } else if (!error) {
+ /* No ']' found on section line */
+ error = lineno;
+ }
+ } else if (*start && *start != ';') {
+ /* Not a comment, must be a name[=:]value pair */
+ end = find_char_or_comment(start, '=');
+ if (*end != '=')
+ end = find_char_or_comment(start, ':');
+ if (*end == '=' || *end == ':') {
+ *end = '\0';
+ name = rstrip(start);
+ value = lskip(end + 1);
+ end = find_char_or_comment(value, '\0');
+ if (*end == ';')
+ *end = '\0';
+ rstrip(value);
+ /* Strip double-quotes */
+ if (value[0] == '"' &&
+ value[strlen(value)-1] == '"') {
+ value[strlen(value)-1] = '\0';
+ value += 1;
+ }
+
+ /*
+ * Valid name[=:]value pair found, call handler
+ */
+ strncpy0(prev_name, name, sizeof(prev_name));
+ if (!handler(user, section, name, value) &&
+ !error)
+ error = lineno;
+ } else if (!error)
+ /* No '=' or ':' found on name[=:]value line */
+ error = lineno;
+ }
+ }
+
+ return error;
+}
+
+static int ini_handler(void *user, char *section, char *name, char *value)
+{
+ char *requested_section = (char *)user;
+#ifdef CONFIG_INI_CASE_INSENSITIVE
+ int i;
+
+ for (i = 0; i < strlen(requested_section); i++)
+ requested_section[i] = tolower(requested_section[i]);
+ for (i = 0; i < strlen(section); i++)
+ section[i] = tolower(section[i]);
+#endif
+
+ if (!strcmp(section, requested_section)) {
+#ifdef CONFIG_INI_CASE_INSENSITIVE
+ for (i = 0; i < strlen(name); i++)
+ name[i] = tolower(name[i]);
+ for (i = 0; i < strlen(value); i++)
+ value[i] = tolower(value[i]);
+#endif
+ env_set(name, value);
+ printf("ini: Imported %s as %s\n", name, value);
+ }
+
+ /* success */
+ return 1;
+}
+
+static int do_ini(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ const char *section;
+ char *file_address;
+ size_t file_size;
+
+ if (argc == 1)
+ return CMD_RET_USAGE;
+
+ section = argv[1];
+ file_address = (char *)hextoul(argc < 3 ? env_get("loadaddr") : argv[2],
+ NULL);
+ file_size = (size_t)hextoul(argc < 4 ? env_get("filesize") : argv[3],
+ NULL);
+
+ return ini_parse(file_address, file_size, ini_handler, (void *)section);
+}
+
+U_BOOT_CMD(
+ ini, 4, 0, do_ini,
+ "parse an ini file in memory and merge the specified section into the env",
+ "section [[file-address] file-size]"
+);
diff --git a/cmd/io.c b/cmd/io.c
new file mode 100644
index 00000000000..617373d3cb7
--- /dev/null
+++ b/cmd/io.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2012 The Chromium OS Authors.
+ */
+
+/*
+ * IO space access commands.
+ */
+
+#include <command.h>
+#include <display_options.h>
+#include <vsprintf.h>
+#include <asm/io.h>
+
+/* Display values from last command */
+static ulong last_addr, last_size;
+static ulong last_length = 0x40;
+static ulong base_address;
+
+#define DISP_LINE_LEN 16
+
+/*
+ * IO Display
+ *
+ * Syntax:
+ * iod{.b, .w, .l} {addr}
+ */
+int do_io_iod(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ ulong addr, length, bytes;
+ u8 buf[DISP_LINE_LEN];
+ int size, todo;
+
+ /*
+ * We use the last specified parameters, unless new ones are
+ * entered.
+ */
+ addr = last_addr;
+ size = last_size;
+ length = last_length;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ /*
+ * New command specified. Check for a size specification.
+ * Defaults to long if no or incorrect specification.
+ */
+ size = cmd_get_data_size(argv[0], 4);
+ if (size < 0)
+ return 1;
+
+ /* Address is specified since argc > 1 */
+ addr = hextoul(argv[1], NULL);
+ addr += base_address;
+
+ /*
+ * If another parameter, it is the length to display.
+ * Length is the number of objects, not number of bytes.
+ */
+ if (argc > 2)
+ length = hextoul(argv[2], NULL);
+ }
+
+ bytes = size * length;
+
+ /* Print the lines */
+ for (; bytes > 0; addr += todo) {
+ u8 *ptr = buf;
+ int i;
+
+ todo = min(bytes, (ulong)DISP_LINE_LEN);
+ for (i = 0; i < todo; i += size, ptr += size) {
+ if (size == 4)
+ *(u32 *)ptr = inl(addr + i);
+ else if (size == 2)
+ *(u16 *)ptr = inw(addr + i);
+ else
+ *ptr = inb(addr + i);
+ }
+ print_buffer(addr, buf, size, todo / size,
+ DISP_LINE_LEN / size);
+ bytes -= todo;
+ }
+
+ last_addr = addr;
+ last_length = length;
+ last_size = size;
+
+ return 0;
+}
+
+int do_io_iow(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ ulong addr, val;
+ int size;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ size = cmd_get_data_size(argv[0], 4);
+ if (size < 0)
+ return 1;
+
+ addr = hextoul(argv[1], NULL);
+ val = hextoul(argv[2], NULL);
+
+ if (size == 4)
+ outl((u32) val, addr);
+ else if (size == 2)
+ outw((u16) val, addr);
+ else
+ outb((u8) val, addr);
+
+ return 0;
+}
+
+/**************************************************/
+U_BOOT_CMD(iod, 3, 1, do_io_iod,
+ "IO space display", "[.b, .w, .l] address");
+
+U_BOOT_CMD(iow, 3, 0, do_io_iow,
+ "IO space modify",
+ "[.b, .w, .l] address value");
diff --git a/cmd/iotrace.c b/cmd/iotrace.c
new file mode 100644
index 00000000000..0a041ed8652
--- /dev/null
+++ b/cmd/iotrace.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2014 Google, Inc
+ */
+
+#include <command.h>
+#include <iotrace.h>
+#include <vsprintf.h>
+
+static void do_print_stats(void)
+{
+ ulong start, size, needed_size, offset, count;
+
+ printf("iotrace is %sabled\n", iotrace_get_enabled() ? "en" : "dis");
+ iotrace_get_buffer(&start, &size, &needed_size, &offset, &count);
+ printf("Start: %08lx\n", start);
+ printf("Actual Size: %08lx\n", size);
+ printf("Needed Size: %08lx\n", needed_size);
+ iotrace_get_region(&start, &size);
+ printf("Region: %08lx\n", start);
+ printf("Size: %08lx\n", size);
+ printf("Offset: %08lx\n", offset);
+ printf("Output: %08lx\n", start + offset);
+ printf("Count: %08lx\n", count);
+ printf("CRC32: %08lx\n", (ulong)iotrace_get_checksum());
+}
+
+static void do_print_trace(void)
+{
+ ulong start, size, needed_size, offset, count;
+
+ struct iotrace_record *cur_record;
+
+ iotrace_get_buffer(&start, &size, &needed_size, &offset, &count);
+
+ if (!start || !size || !count)
+ return;
+
+ printf("Timestamp Value Address\n");
+
+ cur_record = (struct iotrace_record *)start;
+ for (int i = 0; i < count; i++) {
+ if (cur_record->flags & IOT_WRITE)
+ printf("%08llu: 0x%08lx --> 0x%08llx\n",
+ cur_record->timestamp,
+ cur_record->value,
+ (unsigned long long)cur_record->addr);
+ else
+ printf("%08llu: 0x%08lx <-- 0x%08llx\n",
+ cur_record->timestamp,
+ cur_record->value,
+ (unsigned long long)cur_record->addr);
+
+ cur_record++;
+ }
+}
+
+static int do_set_buffer(int argc, char *const argv[])
+{
+ ulong addr = 0, size = 0;
+
+ if (argc == 2) {
+ addr = hextoul(*argv++, NULL);
+ size = hextoul(*argv++, NULL);
+ } else if (argc != 0) {
+ return CMD_RET_USAGE;
+ }
+
+ iotrace_set_buffer(addr, size);
+
+ return 0;
+}
+
+static int do_set_region(int argc, char *const argv[])
+{
+ ulong addr = 0, size = 0;
+
+ if (argc == 2) {
+ addr = hextoul(*argv++, NULL);
+ size = hextoul(*argv++, NULL);
+ } else if (argc != 0) {
+ return CMD_RET_USAGE;
+ }
+
+ iotrace_set_region(addr, size);
+
+ return 0;
+}
+
+int do_iotrace(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ const char *cmd = argc < 2 ? NULL : argv[1];
+
+ if (!cmd)
+ return cmd_usage(cmdtp);
+ switch (*cmd) {
+ case 'b':
+ return do_set_buffer(argc - 2, argv + 2);
+ case 'l':
+ return do_set_region(argc - 2, argv + 2);
+ case 'p':
+ iotrace_set_enabled(0);
+ break;
+ case 'r':
+ iotrace_set_enabled(1);
+ break;
+ case 's':
+ do_print_stats();
+ break;
+ case 'd':
+ do_print_trace();
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ iotrace, 4, 1, do_iotrace,
+ "iotrace utility commands",
+ "stats - display iotrace stats\n"
+ "iotrace buffer <address> <size> - set iotrace buffer\n"
+ "iotrace limit <address> <size> - set iotrace region limit\n"
+ "iotrace pause - pause tracing\n"
+ "iotrace resume - resume tracing\n"
+ "iotrace dump - dump iotrace buffer"
+);
diff --git a/cmd/irq.c b/cmd/irq.c
new file mode 100644
index 00000000000..58483d04de8
--- /dev/null
+++ b/cmd/irq.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ */
+
+#include <config.h>
+#include <command.h>
+#include <irq_func.h>
+#include <linux/string.h>
+
+static int do_interrupts(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ /* on */
+ if (strncmp(argv[1], "on", 2) == 0)
+ enable_interrupts();
+ else
+ disable_interrupts();
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ interrupts, 5, 0, do_interrupts,
+ "enable or disable interrupts",
+ "[on, off]"
+);
+
+U_BOOT_CMD(
+ irqinfo, 1, 1, do_irqinfo,
+ "print information about IRQs",
+ ""
+);
diff --git a/cmd/itest.c b/cmd/itest.c
new file mode 100644
index 00000000000..b79512a505d
--- /dev/null
+++ b/cmd/itest.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2003
+ * Tait Electronics Limited, Christchurch, New Zealand
+ */
+
+/*
+ * This file provides a shell like 'test' function to return
+ * true/false from an integer or string compare of two memory
+ * locations or a location and a scalar/literal.
+ * A few parts were lifted from bash 'test' command
+ */
+
+#include <config.h>
+#include <command.h>
+#include <env.h>
+#include <mapmem.h>
+#include <vsprintf.h>
+
+#include <asm/io.h>
+
+#define EQ 0
+#define NE 1
+#define LT 2
+#define GT 3
+#define LE 4
+#define GE 5
+
+struct op_tbl_s {
+ char *op; /* operator string */
+ int opcode; /* internal representation of opcode */
+};
+
+typedef struct op_tbl_s op_tbl_t;
+
+static const op_tbl_t op_table [] = {
+ { "-lt", LT },
+ { "<" , LT },
+ { "-gt", GT },
+ { ">" , GT },
+ { "-eq", EQ },
+ { "==" , EQ },
+ { "-ne", NE },
+ { "!=" , NE },
+ { "<>" , NE },
+ { "-ge", GE },
+ { ">=" , GE },
+ { "-le", LE },
+ { "<=" , LE },
+};
+
+static long evalexp(char *s, int w)
+{
+ long l = 0;
+ unsigned long addr;
+ void *buf;
+
+ /* if the parameter starts with a * then assume is a pointer to the value we want */
+ if (s[0] == '*') {
+ addr = hextoul(&s[1], NULL);
+ buf = map_physmem(addr, w, MAP_WRBACK);
+ if (!buf && addr) {
+ puts("Failed to map physical memory\n");
+ return 0;
+ }
+ switch (w) {
+ case 1:
+ l = (long)(*(u8 *)buf);
+ break;
+ case 2:
+ l = (long)(*(u16 *)buf);
+ break;
+ case 4:
+ l = (long)(*(u32 *)buf);
+ break;
+#ifdef CONFIG_PHYS_64BIT
+ case 8:
+ l = (long)(*(unsigned long *)buf);
+ break;
+#endif
+ }
+ unmap_physmem(buf, w);
+ return l;
+ } else {
+ l = hextoul(s, NULL);
+ }
+
+ /* avoid overflow on mask calculus */
+ return (w >= sizeof(long)) ? l : (l & ((1UL << (w * 8)) - 1));
+}
+
+static char * evalstr(char *s)
+{
+ /* if the parameter starts with a * then assume a string pointer else its a literal */
+ if (s[0] == '*') {
+ return (char *)hextoul(&s[1], NULL);
+ } else if (s[0] == '$') {
+ int i = 2;
+
+ if (s[1] != '{')
+ return NULL;
+
+ while (s[i] != '}') {
+ if (s[i] == 0)
+ return NULL;
+ i++;
+ }
+ s[i] = 0;
+ return env_get((const char *)&s[2]);
+ } else {
+ return s;
+ }
+}
+
+static int stringcomp(char *s, char *t, int op)
+{
+ int p;
+ char *l, *r;
+
+ l = evalstr(s);
+ r = evalstr(t);
+
+ p = strcmp(l, r);
+ switch (op) {
+ case EQ: return (p == 0);
+ case NE: return (p != 0);
+ case LT: return (p < 0);
+ case GT: return (p > 0);
+ case LE: return (p <= 0);
+ case GE: return (p >= 0);
+ }
+ return (0);
+}
+
+static int arithcomp (char *s, char *t, int op, int w)
+{
+ long l, r;
+
+ l = evalexp (s, w);
+ r = evalexp (t, w);
+
+ switch (op) {
+ case EQ: return (l == r);
+ case NE: return (l != r);
+ case LT: return (l < r);
+ case GT: return (l > r);
+ case LE: return (l <= r);
+ case GE: return (l >= r);
+ }
+ return (0);
+}
+
+static int binary_test(char *op, char *arg1, char *arg2, int w)
+{
+ int len, i;
+ const op_tbl_t *optp;
+
+ len = strlen(op);
+
+ for (optp = (op_tbl_t *)&op_table, i = 0;
+ i < ARRAY_SIZE(op_table);
+ optp++, i++) {
+
+ if ((strncmp (op, optp->op, len) == 0) && (len == strlen (optp->op))) {
+ if (w == 0) {
+ return (stringcomp(arg1, arg2, optp->opcode));
+ } else {
+ return (arithcomp (arg1, arg2, optp->opcode, w));
+ }
+ }
+ }
+
+ printf("Unknown operator '%s'\n", op);
+ return 0; /* op code not found */
+}
+
+/* command line interface to the shell test */
+static int do_itest(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int value, w;
+
+ /* Validate arguments */
+ if ((argc != 4))
+ return CMD_RET_USAGE;
+
+ /* Check for a data width specification.
+ * Defaults to long (4) if no specification.
+ * Uses -2 as 'width' for .s (string) so as not to upset existing code
+ */
+ switch (w = cmd_get_data_size(argv[0], 4)) {
+ case 1:
+ case 2:
+ case 4:
+#ifdef CONFIG_PHYS_64BIT
+ case 8:
+#endif
+ value = binary_test (argv[2], argv[1], argv[3], w);
+ break;
+ case CMD_DATA_SIZE_STR:
+ value = binary_test (argv[2], argv[1], argv[3], 0);
+ break;
+ case CMD_DATA_SIZE_ERR:
+ default:
+ puts("Invalid data width specifier\n");
+ value = 0;
+ break;
+ }
+
+ return !value;
+}
+
+U_BOOT_CMD(
+ itest, 4, 0, do_itest,
+ "return true/false on integer compare",
+#ifdef CONFIG_PHYS_64BIT
+ "[.b, .w, .l, .q, .s] [*]value1 <op> [*]value2"
+#else
+ "[.b, .w, .l, .s] [*]value1 <op> [*]value2"
+#endif
+);
diff --git a/cmd/jffs2.c b/cmd/jffs2.c
new file mode 100644
index 00000000000..89d336f5958
--- /dev/null
+++ b/cmd/jffs2.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2002
+ * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de>
+ *
+ * (C) Copyright 2003
+ * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de>
+ *
+ * (C) Copyright 2005
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * Added support for reading flash partition table from environment.
+ * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4
+ * kernel tree.
+ *
+ * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $
+ * Copyright 2002 SYSGO Real-Time Solutions GmbH
+ */
+
+/*
+ * Three environment variables are used by the parsing routines:
+ *
+ * 'partition' - keeps current partition identifier
+ *
+ * partition := <part-id>
+ * <part-id> := <dev-id>,part_num
+ *
+ *
+ * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping
+ *
+ * mtdids=<idmap>[,<idmap>,...]
+ *
+ * <idmap> := <dev-id>=<mtd-id>
+ * <dev-id> := 'nand'|'nor'|'onenand'<dev-num>
+ * <dev-num> := mtd device number, 0...
+ * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)
+ *
+ *
+ * 'mtdparts' - partition list
+ *
+ * mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]
+ *
+ * <mtd-def> := <mtd-id>:<part-def>[,<part-def>...]
+ * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)
+ * <part-def> := <size>[@<offset>][<name>][<ro-flag>]
+ * <size> := standard linux memsize OR '-' to denote all remaining space
+ * <offset> := partition start offset within the device
+ * <name> := '(' NAME ')'
+ * <ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)
+ *
+ * Notes:
+ * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping
+ * - if the above variables are not set defaults for a given target are used
+ *
+ * Examples:
+ *
+ * 1 NOR Flash, with 1 single writable partition:
+ * mtdids=nor0=edb7312-nor
+ * mtdparts=mtdparts=edb7312-nor:-
+ *
+ * 1 NOR Flash with 2 partitions, 1 NAND with one
+ * mtdids=nor0=edb7312-nor,nand0=edb7312-nand
+ * mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
+ *
+ */
+
+/*
+ * JFFS2/CRAMFS support
+ */
+#include <command.h>
+#include <env.h>
+#if defined(CONFIG_CMD_FLASH)
+#include <flash.h>
+#endif
+#include <image.h>
+#include <malloc.h>
+#include <jffs2/jffs2.h>
+#include <linux/bug.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <cramfs/cramfs_fs.h>
+
+#if defined(CONFIG_CMD_NAND)
+#include <linux/mtd/rawnand.h>
+#include <nand.h>
+#endif
+
+#if defined(CONFIG_CMD_ONENAND)
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+#include <onenand_uboot.h>
+#endif
+
+/* enable/disable debugging messages */
+#define DEBUG_JFFS
+#undef DEBUG_JFFS
+
+#ifdef DEBUG_JFFS
+# define DEBUGF(fmt, args...) printf(fmt ,##args)
+#else
+# define DEBUGF(fmt, args...)
+#endif
+
+/* special size referring to all the remaining space in a partition */
+#define SIZE_REMAINING 0xFFFFFFFF
+
+/* special offset value, it is used when not provided by user
+ *
+ * this value is used temporarily during parsing, later such offests
+ * are recalculated */
+#define OFFSET_NOT_SPECIFIED 0xFFFFFFFF
+
+/* minimum partition size */
+#define MIN_PART_SIZE 4096
+
+/* this flag needs to be set in part_info struct mask_flags
+ * field for read-only partitions */
+#define MTD_WRITEABLE_CMD 1
+
+/* current active device and partition number */
+#ifdef CONFIG_CMD_MTDPARTS
+/* Use the ones declared in cmd_mtdparts.c */
+extern struct mtd_device *current_mtd_dev;
+extern u8 current_mtd_partnum;
+#else
+/* Use local ones */
+struct mtd_device *current_mtd_dev = NULL;
+u8 current_mtd_partnum = 0;
+#endif
+
+#if defined(CONFIG_CMD_CRAMFS)
+extern int cramfs_check (struct part_info *info);
+extern int cramfs_load (char *loadoffset, struct part_info *info, char *filename);
+extern int cramfs_ls (struct part_info *info, char *filename);
+extern int cramfs_info (struct part_info *info);
+#else
+/* defining empty macros for function names is ugly but avoids ifdef clutter
+ * all over the code */
+#define cramfs_check(x) (0)
+#define cramfs_load(x,y,z) (-1)
+#define cramfs_ls(x,y) (0)
+#define cramfs_info(x) (0)
+#endif
+
+#ifndef CONFIG_CMD_MTDPARTS
+/**
+ * Check device number to be within valid range for given device type.
+ *
+ * @param dev device to validate
+ * Return: 0 if device is valid, 1 otherwise
+ */
+static int mtd_device_validate(u8 type, u8 num, u32 *size)
+{
+ if (type == MTD_DEV_TYPE_NOR) {
+#if defined(CONFIG_CMD_FLASH)
+ if (num < CONFIG_SYS_MAX_FLASH_BANKS) {
+ *size = flash_info[num].size;
+
+ return 0;
+ }
+
+ printf("no such FLASH device: %s%d (valid range 0 ... %d\n",
+ MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_FLASH_BANKS - 1);
+#else
+ printf("support for FLASH devices not present\n");
+#endif
+ } else if (type == MTD_DEV_TYPE_NAND) {
+#if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
+ struct mtd_info *mtd = get_nand_dev_by_index(num);
+ if (mtd) {
+ *size = mtd->size;
+ return 0;
+ }
+
+ printf("no such NAND device: %s%d (valid range 0 ... %d)\n",
+ MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_NAND_DEVICE - 1);
+#else
+ printf("support for NAND devices not present\n");
+#endif
+ } else if (type == MTD_DEV_TYPE_ONENAND) {
+#if defined(CONFIG_CMD_ONENAND)
+ *size = onenand_mtd.size;
+ return 0;
+#else
+ printf("support for OneNAND devices not present\n");
+#endif
+ } else
+ printf("Unknown defice type %d\n", type);
+
+ return 1;
+}
+
+/**
+ * Parse device id string <dev-id> := 'nand'|'nor'|'onenand'<dev-num>,
+ * return device type and number.
+ *
+ * @param id string describing device id
+ * @param ret_id output pointer to next char after parse completes (output)
+ * @param dev_type parsed device type (output)
+ * @param dev_num parsed device number (output)
+ * Return: 0 on success, 1 otherwise
+ */
+static int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num)
+{
+ const char *p = id;
+
+ *dev_type = 0;
+ if (strncmp(p, "nand", 4) == 0) {
+ *dev_type = MTD_DEV_TYPE_NAND;
+ p += 4;
+ } else if (strncmp(p, "nor", 3) == 0) {
+ *dev_type = MTD_DEV_TYPE_NOR;
+ p += 3;
+ } else if (strncmp(p, "onenand", 7) == 0) {
+ *dev_type = MTD_DEV_TYPE_ONENAND;
+ p += 7;
+ } else {
+ printf("incorrect device type in %s\n", id);
+ return 1;
+ }
+
+ if (!isdigit(*p)) {
+ printf("incorrect device number in %s\n", id);
+ return 1;
+ }
+
+ *dev_num = simple_strtoul(p, (char **)&p, 0);
+ if (ret_id)
+ *ret_id = p;
+ return 0;
+}
+
+/*
+ * 'Static' version of command line mtdparts_init() routine. Single partition on
+ * a single device configuration.
+ */
+
+/**
+ * Calculate sector size.
+ *
+ * Return: sector size
+ */
+static inline u32 get_part_sector_size_nand(struct mtdids *id)
+{
+#if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
+ struct mtd_info *mtd;
+
+ mtd = get_nand_dev_by_index(id->num);
+
+ return mtd->erasesize;
+#else
+ BUG();
+ return 0;
+#endif
+}
+
+static inline u32 get_part_sector_size_nor(struct mtdids *id, struct part_info *part)
+{
+#if defined(CONFIG_CMD_FLASH)
+ u32 end_phys, start_phys, sector_size = 0, size = 0;
+ int i;
+ flash_info_t *flash;
+
+ flash = &flash_info[id->num];
+
+ start_phys = flash->start[0] + part->offset;
+ end_phys = start_phys + part->size - 1;
+
+ for (i = 0; i < flash->sector_count; i++) {
+ if (flash->start[i] >= end_phys)
+ break;
+
+ if (flash->start[i] >= start_phys) {
+ if (i == flash->sector_count - 1) {
+ size = flash->start[0] + flash->size - flash->start[i];
+ } else {
+ size = flash->start[i+1] - flash->start[i];
+ }
+
+ if (sector_size < size)
+ sector_size = size;
+ }
+ }
+
+ return sector_size;
+#else
+ BUG();
+ return 0;
+#endif
+}
+
+static inline u32 get_part_sector_size_onenand(void)
+{
+#if defined(CONFIG_CMD_ONENAND)
+ struct mtd_info *mtd;
+
+ mtd = &onenand_mtd;
+
+ return mtd->erasesize;
+#else
+ BUG();
+ return 0;
+#endif
+}
+
+static inline u32 get_part_sector_size(struct mtdids *id, struct part_info *part)
+{
+ if (id->type == MTD_DEV_TYPE_NAND)
+ return get_part_sector_size_nand(id);
+ else if (id->type == MTD_DEV_TYPE_NOR)
+ return get_part_sector_size_nor(id, part);
+ else if (id->type == MTD_DEV_TYPE_ONENAND)
+ return get_part_sector_size_onenand();
+ else
+ DEBUGF("Error: Unknown device type.\n");
+
+ return 0;
+}
+
+/**
+ * Parse and initialize global mtdids mapping and create global
+ * device/partition list.
+ *
+ * 'Static' version of command line mtdparts_init() routine. Single partition on
+ * a single device configuration.
+ *
+ * Return: 0 on success, 1 otherwise
+ */
+int mtdparts_init(void)
+{
+ static int initialized = 0;
+ u32 size;
+ char *dev_name;
+
+ DEBUGF("\n---mtdparts_init---\n");
+ if (!initialized) {
+ struct mtdids *id;
+ struct part_info *part;
+
+ initialized = 1;
+ current_mtd_dev = (struct mtd_device *)
+ malloc(sizeof(struct mtd_device) +
+ sizeof(struct part_info) +
+ sizeof(struct mtdids));
+ if (!current_mtd_dev) {
+ printf("out of memory\n");
+ return 1;
+ }
+ memset(current_mtd_dev, 0, sizeof(struct mtd_device) +
+ sizeof(struct part_info) + sizeof(struct mtdids));
+
+ id = (struct mtdids *)(current_mtd_dev + 1);
+ part = (struct part_info *)(id + 1);
+
+ /* id */
+ id->mtd_id = "single part";
+
+ dev_name = CONFIG_JFFS2_DEV;
+
+ if ((mtd_id_parse(dev_name, NULL, &id->type, &id->num) != 0) ||
+ (mtd_device_validate(id->type, id->num, &size) != 0)) {
+ printf("incorrect device: %s%d\n", MTD_DEV_TYPE(id->type), id->num);
+ free(current_mtd_dev);
+ return 1;
+ }
+ id->size = size;
+ INIT_LIST_HEAD(&id->link);
+
+ DEBUGF("dev id: type = %d, num = %d, size = 0x%08lx, mtd_id = %s\n",
+ id->type, id->num, id->size, id->mtd_id);
+
+ /* partition */
+ part->name = "static";
+ part->auto_name = 0;
+
+ part->size = CONFIG_JFFS2_PART_SIZE;
+
+ part->offset = CONFIG_JFFS2_PART_OFFSET;
+
+ part->dev = current_mtd_dev;
+ INIT_LIST_HEAD(&part->link);
+
+ /* recalculate size if needed */
+ if (part->size == SIZE_REMAINING)
+ part->size = id->size - part->offset;
+
+ part->sector_size = get_part_sector_size(id, part);
+
+ DEBUGF("part : name = %s, size = 0x%08lx, offset = 0x%08lx\n",
+ part->name, part->size, part->offset);
+
+ /* device */
+ current_mtd_dev->id = id;
+ INIT_LIST_HEAD(&current_mtd_dev->link);
+ current_mtd_dev->num_parts = 1;
+ INIT_LIST_HEAD(&current_mtd_dev->parts);
+ list_add(&part->link, &current_mtd_dev->parts);
+ }
+
+ return 0;
+}
+#endif /* #ifndef CONFIG_CMD_MTDPARTS */
+
+/**
+ * Return pointer to the partition of a requested number from a requested
+ * device.
+ *
+ * @param dev device that is to be searched for a partition
+ * @param part_num requested partition number
+ * Return: pointer to the part_info, NULL otherwise
+ */
+static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num)
+{
+ struct list_head *entry;
+ struct part_info *part;
+ int num;
+
+ if (!dev)
+ return NULL;
+
+ DEBUGF("\n--- jffs2_part_info: partition number %d for device %s%d (%s)\n",
+ part_num, MTD_DEV_TYPE(dev->id->type),
+ dev->id->num, dev->id->mtd_id);
+
+ if (part_num >= dev->num_parts) {
+ printf("invalid partition number %d for device %s%d (%s)\n",
+ part_num, MTD_DEV_TYPE(dev->id->type),
+ dev->id->num, dev->id->mtd_id);
+ return NULL;
+ }
+
+ /* locate partition number, return it */
+ num = 0;
+ list_for_each(entry, &dev->parts) {
+ part = list_entry(entry, struct part_info, link);
+
+ if (part_num == num++) {
+ return part;
+ }
+ }
+
+ return NULL;
+}
+
+/***************************************************/
+/* U-Boot commands */
+/***************************************************/
+
+/**
+ * Routine implementing fsload u-boot command. This routine tries to load
+ * a requested file from jffs2/cramfs filesystem on a current partition.
+ *
+ * @param cmdtp command internal data
+ * @param flag command flag
+ * @param argc number of arguments supplied to the command
+ * @param argv arguments list
+ * Return: 0 on success, 1 otherwise
+ */
+int do_jffs2_fsload(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *fsname;
+ char *filename;
+ int size;
+ struct part_info *part;
+ ulong offset = image_load_addr;
+
+ /* pre-set Boot file name */
+ filename = env_get("bootfile");
+ if (!filename)
+ filename = "uImage";
+
+ if (argc == 2) {
+ filename = argv[1];
+ }
+ if (argc == 3) {
+ offset = hextoul(argv[1], NULL);
+ image_load_addr = offset;
+ filename = argv[2];
+ }
+
+ /* make sure we are in sync with env variables */
+ if (mtdparts_init() !=0)
+ return 1;
+
+ if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){
+
+ /* check partition type for cramfs */
+ fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2");
+ printf("### %s loading '%s' to 0x%lx\n", fsname, filename, offset);
+
+ if (cramfs_check(part)) {
+ size = cramfs_load ((char *) offset, part, filename);
+ } else {
+ /* if this is not cramfs assume jffs2 */
+ size = jffs2_1pass_load((char *)offset, part, filename);
+ }
+
+ if (size > 0) {
+ printf("### %s load complete: %d bytes loaded to 0x%lx\n",
+ fsname, size, offset);
+ env_set_hex("filesize", size);
+ } else {
+ printf("### %s LOAD ERROR<%x> for %s!\n", fsname, size, filename);
+ }
+
+ return !(size > 0);
+ }
+ return 1;
+}
+
+/**
+ * Routine implementing u-boot ls command which lists content of a given
+ * directory on a current partition.
+ *
+ * @param cmdtp command internal data
+ * @param flag command flag
+ * @param argc number of arguments supplied to the command
+ * @param argv arguments list
+ * Return: 0 on success, 1 otherwise
+ */
+int do_jffs2_ls(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char *filename = "/";
+ int ret;
+ struct part_info *part;
+
+ if (argc == 2)
+ filename = argv[1];
+
+ /* make sure we are in sync with env variables */
+ if (mtdparts_init() !=0)
+ return 1;
+
+ if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){
+
+ /* check partition type for cramfs */
+ if (cramfs_check(part)) {
+ ret = cramfs_ls (part, filename);
+ } else {
+ /* if this is not cramfs assume jffs2 */
+ ret = jffs2_1pass_ls(part, filename);
+ }
+
+ return ret ? 0 : 1;
+ }
+ return 1;
+}
+
+/**
+ * Routine implementing u-boot fsinfo command. This routine prints out
+ * miscellaneous filesystem informations/statistics.
+ *
+ * @param cmdtp command internal data
+ * @param flag command flag
+ * @param argc number of arguments supplied to the command
+ * @param argv arguments list
+ * Return: 0 on success, 1 otherwise
+ */
+int do_jffs2_fsinfo(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct part_info *part;
+ char *fsname;
+ int ret;
+
+ /* make sure we are in sync with env variables */
+ if (mtdparts_init() !=0)
+ return 1;
+
+ if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){
+
+ /* check partition type for cramfs */
+ fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2");
+ printf("### filesystem type is %s\n", fsname);
+
+ if (cramfs_check(part)) {
+ ret = cramfs_info (part);
+ } else {
+ /* if this is not cramfs assume jffs2 */
+ ret = jffs2_1pass_info(part);
+ }
+
+ return ret ? 0 : 1;
+ }
+ return 1;
+}
+
+/***************************************************/
+U_BOOT_CMD(
+ fsload, 3, 0, do_jffs2_fsload,
+ "load binary file from a filesystem image",
+ "[ off ] [ filename ]\n"
+ " - load binary file from flash bank\n"
+ " with offset 'off'"
+);
+U_BOOT_CMD(
+ fsls, 2, 1, do_jffs2_ls,
+ "list files in a directory (default /)",
+ "[ directory ]"
+);
+
+U_BOOT_CMD(
+ fsinfo, 1, 1, do_jffs2_fsinfo,
+ "print information about filesystems",
+ ""
+);
+/***************************************************/
diff --git a/cmd/kaslrseed.c b/cmd/kaslrseed.c
new file mode 100644
index 00000000000..2ad983a11f9
--- /dev/null
+++ b/cmd/kaslrseed.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The 'kaslrseed' command takes bytes from the hardware random number
+ * generator and uses them to set the kaslr-seed value in the chosen node.
+ *
+ * Copyright (c) 2021, Chris Morgan <macromorgan@hotmail.com>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <hexdump.h>
+#include <malloc.h>
+#include <rng.h>
+#include <fdt_support.h>
+
+static int do_kaslr_seed(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int err = CMD_RET_SUCCESS;
+
+ printf("Notice: a /chosen/kaslr-seed is automatically added to the device-tree when booted via booti/bootm/bootz therefore using this command is likely no longer needed\n");
+
+ if (!working_fdt) {
+ printf("No FDT memory address configured. Please configure\n"
+ "the FDT address via \"fdt addr <address>\" command.\n"
+ "Aborting!\n");
+ err = CMD_RET_FAILURE;
+ } else {
+ if (fdt_kaslrseed(working_fdt, true) < 0)
+ err = CMD_RET_FAILURE;
+ }
+
+ return cmd_process_error(cmdtp, err);
+}
+
+U_BOOT_LONGHELP(kaslrseed,
+ "\n"
+ " - append random bytes to chosen kaslr-seed node\n");
+
+U_BOOT_CMD(
+ kaslrseed, 1, 0, do_kaslr_seed,
+ "feed bytes from the hardware random number generator to the kaslr-seed",
+ kaslrseed_help_text
+);
diff --git a/cmd/led.c b/cmd/led.c
new file mode 100644
index 00000000000..91fb856ee59
--- /dev/null
+++ b/cmd/led.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2017 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <led.h>
+#include <dm/uclass-internal.h>
+
+#define LED_TOGGLE LEDST_COUNT
+
+static const char *const state_label[] = {
+ [LEDST_OFF] = "off",
+ [LEDST_ON] = "on",
+ [LEDST_TOGGLE] = "toggle",
+ [LEDST_BLINK] = "blink",
+};
+
+enum led_state_t get_led_cmd(char *var)
+{
+ int i;
+
+ for (i = 0; i < LEDST_COUNT; i++) {
+ if (!strncmp(var, state_label[i], strlen(var)))
+ return i;
+ }
+
+ return -1;
+}
+
+static int show_led_state(struct udevice *dev)
+{
+ int ret;
+
+ ret = led_get_state(dev);
+ if (ret >= LEDST_COUNT)
+ ret = -EINVAL;
+ if (ret >= 0)
+ printf("%s\n", state_label[ret]);
+
+ return ret;
+}
+
+static int list_leds(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ for (uclass_find_first_device(UCLASS_LED, &dev);
+ dev;
+ uclass_find_next_device(&dev)) {
+ struct led_uc_plat *plat = dev_get_uclass_plat(dev);
+
+ if (!plat->label)
+ continue;
+ printf("%-15s ", plat->label);
+ if (device_active(dev)) {
+ ret = show_led_state(dev);
+ if (ret < 0)
+ printf("Error %d\n", ret);
+ } else {
+ printf("<inactive>\n");
+ }
+ }
+
+ return 0;
+}
+
+int do_led(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ enum led_state_t cmd;
+ const char *led_label;
+ struct udevice *dev;
+ int freq_ms = 0;
+ int ret;
+
+ /* Validate arguments */
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ led_label = argv[1];
+ if (strncmp(led_label, "list", 4) == 0)
+ return list_leds();
+
+ cmd = argc > 2 ? get_led_cmd(argv[2]) : LEDST_COUNT;
+ if (cmd == LEDST_BLINK) {
+ if (argc < 4)
+ return CMD_RET_USAGE;
+ freq_ms = dectoul(argv[3], NULL);
+ }
+ ret = led_get_by_label(led_label, &dev);
+ if (ret) {
+ printf("LED '%s' not found (err=%d)\n", led_label, ret);
+ return CMD_RET_FAILURE;
+ }
+ switch (cmd) {
+ case LEDST_OFF:
+ case LEDST_ON:
+ case LEDST_TOGGLE:
+ ret = led_set_state(dev, cmd);
+ break;
+ case LEDST_BLINK:
+ ret = led_set_period(dev, freq_ms);
+ if (!ret)
+ ret = led_set_state(dev, LEDST_BLINK);
+ break;
+ case LEDST_COUNT:
+ printf("LED '%s': ", led_label);
+ ret = show_led_state(dev);
+ break;
+ }
+ if (ret < 0) {
+ printf("LED '%s' operation failed (err=%d)\n", led_label, ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_LED_BLINK) || defined(CONFIG_LED_SW_BLINK)
+#define BLINK "|blink [blink-freq in ms]"
+#else
+#define BLINK ""
+#endif
+
+U_BOOT_CMD(
+ led, 4, 1, do_led,
+ "manage LEDs",
+ "<led_label> on|off|toggle" BLINK "\tChange LED state\n"
+ "led <led_label>\tGet LED state\n"
+ "led list\t\tshow a list of LEDs"
+);
diff --git a/cmd/legacy-mtd-utils.c b/cmd/legacy-mtd-utils.c
new file mode 100644
index 00000000000..34a6da01947
--- /dev/null
+++ b/cmd/legacy-mtd-utils.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <jffs2/jffs2.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/string.h>
+
+static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size,
+ loff_t *maxsize, int devtype)
+{
+#ifdef CONFIG_CMD_MTDPARTS
+ struct mtd_device *dev;
+ struct part_info *part;
+ u8 pnum;
+ int ret;
+
+ ret = mtdparts_init();
+ if (ret)
+ return ret;
+
+ ret = find_dev_and_part(partname, &dev, &pnum, &part);
+ if (ret)
+ return ret;
+
+ if (dev->id->type != devtype) {
+ printf("not same typ %d != %d\n", dev->id->type, devtype);
+ return -1;
+ }
+
+ *off = part->offset;
+ *size = part->size;
+ *maxsize = part->size;
+ *idx = dev->id->num;
+
+ return 0;
+#else
+ puts("mtdparts support missing.\n");
+ return -1;
+#endif
+}
+
+int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size,
+ loff_t *maxsize, int devtype, uint64_t chipsize)
+{
+ if (!str2off(arg, off))
+ return get_part(arg, idx, off, size, maxsize, devtype);
+
+ if (*off >= chipsize) {
+ puts("Offset exceeds device limit\n");
+ return -1;
+ }
+
+ *maxsize = chipsize - *off;
+ *size = *maxsize;
+ return 0;
+}
+
+int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off,
+ loff_t *size, loff_t *maxsize, int devtype,
+ uint64_t chipsize)
+{
+ int ret;
+
+ if (argc == 0) {
+ *off = 0;
+ *size = chipsize;
+ *maxsize = *size;
+ goto print;
+ }
+
+ ret = mtd_arg_off(argv[0], idx, off, size, maxsize, devtype,
+ chipsize);
+ if (ret)
+ return ret;
+
+ if (argc == 1)
+ goto print;
+
+ if (!str2off(argv[1], size)) {
+ printf("'%s' is not a number\n", argv[1]);
+ return -1;
+ }
+
+ if (*size > *maxsize) {
+ puts("Size exceeds partition or device limit\n");
+ return -1;
+ }
+
+ if (*size == 0) {
+ debug("ERROR: Invalid size 0\n");
+ return -1;
+ }
+
+print:
+ printf("device %d ", *idx);
+ if (*size == chipsize)
+ puts("whole chip\n");
+ else
+ printf("offset 0x%llx, size 0x%llx\n",
+ (unsigned long long)*off, (unsigned long long)*size);
+ return 0;
+}
diff --git a/cmd/legacy-mtd-utils.h b/cmd/legacy-mtd-utils.h
new file mode 100644
index 00000000000..ac441d5558a
--- /dev/null
+++ b/cmd/legacy-mtd-utils.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef __LEGACY_MTD_UTILS_H
+#define __LEGACY_MTD_UTILS_H
+
+int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size,
+ loff_t *maxsize, int devtype, uint64_t chipsize);
+int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off,
+ loff_t *size, loff_t *maxsize, int devtype,
+ uint64_t chipsize);
+
+#endif /* LEGACY_MTD_UTILS_H */
diff --git a/cmd/legacy_led.c b/cmd/legacy_led.c
new file mode 100644
index 00000000000..db312ae6e2d
--- /dev/null
+++ b/cmd/legacy_led.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2010
+ * Jason Kridner <jkridner@beagleboard.org>
+ *
+ * Based on cmd_led.c patch from:
+ * http://www.mail-archive.com/u-boot@lists.denx.de/msg06873.html
+ * (C) Copyright 2008
+ * Ulf Samuelsson <ulf.samuelsson@atmel.com>
+ */
+
+#include <command.h>
+#include <status_led.h>
+#include <vsprintf.h>
+#include <linux/string.h>
+
+struct led_tbl_s {
+ char *string; /* String for use in the command */
+ led_id_t mask; /* Mask used for calling __led_set() */
+ void (*off)(void); /* Optional function for turning LED off */
+ void (*on)(void); /* Optional function for turning LED on */
+ void (*toggle)(void);/* Optional function for toggling LED */
+};
+
+typedef struct led_tbl_s led_tbl_t;
+
+static const led_tbl_t led_commands[] = {
+#ifdef CONFIG_LED_STATUS_BOARD_SPECIFIC
+#ifdef CONFIG_LED_STATUS0
+ { "0", CONFIG_LED_STATUS_BIT, NULL, NULL, NULL },
+#endif
+#ifdef CONFIG_LED_STATUS1
+ { "1", CONFIG_LED_STATUS_BIT1, NULL, NULL, NULL },
+#endif
+#ifdef CONFIG_LED_STATUS2
+ { "2", CONFIG_LED_STATUS_BIT2, NULL, NULL, NULL },
+#endif
+#ifdef CONFIG_LED_STATUS3
+ { "3", CONFIG_LED_STATUS_BIT3, NULL, NULL, NULL },
+#endif
+#ifdef CONFIG_LED_STATUS4
+ { "4", CONFIG_LED_STATUS_BIT4, NULL, NULL, NULL },
+#endif
+#ifdef CONFIG_LED_STATUS5
+ { "5", CONFIG_LED_STATUS_BIT5, NULL, NULL, NULL },
+#endif
+#endif
+#ifdef CONFIG_LED_STATUS_GREEN
+ { "green", CONFIG_LED_STATUS_GREEN, green_led_off, green_led_on, NULL },
+#endif
+#ifdef CONFIG_LED_STATUS_YELLOW
+ { "yellow", CONFIG_LED_STATUS_YELLOW, yellow_led_off, yellow_led_on,
+ NULL },
+#endif
+#ifdef CONFIG_LED_STATUS_RED
+ { "red", CONFIG_LED_STATUS_RED, red_led_off, red_led_on, NULL },
+#endif
+#ifdef CONFIG_LED_STATUS_BLUE
+ { "blue", CONFIG_LED_STATUS_BLUE, blue_led_off, blue_led_on, NULL },
+#endif
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+enum led_cmd { LED_ON, LED_OFF, LED_TOGGLE, LED_BLINK };
+
+enum led_cmd get_led_cmd(char *var)
+{
+ if (strcmp(var, "off") == 0)
+ return LED_OFF;
+ if (strcmp(var, "on") == 0)
+ return LED_ON;
+ if (strcmp(var, "toggle") == 0)
+ return LED_TOGGLE;
+ if (strcmp(var, "blink") == 0)
+ return LED_BLINK;
+
+ return -1;
+}
+
+/*
+ * LED drivers providing a blinking LED functionality, like the
+ * PCA9551, can override this empty weak function
+ */
+void __weak __led_blink(led_id_t mask, int freq)
+{
+}
+
+int do_legacy_led(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int i, match = 0;
+ enum led_cmd cmd;
+ int freq;
+
+ /* Validate arguments */
+ if ((argc < 3) || (argc > 4))
+ return CMD_RET_USAGE;
+
+ cmd = get_led_cmd(argv[2]);
+ if (cmd < 0) {
+ return CMD_RET_USAGE;
+ }
+
+ for (i = 0; led_commands[i].string; i++) {
+ if ((strcmp("all", argv[1]) == 0) ||
+ (strcmp(led_commands[i].string, argv[1]) == 0)) {
+ match = 1;
+ switch (cmd) {
+ case LED_ON:
+ if (led_commands[i].on)
+ led_commands[i].on();
+ else
+ __led_set(led_commands[i].mask,
+ CONFIG_LED_STATUS_ON);
+ break;
+ case LED_OFF:
+ if (led_commands[i].off)
+ led_commands[i].off();
+ else
+ __led_set(led_commands[i].mask,
+ CONFIG_LED_STATUS_OFF);
+ break;
+ case LED_TOGGLE:
+ if (led_commands[i].toggle)
+ led_commands[i].toggle();
+ else
+ __led_toggle(led_commands[i].mask);
+ break;
+ case LED_BLINK:
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ freq = dectoul(argv[3], NULL);
+ __led_blink(led_commands[i].mask, freq);
+ }
+ /* Need to set only 1 led if led_name wasn't 'all' */
+ if (strcmp("all", argv[1]) != 0)
+ break;
+ }
+ }
+
+ /* If we ran out of matches, print Usage */
+ if (!match) {
+ return CMD_RET_USAGE;
+ }
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ led, 4, 1, do_legacy_led,
+ "["
+#ifdef CONFIG_LED_STATUS_BOARD_SPECIFIC
+#ifdef CONFIG_LED_STATUS0
+ "0|"
+#endif
+#ifdef CONFIG_LED_STATUS1
+ "1|"
+#endif
+#ifdef CONFIG_LED_STATUS2
+ "2|"
+#endif
+#ifdef CONFIG_LED_STATUS3
+ "3|"
+#endif
+#ifdef CONFIG_LED_STATUS4
+ "4|"
+#endif
+#ifdef CONFIG_LED_STATUS5
+ "5|"
+#endif
+#endif
+#ifdef CONFIG_LED_STATUS_GREEN
+ "green|"
+#endif
+#ifdef CONFIG_LED_STATUS_YELLOW
+ "yellow|"
+#endif
+#ifdef CONFIG_LED_STATUS_RED
+ "red|"
+#endif
+#ifdef CONFIG_LED_STATUS_BLUE
+ "blue|"
+#endif
+ "all] [on|off|toggle|blink] [blink-freq in ms]",
+ "[led_name] [on|off|toggle|blink] sets or clears led(s)"
+);
diff --git a/cmd/license.c b/cmd/license.c
new file mode 100644
index 00000000000..161663ff29c
--- /dev/null
+++ b/cmd/license.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2007 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ */
+
+#include <command.h>
+#include <gzip.h>
+#include <malloc.h>
+
+#include "license_data_gz.h"
+#include "license_data_size.h"
+
+static int do_license(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *dst;
+ unsigned long len = data_size;
+ int ret = CMD_RET_SUCCESS;
+
+ dst = malloc(data_size + 1);
+ if (!dst)
+ return CMD_RET_FAILURE;
+
+ ret = gunzip(dst, data_size, (unsigned char *)data_gz, &len);
+ if (ret) {
+ printf("Error uncompressing license text\n");
+ ret = CMD_RET_FAILURE;
+ goto free;
+ }
+
+ dst[data_size] = 0;
+ puts(dst);
+
+free:
+ free(dst);
+
+ return ret;
+}
+
+U_BOOT_CMD(
+ license, 1, 1, do_license,
+ "print GPL license text",
+ ""
+);
diff --git a/cmd/load.c b/cmd/load.c
new file mode 100644
index 00000000000..159767aa7f7
--- /dev/null
+++ b/cmd/load.c
@@ -0,0 +1,1193 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000-2004
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+/*
+ * Serial up- and download support
+ */
+#include <command.h>
+#include <console.h>
+#include <cpu_func.h>
+#include <efi_loader.h>
+#include <env.h>
+#include <exports.h>
+#ifdef CONFIG_MTD_NOR_FLASH
+#include <flash.h>
+#endif
+#include <image.h>
+#include <lmb.h>
+#include <mapmem.h>
+#include <net.h>
+#include <s_record.h>
+#include <serial.h>
+#include <xyzModem.h>
+#include <asm/cache.h>
+#include <asm/global_data.h>
+#include <linux/delay.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if defined(CONFIG_CMD_LOADB)
+static ulong load_serial_ymodem(ulong offset, int mode);
+#endif
+
+#if defined(CONFIG_CMD_LOADS)
+static ulong load_serial(long offset);
+static int read_record(char *buf, ulong len);
+# if defined(CONFIG_CMD_SAVES)
+static int save_serial(ulong offset, ulong size);
+static int write_record(char *buf);
+#endif
+
+static int do_echo = 1;
+#endif
+
+/* -------------------------------------------------------------------- */
+
+#if defined(CONFIG_CMD_LOADS)
+static int do_load_serial(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ long offset = 0;
+ ulong addr;
+ int i;
+ char *env_echo;
+ int rcode = 0;
+#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
+ int load_baudrate, current_baudrate;
+
+ load_baudrate = current_baudrate = gd->baudrate;
+#endif
+
+ env_echo = env_get("loads_echo");
+ if (env_echo && *env_echo == '1')
+ do_echo = 1;
+ else
+ do_echo = 0;
+
+#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
+ if (argc >= 2) {
+ offset = simple_strtol(argv[1], NULL, 16);
+ }
+ if (argc == 3) {
+ load_baudrate = (int)dectoul(argv[2], NULL);
+
+ /* default to current baudrate */
+ if (load_baudrate == 0)
+ load_baudrate = current_baudrate;
+ }
+ if (load_baudrate != current_baudrate) {
+ printf("## Switch baudrate to %d bps and press ENTER ...\n",
+ load_baudrate);
+ udelay(50000);
+ flush();
+ gd->baudrate = load_baudrate;
+ serial_setbrg();
+ udelay(50000);
+ for (;;) {
+ if (getchar() == '\r')
+ break;
+ }
+ }
+#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
+ if (argc == 2) {
+ offset = simple_strtol(argv[1], NULL, 16);
+ }
+#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
+
+ printf("## Ready for S-Record download ...\n");
+
+ addr = load_serial(offset);
+
+ /*
+ * Gather any trailing characters (for instance, the ^D which
+ * is sent by 'cu' after sending a file), and give the
+ * box some time (100 * 1 ms)
+ */
+ for (i=0; i<100; ++i) {
+ if (tstc()) {
+ getchar();
+ }
+ udelay(1000);
+ }
+
+ if (addr == ~0) {
+ printf("## S-Record download aborted\n");
+ rcode = 1;
+ } else {
+ printf("## Start Addr = 0x%08lX\n", addr);
+ image_load_addr = addr;
+ }
+
+#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
+ if (load_baudrate != current_baudrate) {
+ printf("## Switch baudrate to %d bps and press ESC ...\n",
+ current_baudrate);
+ udelay(50000);
+ flush();
+ gd->baudrate = current_baudrate;
+ serial_setbrg();
+ udelay(50000);
+ for (;;) {
+ if (getchar() == 0x1B) /* ESC */
+ break;
+ }
+ }
+#endif
+ return rcode;
+}
+
+static ulong load_serial(long offset)
+{
+ char record[SREC_MAXRECLEN + 1]; /* buffer for one S-Record */
+ char binbuf[SREC_MAXBINLEN]; /* buffer for binary data */
+ int binlen; /* no. of data bytes in S-Rec. */
+ int type; /* return code for record type */
+ ulong addr; /* load address from S-Record */
+ ulong size; /* number of bytes transferred */
+ ulong store_addr;
+ ulong start_addr = ~0;
+ ulong end_addr = 0;
+ int line_count = 0;
+ long ret;
+
+ while (read_record(record, SREC_MAXRECLEN + 1) >= 0) {
+ type = srec_decode(record, &binlen, &addr, binbuf);
+
+ if (type < 0) {
+ return (~0); /* Invalid S-Record */
+ }
+
+ switch (type) {
+ case SREC_DATA2:
+ case SREC_DATA3:
+ case SREC_DATA4:
+ store_addr = addr + offset;
+#ifdef CONFIG_MTD_NOR_FLASH
+ if (addr2info(store_addr)) {
+ int rc;
+
+ rc = flash_write((char *)binbuf,store_addr,binlen);
+ if (rc != 0) {
+ flash_perror(rc);
+ return (~0);
+ }
+ } else
+#endif
+ {
+ void *dst;
+ phys_addr_t dst_addr;
+
+ dst_addr = (phys_addr_t)store_addr;
+ ret = lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &dst_addr,
+ binlen, LMB_NONE);
+ if (ret) {
+ printf("\nCannot overwrite reserved area (%08lx..%08lx)\n",
+ store_addr, store_addr + binlen);
+ return ret;
+ }
+ dst = map_sysmem(dst_addr, binlen);
+ memcpy(dst, binbuf, binlen);
+ unmap_sysmem(dst);
+ lmb_free(dst_addr, binlen, LMB_NONE);
+ }
+ if ((store_addr) < start_addr)
+ start_addr = store_addr;
+ if ((store_addr + binlen - 1) > end_addr)
+ end_addr = store_addr + binlen - 1;
+ break;
+ case SREC_END2:
+ case SREC_END3:
+ case SREC_END4:
+ udelay(10000);
+ size = end_addr - start_addr + 1;
+ printf("\n"
+ "## First Load Addr = 0x%08lX\n"
+ "## Last Load Addr = 0x%08lX\n"
+ "## Total Size = 0x%08lX = %ld Bytes\n",
+ start_addr, end_addr, size, size
+ );
+ flush_cache(start_addr, size);
+ env_set_hex("filesize", size);
+ return (addr);
+ case SREC_START:
+ break;
+ default:
+ break;
+ }
+ if (!do_echo) { /* print a '.' every 100 lines */
+ if ((++line_count % 100) == 0)
+ putc('.');
+ }
+ }
+
+ return (~0); /* Download aborted */
+}
+
+static int read_record(char *buf, ulong len)
+{
+ char *p;
+ int c;
+
+ --len; /* always leave room for terminating '\0' byte */
+
+ for (p=buf; p < buf+len; ++p) {
+ c = getchar(); /* read character */
+ if (do_echo)
+ putc(c); /* ... and echo it */
+
+ switch (c) {
+ case '\r':
+ case '\n':
+ *p = '\0';
+ return (p - buf);
+ case '\0':
+ case 0x03: /* ^C - Control C */
+ return (-1);
+ default:
+ *p = c;
+ }
+
+ /* Check for the console hangup (if any different from serial) */
+ if (gd->jt->getc != getchar) {
+ if (ctrlc())
+ return (-1);
+ }
+ }
+
+ /* line too long - truncate */
+ *p = '\0';
+ return (p - buf);
+}
+
+#if defined(CONFIG_CMD_SAVES)
+
+int do_save_serial(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong offset = 0;
+ ulong size = 0;
+#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
+ int save_baudrate, current_baudrate;
+
+ save_baudrate = current_baudrate = gd->baudrate;
+#endif
+
+ if (argc >= 2) {
+ offset = hextoul(argv[1], NULL);
+ }
+#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
+ if (argc >= 3) {
+ size = hextoul(argv[2], NULL);
+ }
+ if (argc == 4) {
+ save_baudrate = (int)dectoul(argv[3], NULL);
+
+ /* default to current baudrate */
+ if (save_baudrate == 0)
+ save_baudrate = current_baudrate;
+ }
+ if (save_baudrate != current_baudrate) {
+ printf("## Switch baudrate to %d bps and press ENTER ...\n",
+ save_baudrate);
+ udelay(50000);
+ gd->baudrate = save_baudrate;
+ serial_setbrg();
+ udelay(50000);
+ for (;;) {
+ if (getchar() == '\r')
+ break;
+ }
+ }
+#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
+ if (argc == 3) {
+ size = hextoul(argv[2], NULL);
+ }
+#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
+
+ printf("## Ready for S-Record upload, press ENTER to proceed ...\n");
+ for (;;) {
+ if (getchar() == '\r')
+ break;
+ }
+ if (save_serial(offset, size)) {
+ printf("## S-Record upload aborted\n");
+ } else {
+ printf("## S-Record upload complete\n");
+ }
+#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
+ if (save_baudrate != current_baudrate) {
+ printf("## Switch baudrate to %d bps and press ESC ...\n",
+ (int)current_baudrate);
+ udelay(50000);
+ flush();
+ gd->baudrate = current_baudrate;
+ serial_setbrg();
+ udelay(50000);
+ for (;;) {
+ if (getchar() == 0x1B) /* ESC */
+ break;
+ }
+ }
+#endif
+ return 0;
+}
+
+#define SREC3_START "S0030000FC\n"
+#define SREC3_FORMAT "S3%02X%08lX%s%02X\n"
+#define SREC3_END "S70500000000FA\n"
+#define SREC_BYTES_PER_RECORD 16
+
+static int save_serial(ulong address, ulong count)
+{
+ int i, c, reclen, checksum, length;
+ char *hex = "0123456789ABCDEF";
+ char record[2*SREC_BYTES_PER_RECORD+16]; /* buffer for one S-Record */
+ char data[2*SREC_BYTES_PER_RECORD+1]; /* buffer for hex data */
+
+ reclen = 0;
+ checksum = 0;
+
+ if(write_record(SREC3_START)) /* write the header */
+ return (-1);
+ do {
+ volatile uchar *src;
+
+ src = map_sysmem(address, count);
+ if (count) { /* collect hex data in the buffer */
+ c = src[reclen]; /* get one byte */
+ checksum += c; /* accumulate checksum */
+ data[2*reclen] = hex[(c>>4)&0x0f];
+ data[2*reclen+1] = hex[c & 0x0f];
+ data[2*reclen+2] = '\0';
+ ++reclen;
+ --count;
+ }
+ unmap_sysmem((void *)src);
+ if(reclen == SREC_BYTES_PER_RECORD || count == 0) {
+ /* enough data collected for one record: dump it */
+ if(reclen) { /* build & write a data record: */
+ /* address + data + checksum */
+ length = 4 + reclen + 1;
+
+ /* accumulate length bytes into checksum */
+ for(i = 0; i < 2; i++)
+ checksum += (length >> (8*i)) & 0xff;
+
+ /* accumulate address bytes into checksum: */
+ for(i = 0; i < 4; i++)
+ checksum += (address >> (8*i)) & 0xff;
+
+ /* make proper checksum byte: */
+ checksum = ~checksum & 0xff;
+
+ /* output one record: */
+ sprintf(record, SREC3_FORMAT, length, address, data, checksum);
+ if(write_record(record))
+ return (-1);
+ }
+ address += reclen; /* increment address */
+ checksum = 0;
+ reclen = 0;
+ }
+ }
+ while(count);
+ if(write_record(SREC3_END)) /* write the final record */
+ return (-1);
+ return(0);
+}
+
+static int write_record(char *buf)
+{
+ char c;
+
+ while((c = *buf++))
+ putc(c);
+
+ /* Check for the console hangup (if any different from serial) */
+
+ if (ctrlc()) {
+ return (-1);
+ }
+ return (0);
+}
+# endif
+
+#endif
+
+#if defined(CONFIG_CMD_LOADB)
+/*
+ * loadb command (load binary) included
+ */
+#define XON_CHAR 17
+#define XOFF_CHAR 19
+#define START_CHAR 0x01
+#define ETX_CHAR 0x03
+#define END_CHAR 0x0D
+#define SPACE 0x20
+#define K_ESCAPE 0x23
+#define SEND_TYPE 'S'
+#define DATA_TYPE 'D'
+#define ACK_TYPE 'Y'
+#define NACK_TYPE 'N'
+#define BREAK_TYPE 'B'
+#define tochar(x) ((char) (((x) + SPACE) & 0xff))
+#define untochar(x) ((int) (((x) - SPACE) & 0xff))
+
+static void set_kerm_bin_mode(unsigned long *);
+static int k_recv(void);
+static ulong load_serial_bin(ulong offset);
+
+static char his_eol; /* character he needs at end of packet */
+static int his_pad_count; /* number of pad chars he needs */
+static char his_pad_char; /* pad chars he needs */
+static char his_quote; /* quote chars he'll use */
+
+static int do_load_serial_bin(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong offset = 0;
+ ulong addr;
+ int load_baudrate, current_baudrate;
+ int rcode = 0;
+ char *s;
+
+ /* pre-set offset from CONFIG_SYS_LOAD_ADDR */
+ offset = CONFIG_SYS_LOAD_ADDR;
+
+ /* pre-set offset from $loadaddr */
+ s = env_get("loadaddr");
+ if (s)
+ offset = hextoul(s, NULL);
+
+ load_baudrate = current_baudrate = gd->baudrate;
+
+ if (argc >= 2) {
+ offset = hextoul(argv[1], NULL);
+ }
+ if (argc == 3) {
+ load_baudrate = (int)dectoul(argv[2], NULL);
+
+ /* default to current baudrate */
+ if (load_baudrate == 0)
+ load_baudrate = current_baudrate;
+ }
+
+ if (load_baudrate != current_baudrate) {
+ printf("## Switch baudrate to %d bps and press ENTER ...\n",
+ load_baudrate);
+ udelay(50000);
+ flush();
+ gd->baudrate = load_baudrate;
+ serial_setbrg();
+ udelay(50000);
+ for (;;) {
+ if (getchar() == '\r')
+ break;
+ }
+ }
+
+ if (strcmp(argv[0],"loady")==0) {
+ printf("## Ready for binary (ymodem) download "
+ "to 0x%08lX at %d bps...\n",
+ offset,
+ load_baudrate);
+
+ addr = load_serial_ymodem(offset, xyzModem_ymodem);
+
+ if (addr == ~0) {
+ image_load_addr = 0;
+ printf("## Binary (ymodem) download aborted\n");
+ rcode = 1;
+ } else {
+ printf("## Start Addr = 0x%08lX\n", addr);
+ image_load_addr = addr;
+ }
+ } else if (strcmp(argv[0],"loadx")==0) {
+ printf("## Ready for binary (xmodem) download "
+ "to 0x%08lX at %d bps...\n",
+ offset,
+ load_baudrate);
+
+ addr = load_serial_ymodem(offset, xyzModem_xmodem);
+
+ if (addr == ~0) {
+ image_load_addr = 0;
+ printf("## Binary (xmodem) download aborted\n");
+ rcode = 1;
+ } else {
+ printf("## Start Addr = 0x%08lX\n", addr);
+ image_load_addr = addr;
+ }
+ } else {
+
+ printf("## Ready for binary (kermit) download "
+ "to 0x%08lX at %d bps...\n",
+ offset,
+ load_baudrate);
+ addr = load_serial_bin(offset);
+
+ if (addr == ~0) {
+ image_load_addr = 0;
+ printf("## Binary (kermit) download aborted\n");
+ rcode = 1;
+ } else {
+ printf("## Start Addr = 0x%08lX\n", addr);
+ image_load_addr = addr;
+ }
+ }
+ if (load_baudrate != current_baudrate) {
+ printf("## Switch baudrate to %d bps and press ESC ...\n",
+ current_baudrate);
+ udelay(50000);
+ flush();
+ gd->baudrate = current_baudrate;
+ serial_setbrg();
+ udelay(50000);
+ for (;;) {
+ if (getchar() == 0x1B) /* ESC */
+ break;
+ }
+ }
+
+ return rcode;
+}
+
+static ulong load_serial_bin(ulong offset)
+{
+ int size, i;
+
+ set_kerm_bin_mode((ulong *) offset);
+ size = k_recv();
+
+ /*
+ * Gather any trailing characters (for instance, the ^D which
+ * is sent by 'cu' after sending a file), and give the
+ * box some time (100 * 1 ms)
+ */
+ for (i=0; i<100; ++i) {
+ if (tstc()) {
+ getchar();
+ }
+ udelay(1000);
+ }
+
+ if (size == 0)
+ return ~0; /* Download aborted */
+
+ flush_cache(offset, size);
+
+ printf("## Total Size = 0x%08x = %d Bytes\n", size, size);
+ env_set_hex("filesize", size);
+
+ return offset;
+}
+
+static void send_pad(void)
+{
+ int count = his_pad_count;
+
+ while (count-- > 0)
+ putc(his_pad_char);
+}
+
+/* converts escaped kermit char to binary char */
+static char ktrans(char in)
+{
+ if ((in & 0x60) == 0x40) {
+ return (char) (in & ~0x40);
+ } else if ((in & 0x7f) == 0x3f) {
+ return (char) (in | 0x40);
+ } else
+ return in;
+}
+
+static int chk1(char *buffer)
+{
+ int total = 0;
+
+ while (*buffer) {
+ total += *buffer++;
+ }
+ return (int) ((total + ((total >> 6) & 0x03)) & 0x3f);
+}
+
+static void s1_sendpacket(char *packet)
+{
+ send_pad();
+ while (*packet) {
+ putc(*packet++);
+ }
+}
+
+static char a_b[24];
+static void send_ack(int n)
+{
+ a_b[0] = START_CHAR;
+ a_b[1] = tochar(3);
+ a_b[2] = tochar(n);
+ a_b[3] = ACK_TYPE;
+ a_b[4] = '\0';
+ a_b[4] = tochar(chk1(&a_b[1]));
+ a_b[5] = his_eol;
+ a_b[6] = '\0';
+ s1_sendpacket(a_b);
+}
+
+static void send_nack(int n)
+{
+ a_b[0] = START_CHAR;
+ a_b[1] = tochar(3);
+ a_b[2] = tochar(n);
+ a_b[3] = NACK_TYPE;
+ a_b[4] = '\0';
+ a_b[4] = tochar(chk1(&a_b[1]));
+ a_b[5] = his_eol;
+ a_b[6] = '\0';
+ s1_sendpacket(a_b);
+}
+
+static void (*os_data_init)(void);
+static void (*os_data_char)(char new_char);
+static int os_data_state, os_data_state_saved;
+static char *os_data_addr, *os_data_addr_saved;
+static char *bin_start_address;
+
+static void bin_data_init(void)
+{
+ os_data_state = 0;
+ os_data_addr = bin_start_address;
+}
+
+static void os_data_save(void)
+{
+ os_data_state_saved = os_data_state;
+ os_data_addr_saved = os_data_addr;
+}
+
+static void os_data_restore(void)
+{
+ os_data_state = os_data_state_saved;
+ os_data_addr = os_data_addr_saved;
+}
+
+static void bin_data_char(char new_char)
+{
+ switch (os_data_state) {
+ case 0: /* data */
+ *os_data_addr++ = new_char;
+ break;
+ }
+}
+
+static void set_kerm_bin_mode(unsigned long *addr)
+{
+ bin_start_address = (char *) addr;
+ os_data_init = bin_data_init;
+ os_data_char = bin_data_char;
+}
+
+/* k_data_* simply handles the kermit escape translations */
+static int k_data_escape, k_data_escape_saved;
+static void k_data_init(void)
+{
+ k_data_escape = 0;
+ os_data_init();
+}
+
+static void k_data_save(void)
+{
+ k_data_escape_saved = k_data_escape;
+ os_data_save();
+}
+
+static void k_data_restore(void)
+{
+ k_data_escape = k_data_escape_saved;
+ os_data_restore();
+}
+
+static void k_data_char(char new_char)
+{
+ if (k_data_escape) {
+ /* last char was escape - translate this character */
+ os_data_char(ktrans(new_char));
+ k_data_escape = 0;
+ } else {
+ if (new_char == his_quote) {
+ /* this char is escape - remember */
+ k_data_escape = 1;
+ } else {
+ /* otherwise send this char as-is */
+ os_data_char(new_char);
+ }
+ }
+}
+
+#define SEND_DATA_SIZE 20
+static char send_parms[SEND_DATA_SIZE];
+static char *send_ptr;
+
+/* handle_send_packet interprits the protocol info and builds and
+ sends an appropriate ack for what we can do */
+static void handle_send_packet(int n)
+{
+ int length = 3;
+ int bytes;
+
+ /* initialize some protocol parameters */
+ his_eol = END_CHAR; /* default end of line character */
+ his_pad_count = 0;
+ his_pad_char = '\0';
+ his_quote = K_ESCAPE;
+
+ /* ignore last character if it filled the buffer */
+ if (send_ptr == &send_parms[SEND_DATA_SIZE - 1])
+ --send_ptr;
+ bytes = send_ptr - send_parms; /* how many bytes we'll process */
+ do {
+ if (bytes-- <= 0)
+ break;
+ /* handle MAXL - max length */
+ /* ignore what he says - most I'll take (here) is 94 */
+ a_b[++length] = tochar(94);
+ if (bytes-- <= 0)
+ break;
+ /* handle TIME - time you should wait for my packets */
+ /* ignore what he says - don't wait for my ack longer than 1 second */
+ a_b[++length] = tochar(1);
+ if (bytes-- <= 0)
+ break;
+ /* handle NPAD - number of pad chars I need */
+ /* remember what he says - I need none */
+ his_pad_count = untochar(send_parms[2]);
+ a_b[++length] = tochar(0);
+ if (bytes-- <= 0)
+ break;
+ /* handle PADC - pad chars I need */
+ /* remember what he says - I need none */
+ his_pad_char = ktrans(send_parms[3]);
+ a_b[++length] = 0x40; /* He should ignore this */
+ if (bytes-- <= 0)
+ break;
+ /* handle EOL - end of line he needs */
+ /* remember what he says - I need CR */
+ his_eol = untochar(send_parms[4]);
+ a_b[++length] = tochar(END_CHAR);
+ if (bytes-- <= 0)
+ break;
+ /* handle QCTL - quote control char he'll use */
+ /* remember what he says - I'll use '#' */
+ his_quote = send_parms[5];
+ a_b[++length] = '#';
+ if (bytes-- <= 0)
+ break;
+ /* handle QBIN - 8-th bit prefixing */
+ /* ignore what he says - I refuse */
+ a_b[++length] = 'N';
+ if (bytes-- <= 0)
+ break;
+ /* handle CHKT - the clock check type */
+ /* ignore what he says - I do type 1 (for now) */
+ a_b[++length] = '1';
+ if (bytes-- <= 0)
+ break;
+ /* handle REPT - the repeat prefix */
+ /* ignore what he says - I refuse (for now) */
+ a_b[++length] = 'N';
+ if (bytes-- <= 0)
+ break;
+ /* handle CAPAS - the capabilities mask */
+ /* ignore what he says - I only do long packets - I don't do windows */
+ a_b[++length] = tochar(2); /* only long packets */
+ a_b[++length] = tochar(0); /* no windows */
+ a_b[++length] = tochar(94); /* large packet msb */
+ a_b[++length] = tochar(94); /* large packet lsb */
+ } while (0);
+
+ a_b[0] = START_CHAR;
+ a_b[1] = tochar(length);
+ a_b[2] = tochar(n);
+ a_b[3] = ACK_TYPE;
+ a_b[++length] = '\0';
+ a_b[length] = tochar(chk1(&a_b[1]));
+ a_b[++length] = his_eol;
+ a_b[++length] = '\0';
+ s1_sendpacket(a_b);
+}
+
+/* k_recv receives a OS Open image file over kermit line */
+static int k_recv(void)
+{
+ int new_char;
+ char k_state, k_state_saved;
+ int sum;
+ int done;
+ int length;
+ int n, last_n;
+ int len_lo, len_hi;
+
+ /* initialize some protocol parameters */
+ his_eol = END_CHAR; /* default end of line character */
+ his_pad_count = 0;
+ his_pad_char = '\0';
+ his_quote = K_ESCAPE;
+
+ /* initialize the k_recv and k_data state machine */
+ done = 0;
+ k_state = 0;
+ k_data_init();
+ k_state_saved = k_state;
+ k_data_save();
+ n = 0; /* just to get rid of a warning */
+ last_n = -1;
+
+ /* expect this "type" sequence (but don't check):
+ S: send initiate
+ F: file header
+ D: data (multiple)
+ Z: end of file
+ B: break transmission
+ */
+
+ /* enter main loop */
+ while (!done) {
+ /* set the send packet pointer to begining of send packet parms */
+ send_ptr = send_parms;
+
+ /* With each packet, start summing the bytes starting with the length.
+ Save the current sequence number.
+ Note the type of the packet.
+ If a character less than SPACE (0x20) is received - error.
+ */
+
+#if 0
+ /* OLD CODE, Prior to checking sequence numbers */
+ /* first have all state machines save current states */
+ k_state_saved = k_state;
+ k_data_save ();
+#endif
+
+ /* get a packet */
+ /* wait for the starting character or ^C */
+ for (;;) {
+ switch (getchar()) {
+ case START_CHAR: /* start packet */
+ goto START;
+ case ETX_CHAR: /* ^C waiting for packet */
+ return (0);
+ default:
+ ;
+ }
+ }
+START:
+ /* get length of packet */
+ sum = 0;
+ new_char = getchar();
+ if ((new_char & 0xE0) == 0)
+ goto packet_error;
+ sum += new_char & 0xff;
+ length = untochar(new_char);
+ /* get sequence number */
+ new_char = getchar();
+ if ((new_char & 0xE0) == 0)
+ goto packet_error;
+ sum += new_char & 0xff;
+ n = untochar(new_char);
+ --length;
+
+ /* NEW CODE - check sequence numbers for retried packets */
+ /* Note - this new code assumes that the sequence number is correctly
+ * received. Handling an invalid sequence number adds another layer
+ * of complexity that may not be needed - yet! At this time, I'm hoping
+ * that I don't need to buffer the incoming data packets and can write
+ * the data into memory in real time.
+ */
+ if (n == last_n) {
+ /* same sequence number, restore the previous state */
+ k_state = k_state_saved;
+ k_data_restore();
+ } else {
+ /* new sequence number, checkpoint the download */
+ last_n = n;
+ k_state_saved = k_state;
+ k_data_save();
+ }
+ /* END NEW CODE */
+
+ /* get packet type */
+ new_char = getchar();
+ if ((new_char & 0xE0) == 0)
+ goto packet_error;
+ sum += new_char & 0xff;
+ k_state = new_char;
+ --length;
+ /* check for extended length */
+ if (length == -2) {
+ /* (length byte was 0, decremented twice) */
+ /* get the two length bytes */
+ new_char = getchar();
+ if ((new_char & 0xE0) == 0)
+ goto packet_error;
+ sum += new_char & 0xff;
+ len_hi = untochar(new_char);
+ new_char = getchar();
+ if ((new_char & 0xE0) == 0)
+ goto packet_error;
+ sum += new_char & 0xff;
+ len_lo = untochar(new_char);
+ length = len_hi * 95 + len_lo;
+ /* check header checksum */
+ new_char = getchar();
+ if ((new_char & 0xE0) == 0)
+ goto packet_error;
+ if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
+ goto packet_error;
+ sum += new_char & 0xff;
+/* --length; */ /* new length includes only data and block check to come */
+ }
+ /* bring in rest of packet */
+ while (length > 1) {
+ new_char = getchar();
+ if ((new_char & 0xE0) == 0)
+ goto packet_error;
+ sum += new_char & 0xff;
+ --length;
+ if (k_state == DATA_TYPE) {
+ /* pass on the data if this is a data packet */
+ k_data_char (new_char);
+ } else if (k_state == SEND_TYPE) {
+ /* save send pack in buffer as is */
+ *send_ptr++ = new_char;
+ /* if too much data, back off the pointer */
+ if (send_ptr >= &send_parms[SEND_DATA_SIZE])
+ --send_ptr;
+ }
+ }
+ /* get and validate checksum character */
+ new_char = getchar();
+ if ((new_char & 0xE0) == 0)
+ goto packet_error;
+ if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
+ goto packet_error;
+ /* get END_CHAR */
+ new_char = getchar();
+ if (new_char != END_CHAR) {
+ packet_error:
+ /* restore state machines */
+ k_state = k_state_saved;
+ k_data_restore();
+ /* send a negative acknowledge packet in */
+ send_nack(n);
+ } else if (k_state == SEND_TYPE) {
+ /* crack the protocol parms, build an appropriate ack packet */
+ handle_send_packet(n);
+ } else {
+ /* send simple acknowledge packet in */
+ send_ack(n);
+ /* quit if end of transmission */
+ if (k_state == BREAK_TYPE)
+ done = 1;
+ }
+ }
+ return ((ulong) os_data_addr - (ulong) bin_start_address);
+}
+
+static int getcxmodem(void) {
+ if (tstc())
+ return (getchar());
+ return -1;
+}
+static ulong load_serial_ymodem(ulong offset, int mode)
+{
+ int size;
+ int err;
+ int res;
+ connection_info_t info;
+ char ymodemBuf[1024];
+ ulong store_addr = ~0;
+ ulong addr = 0;
+
+ size = 0;
+ info.mode = mode;
+ res = xyzModem_stream_open(&info, &err);
+ if (!res) {
+
+ err = 0;
+ while ((res =
+ xyzModem_stream_read(ymodemBuf, 1024, &err)) > 0) {
+ store_addr = addr + offset;
+ size += res;
+ addr += res;
+#ifdef CONFIG_MTD_NOR_FLASH
+ if (addr2info(store_addr)) {
+ int rc;
+
+ rc = flash_write((char *) ymodemBuf,
+ store_addr, res);
+ if (rc != 0) {
+ xyzModem_stream_terminate(true, &getcxmodem);
+ xyzModem_stream_close(&err);
+ printf("\n");
+ flash_perror(rc);
+ return (~0);
+ }
+ } else
+#endif
+ {
+ memcpy((char *)(store_addr), ymodemBuf,
+ res);
+ }
+
+ }
+ if (err) {
+ xyzModem_stream_terminate((err == xyzModem_cancel) ? false : true, &getcxmodem);
+ xyzModem_stream_close(&err);
+ printf("\n%s\n", xyzModem_error(err));
+ return (~0); /* Download aborted */
+ }
+
+ if (IS_ENABLED(CONFIG_CMD_BOOTEFI))
+ efi_set_bootdev("Uart", "", "",
+ map_sysmem(offset, 0), size);
+
+ } else {
+ printf("\n%s\n", xyzModem_error(err));
+ return (~0); /* Download aborted */
+ }
+
+ xyzModem_stream_terminate(false, &getcxmodem);
+ xyzModem_stream_close(&err);
+
+ flush_cache(offset, ALIGN(size, ARCH_DMA_MINALIGN));
+
+ printf("## Total Size = 0x%08x = %d Bytes\n", size, size);
+ env_set_hex("filesize", size);
+
+ return offset;
+}
+
+#endif
+
+#if defined(CONFIG_CMD_LOADM)
+static int do_load_memory_bin(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr, dest, size;
+ void *src, *dst;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ addr = simple_strtoul(argv[1], NULL, 16);
+
+ dest = simple_strtoul(argv[2], NULL, 16);
+
+ size = simple_strtoul(argv[3], NULL, 16);
+
+ if (!size) {
+ printf("loadm: can not load zero bytes\n");
+ return 1;
+ }
+
+ src = map_sysmem(addr, size);
+ dst = map_sysmem(dest, size);
+
+ memcpy(dst, src, size);
+
+ unmap_sysmem(src);
+ unmap_sysmem(dst);
+
+ if (IS_ENABLED(CONFIG_CMD_BOOTEFI))
+ efi_set_bootdev("Mem", "", "", map_sysmem(dest, 0), size);
+
+ printf("loaded bin to memory: size: %lu\n", size);
+
+ return 0;
+}
+#endif
+
+/* -------------------------------------------------------------------- */
+
+#if defined(CONFIG_CMD_LOADS)
+
+#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
+U_BOOT_CMD(
+ loads, 3, 0, do_load_serial,
+ "load S-Record file over serial line",
+ "[ off ] [ baud ]\n"
+ " - load S-Record file over serial line"
+ " with offset 'off' and baudrate 'baud'"
+);
+
+#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
+U_BOOT_CMD(
+ loads, 2, 0, do_load_serial,
+ "load S-Record file over serial line",
+ "[ off ]\n"
+ " - load S-Record file over serial line with offset 'off'"
+);
+#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
+
+/*
+ * SAVES always requires LOADS support, but not vice versa
+ */
+
+#if defined(CONFIG_CMD_SAVES)
+#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
+U_BOOT_CMD(
+ saves, 4, 0, do_save_serial,
+ "save S-Record file over serial line",
+ "[ off ] [size] [ baud ]\n"
+ " - save S-Record file over serial line"
+ " with offset 'off', size 'size' and baudrate 'baud'"
+);
+#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
+U_BOOT_CMD(
+ saves, 3, 0, do_save_serial,
+ "save S-Record file over serial line",
+ "[ off ] [size]\n"
+ " - save S-Record file over serial line with offset 'off' and size 'size'"
+);
+#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
+#endif /* CONFIG_CMD_SAVES */
+#endif /* CONFIG_CMD_LOADS */
+
+#if defined(CONFIG_CMD_LOADB)
+U_BOOT_CMD(
+ loadb, 3, 0, do_load_serial_bin,
+ "load binary file over serial line (kermit mode)",
+ "[ addr [ baud ] ]\n"
+ " - load binary file over serial line"
+ " at address 'addr' with baudrate 'baud'"
+);
+
+U_BOOT_CMD(
+ loadx, 3, 0, do_load_serial_bin,
+ "load binary file over serial line (xmodem mode)",
+ "[ addr [ baud ] ]\n"
+ " - load binary file over serial line"
+ " at address 'addr' with baudrate 'baud'"
+);
+
+U_BOOT_CMD(
+ loady, 3, 0, do_load_serial_bin,
+ "load binary file over serial line (ymodem mode)",
+ "[ addr [ baud ] ]\n"
+ " - load binary file over serial line"
+ " at address 'addr' with baudrate 'baud'"
+);
+
+#endif /* CONFIG_CMD_LOADB */
+
+#if defined(CONFIG_CMD_LOADM)
+U_BOOT_CMD(
+ loadm, 4, 0, do_load_memory_bin,
+ "load binary blob from source address to destination address",
+ "[src_addr] [dst_addr] [size]\n"
+ " - load a binary blob from one memory location to other"
+ " from src_addr to dst_addr by size bytes"
+);
+#endif /* CONFIG_CMD_LOADM */
diff --git a/cmd/log.c b/cmd/log.c
new file mode 100644
index 00000000000..64add6d8b5a
--- /dev/null
+++ b/cmd/log.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2017 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <getopt.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/global_data.h>
+
+static char log_fmt_chars[LOGF_COUNT] = "clFLfm";
+
+static enum log_level_t parse_log_level(char *const arg)
+{
+ enum log_level_t ret;
+ ulong level;
+
+ if (!strict_strtoul(arg, 10, &level)) {
+ if (level > _LOG_MAX_LEVEL) {
+ printf("Only log levels <= %d are supported\n",
+ _LOG_MAX_LEVEL);
+ return LOGL_NONE;
+ }
+ return level;
+ }
+
+ ret = log_get_level_by_name(arg);
+ if (ret == LOGL_NONE)
+ printf("Unknown log level \"%s\"\n", arg);
+ return ret;
+}
+
+static int do_log_level(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ enum log_level_t log_level;
+
+ if (argc > 1) {
+ log_level = parse_log_level(argv[1]);
+
+ if (log_level == LOGL_NONE)
+ return CMD_RET_FAILURE;
+ gd->default_log_level = log_level;
+ } else {
+ for (log_level = LOGL_FIRST; log_level <= _LOG_MAX_LEVEL;
+ log_level++)
+ printf("%s%s\n", log_get_level_name(log_level),
+ log_level == gd->default_log_level ?
+ " (default)" : "");
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_log_categories(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ enum log_category_t cat;
+ const char *name;
+
+ for (cat = LOGC_FIRST; cat < LOGC_COUNT; cat++) {
+ name = log_get_cat_name(cat);
+ /*
+ * Invalid category names (e.g. <invalid> or <missing>) begin
+ * with '<'.
+ */
+ if (name[0] == '<')
+ continue;
+ printf("%s\n", name);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_log_drivers(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct log_device *ldev;
+
+ list_for_each_entry(ldev, &gd->log_head, sibling_node)
+ printf("%s\n", ldev->drv->name);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_log_filter_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int opt;
+ const char *drv_name = "console";
+ struct getopt_state gs;
+ struct log_filter *filt;
+ struct log_device *ldev;
+
+ getopt_init_state(&gs);
+ while ((opt = getopt(&gs, argc, argv, "d:")) > 0) {
+ switch (opt) {
+ case 'd':
+ drv_name = gs.arg;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+
+ if (gs.index != argc)
+ return CMD_RET_USAGE;
+
+ ldev = log_device_find_by_name(drv_name);
+ if (!ldev) {
+ printf("Could not find log device for \"%s\"\n", drv_name);
+ return CMD_RET_FAILURE;
+ }
+
+ list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
+ printf("%-3d: %s %s %s\n", filt->filter_num,
+ filt->flags & LOGFF_DENY ? "DENY" : "ALLOW",
+ filt->flags & LOGFF_LEVEL_MIN ? ">=" : "<=",
+ log_get_level_name(filt->level));
+
+ if (filt->flags & LOGFF_HAS_CAT) {
+ printf(" Categories:");
+ for (int i = 0;
+ i < LOGF_MAX_CATEGORIES &&
+ filt->cat_list[i] != LOGC_END;
+ ++i) {
+ printf(" %s",
+ log_get_cat_name(filt->cat_list[i]));
+ }
+ printf("\n");
+ }
+ if (filt->file_list)
+ printf(" Files: %s\n", filt->file_list);
+ if (filt->func_list)
+ printf(" Functions: %s\n", filt->func_list);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_log_filter_add(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool level_set = false;
+ bool print_num = false;
+ bool type_set = false;
+ char *file_list = NULL;
+ char *func_list = NULL;
+ const char *drv_name = "console";
+ int opt, err;
+ int cat_count = 0;
+ int flags = 0;
+ enum log_category_t cat_list[LOGF_MAX_CATEGORIES + 1];
+ enum log_level_t level = LOGL_MAX;
+ struct getopt_state gs;
+
+ getopt_init_state(&gs);
+ while ((opt = getopt(&gs, argc, argv, "Ac:d:Df:F:l:L:p")) > 0) {
+ switch (opt) {
+ case 'A':
+#define do_type() do { \
+ if (type_set) { \
+ printf("Allow or deny set twice\n"); \
+ return CMD_RET_USAGE; \
+ } \
+ type_set = true; \
+} while (0)
+ do_type();
+ break;
+ case 'c': {
+ enum log_category_t cat;
+
+ if (cat_count >= LOGF_MAX_CATEGORIES) {
+ printf("Too many categories\n");
+ return CMD_RET_FAILURE;
+ }
+
+ cat = log_get_cat_by_name(gs.arg);
+ if (cat == LOGC_NONE) {
+ printf("Unknown category \"%s\"\n", gs.arg);
+ return CMD_RET_FAILURE;
+ }
+
+ cat_list[cat_count++] = cat;
+ break;
+ }
+ case 'd':
+ drv_name = gs.arg;
+ break;
+ case 'D':
+ do_type();
+ flags |= LOGFF_DENY;
+ break;
+ case 'f':
+ file_list = gs.arg;
+ break;
+ case 'F':
+ func_list = gs.arg;
+ break;
+ case 'l':
+#define do_level() do { \
+ if (level_set) { \
+ printf("Log level set twice\n"); \
+ return CMD_RET_USAGE; \
+ } \
+ level = parse_log_level(gs.arg); \
+ if (level == LOGL_NONE) \
+ return CMD_RET_FAILURE; \
+ level_set = true; \
+} while (0)
+ do_level();
+ break;
+ case 'L':
+ do_level();
+ flags |= LOGFF_LEVEL_MIN;
+ break;
+ case 'p':
+ print_num = true;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+
+ if (gs.index != argc)
+ return CMD_RET_USAGE;
+
+ cat_list[cat_count] = LOGC_END;
+ err = log_add_filter_flags(drv_name, cat_count ? cat_list : NULL, level,
+ file_list, func_list, flags);
+ if (err < 0) {
+ printf("Could not add filter (err = %d)\n", err);
+ return CMD_RET_FAILURE;
+ } else if (print_num) {
+ printf("%d\n", err);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_log_filter_remove(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool all = false;
+ int opt, err;
+ ulong filter_num;
+ const char *drv_name = "console";
+ struct getopt_state gs;
+
+ getopt_init_state(&gs);
+ while ((opt = getopt(&gs, argc, argv, "ad:")) > 0) {
+ switch (opt) {
+ case 'a':
+ all = true;
+ break;
+ case 'd':
+ drv_name = gs.arg;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+
+ if (all) {
+ struct log_filter *filt, *tmp_filt;
+ struct log_device *ldev;
+
+ if (gs.index != argc)
+ return CMD_RET_USAGE;
+
+ ldev = log_device_find_by_name(drv_name);
+ if (!ldev) {
+ printf("Could not find log device for \"%s\"\n",
+ drv_name);
+ return CMD_RET_FAILURE;
+ }
+
+ list_for_each_entry_safe(filt, tmp_filt, &ldev->filter_head,
+ sibling_node) {
+ list_del(&filt->sibling_node);
+ free(filt);
+ }
+ } else {
+ if (gs.index + 1 != argc)
+ return CMD_RET_USAGE;
+
+ if (strict_strtoul(argv[gs.index], 10, &filter_num)) {
+ printf("Invalid filter number \"%s\"\n", argv[gs.index]);
+ return CMD_RET_FAILURE;
+ }
+
+ err = log_remove_filter(drv_name, filter_num);
+ if (err) {
+ printf("Could not remove filter (err = %d)\n", err);
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_log_format(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int i;
+
+ if (argc > 1) {
+ const char *str = argv[1];
+
+ if (!strcmp(str, "default")) {
+ gd->log_fmt = log_get_default_format();
+ } else if (!strcmp(str, "all")) {
+ gd->log_fmt = LOGF_ALL;
+ } else {
+ gd->log_fmt = 0;
+ for (; *str; str++) {
+ char *ptr = strchr(log_fmt_chars, *str);
+
+ if (!ptr) {
+ printf("Invalid log char '%c'\n", *str);
+ return CMD_RET_FAILURE;
+ }
+ gd->log_fmt |= 1 << (ptr - log_fmt_chars);
+ }
+ }
+ } else {
+ printf("Log format: ");
+ for (i = 0; i < LOGF_COUNT; i++) {
+ if (gd->log_fmt & (1 << i))
+ printf("%c", log_fmt_chars[i]);
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static int do_log_rec(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ enum log_category_t cat;
+ enum log_level_t level;
+ const char *file;
+ uint line;
+ const char *func;
+ const char *msg;
+ char *end;
+
+ if (argc < 7)
+ return CMD_RET_USAGE;
+ cat = log_get_cat_by_name(argv[1]);
+ level = dectoul(argv[2], &end);
+ if (end == argv[2]) {
+ level = log_get_level_by_name(argv[2]);
+
+ if (level == LOGL_NONE) {
+ printf("Invalid log level '%s'\n", argv[2]);
+ return CMD_RET_USAGE;
+ }
+ }
+ if (level >= LOGL_MAX) {
+ printf("Invalid log level %u\n", level);
+ return CMD_RET_USAGE;
+ }
+ file = argv[3];
+ line = dectoul(argv[4], NULL);
+ func = argv[5];
+ msg = argv[6];
+ if (_log(cat, level, file, line, func, "%s\n", msg))
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(log,
+ "level [<level>] - get/set log level\n"
+ "categories - list log categories\n"
+ "drivers - list log drivers\n"
+ "log filter-list [OPTIONS] - list all filters for a log driver\n"
+ "\t-d <driver> - Specify the log driver to list filters from; defaults\n"
+ "\t to console\n"
+ "log filter-add [OPTIONS] - add a new filter to a driver\n"
+ "\t-A - Allow messages matching this filter; mutually exclusive with -D\n"
+ "\t This is the default.\n"
+ "\t-c <category> - Category to match; may be specified multiple times\n"
+ "\t-d <driver> - Specify the log driver to add the filter to; defaults\n"
+ "\t to console\n"
+ "\t-D - Deny messages matching this filter; mutually exclusive with -A\n"
+ "\t-f <file_list> - A comma-separated list of files to match\n"
+ "\t-F <func_list> - A comma-separated list of functions to match\n"
+ "\t-l <level> - Match log levels less than or equal to <level>;\n"
+ "\t mutually-exclusive with -L\n"
+ "\t-L <level> - Match log levels greather than or equal to <level>;\n"
+ "\t mutually-exclusive with -l\n"
+ "\t-p - Print the filter number on success\n"
+ "log filter-remove [OPTIONS] [<num>] - Remove filter number <num>\n"
+ "\t-a - Remove ALL filters\n"
+ "\t-d <driver> - Specify the log driver to remove the filter from;\n"
+ "\t defaults to console\n"
+ "log format <fmt> - set log output format. <fmt> is a string where\n"
+ "\teach letter indicates something that should be displayed:\n"
+ "\tc=category, l=level, F=file, L=line number, f=function, m=msg\n"
+ "\tor 'default', or 'all' for all\n"
+ "log rec <category> <level> <file> <line> <func> <message> - "
+ "output a log record");
+
+U_BOOT_CMD_WITH_SUBCMDS(log, "log system", log_help_text,
+ U_BOOT_SUBCMD_MKENT(level, 2, 1, do_log_level),
+ U_BOOT_SUBCMD_MKENT(categories, 1, 1, do_log_categories),
+ U_BOOT_SUBCMD_MKENT(drivers, 1, 1, do_log_drivers),
+ U_BOOT_SUBCMD_MKENT(filter-list, 3, 1, do_log_filter_list),
+ U_BOOT_SUBCMD_MKENT(filter-add, CONFIG_SYS_MAXARGS, 1,
+ do_log_filter_add),
+ U_BOOT_SUBCMD_MKENT(filter-remove, 4, 1, do_log_filter_remove),
+ U_BOOT_SUBCMD_MKENT(format, 2, 1, do_log_format),
+ U_BOOT_SUBCMD_MKENT(rec, 7, 1, do_log_rec),
+);
diff --git a/cmd/lsblk.c b/cmd/lsblk.c
new file mode 100644
index 00000000000..7c00bfdc7a0
--- /dev/null
+++ b/cmd/lsblk.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2020
+ * Niel Fourie, DENX Software Engineering, lusus@denx.de.
+ */
+
+#include <blk.h>
+#include <command.h>
+#include <dm.h>
+
+static int do_lsblk(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ struct driver *d = ll_entry_start(struct driver, driver);
+ const int n_ents = ll_entry_count(struct driver, driver);
+ struct driver *entry;
+ struct udevice *udev;
+ struct uclass *uc;
+ struct blk_desc *desc;
+ int ret, i;
+
+ ret = uclass_get(UCLASS_BLK, &uc);
+ if (ret) {
+ puts("Could not get BLK uclass.\n");
+ return CMD_RET_FAILURE;
+ }
+ puts("Block Driver Devices\n");
+ puts("-----------------------------\n");
+ for (entry = d; entry < d + n_ents; entry++) {
+ if (entry->id != UCLASS_BLK)
+ continue;
+ i = 0;
+ printf("%-20.20s", entry->name);
+ uclass_foreach_dev(udev, uc) {
+ if (udev->driver != entry)
+ continue;
+ desc = dev_get_uclass_plat(udev);
+ printf("%c %s %u", i ? ',' : ':',
+ blk_get_uclass_name(desc->uclass_id),
+ desc->devnum);
+ i++;
+ }
+ if (!i)
+ puts(": <none>");
+ puts("\n");
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(lsblk, 1, 0, do_lsblk, "list block drivers and devices",
+ "- display list of block device drivers and attached block devices"
+);
diff --git a/cmd/lwip/Makefile b/cmd/lwip/Makefile
new file mode 100644
index 00000000000..a7f8976af3f
--- /dev/null
+++ b/cmd/lwip/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_CMD_DHCP) += dhcp.o
+obj-$(CONFIG_CMD_DNS) += dns.o
+obj-$(CONFIG_CMD_PING) += ping.o
+obj-$(CONFIG_CMD_SNTP) += sntp.o
+obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
+obj-$(CONFIG_CMD_WGET) += wget.o
diff --git a/cmd/lwip/dhcp.c b/cmd/lwip/dhcp.c
new file mode 100644
index 00000000000..3894d71f654
--- /dev/null
+++ b/cmd/lwip/dhcp.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024-2025 Linaro Ltd. */
+
+#include <command.h>
+#include <net.h>
+
+U_BOOT_CMD(dhcp, 3, 1, do_dhcp,
+ "boot image via network using DHCP/TFTP protocol",
+ "[loadAddress] [[hostIPaddr:]bootfilename]");
diff --git a/cmd/lwip/dns.c b/cmd/lwip/dns.c
new file mode 100644
index 00000000000..3eb698b3f82
--- /dev/null
+++ b/cmd/lwip/dns.c
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024 Linaro Ltd. */
+
+#include <command.h>
+#include <net.h>
+
+U_BOOT_CMD(dns, 3, 1, do_dns, "lookup the IP of a hostname",
+ "hostname [envvar]");
diff --git a/cmd/lwip/ping.c b/cmd/lwip/ping.c
new file mode 100644
index 00000000000..6d090fc530d
--- /dev/null
+++ b/cmd/lwip/ping.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024 Linaro Ltd. */
+
+#include <command.h>
+#include <console.h>
+#include <dm/device.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <lwip/icmp.h>
+#include <lwip/inet_chksum.h>
+#include <lwip/raw.h>
+#include <lwip/timeouts.h>
+#include <net.h>
+#include <time.h>
+
+U_BOOT_CMD(ping, 2, 1, do_ping, "send ICMP ECHO_REQUEST to network host",
+ "pingAddressOrHostName");
+
+#define PING_DELAY_MS 1000
+#define PING_COUNT 5
+/* Ping identifier - must fit on a u16_t */
+#define PING_ID 0xAFAF
+
+struct ping_ctx {
+ ip_addr_t target;
+ struct raw_pcb *pcb;
+ struct icmp_echo_hdr *iecho;
+ uint16_t seq_num;
+ bool alive;
+};
+
+static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *addr)
+{
+ struct ping_ctx *ctx = arg;
+ struct icmp_echo_hdr *iecho = ctx->iecho;
+
+ if (addr->addr != ctx->target.addr)
+ return 0;
+
+ if ((p->tot_len >= (IP_HLEN + sizeof(struct icmp_echo_hdr))) &&
+ pbuf_remove_header(p, IP_HLEN) == 0) {
+ iecho = (struct icmp_echo_hdr *)p->payload;
+
+ if (iecho->id == PING_ID &&
+ iecho->seqno == lwip_htons(ctx->seq_num)) {
+ ctx->alive = true;
+ printf("host %s is alive\n", ipaddr_ntoa(addr));
+ pbuf_free(p);
+ return 1; /* eat the packet */
+ }
+ /* not eaten, restore original packet */
+ pbuf_add_header(p, IP_HLEN);
+ }
+
+ return 0; /* don't eat the packet */
+}
+
+static int ping_raw_init(struct ping_ctx *ctx)
+{
+ ctx->pcb = raw_new(IP_PROTO_ICMP);
+ if (!ctx->pcb)
+ return -ENOMEM;
+
+ raw_recv(ctx->pcb, ping_recv, ctx);
+ raw_bind(ctx->pcb, IP_ADDR_ANY);
+
+ return 0;
+}
+
+static void ping_raw_stop(struct ping_ctx *ctx)
+{
+ if (ctx->pcb)
+ raw_remove(ctx->pcb);
+}
+
+static void ping_prepare_echo(struct ping_ctx *ctx)
+{
+ struct icmp_echo_hdr *iecho = ctx->iecho;
+
+ ICMPH_TYPE_SET(iecho, ICMP_ECHO);
+ ICMPH_CODE_SET(iecho, 0);
+ iecho->chksum = 0;
+ iecho->id = PING_ID;
+ iecho->seqno = lwip_htons(ctx->seq_num);
+
+ iecho->chksum = inet_chksum(iecho, sizeof(*iecho));
+}
+
+static void ping_send_icmp(struct ping_ctx *ctx)
+{
+ struct pbuf *p;
+ size_t ping_size = sizeof(struct icmp_echo_hdr);
+
+ p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM);
+ if (!p)
+ return;
+
+ if (p->len == p->tot_len && !p->next) {
+ ctx->iecho = (struct icmp_echo_hdr *)p->payload;
+ ping_prepare_echo(ctx);
+ raw_sendto(ctx->pcb, p, &ctx->target);
+ }
+
+ pbuf_free(p);
+}
+
+static void ping_send(void *arg)
+{
+ struct ping_ctx *ctx = arg;
+
+ ctx->seq_num++;
+ if (ctx->seq_num <= PING_COUNT) {
+ ping_send_icmp(ctx);
+ sys_timeout(PING_DELAY_MS, ping_send, ctx);
+ }
+}
+
+static int ping_loop(struct udevice *udev, const ip_addr_t *addr)
+{
+ struct ping_ctx ctx = {};
+ struct netif *netif;
+ int ret;
+
+ netif = net_lwip_new_netif(udev);
+ if (!netif)
+ return -ENODEV;
+
+ printf("Using %s device\n", udev->name);
+
+ ret = ping_raw_init(&ctx);
+ if (ret < 0) {
+ net_lwip_remove_netif(netif);
+ return ret;
+ }
+
+ ctx.target = *addr;
+
+ ping_send(&ctx);
+
+ do {
+ net_lwip_rx(udev, netif);
+ if (ctx.alive)
+ break;
+ if (ctrlc()) {
+ printf("\nAbort\n");
+ break;
+ }
+ } while (ctx.seq_num <= PING_COUNT);
+
+ sys_untimeout(ping_send, &ctx);
+ ping_raw_stop(&ctx);
+
+ net_lwip_remove_netif(netif);
+
+ if (ctx.alive)
+ return 0;
+
+ printf("ping failed; host %s is not alive\n", ipaddr_ntoa(addr));
+ return -1;
+}
+
+int do_ping(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ ip_addr_t addr;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (net_lwip_dns_resolve(argv[1], &addr))
+ return CMD_RET_USAGE;
+
+ net_try_count = 1;
+restart:
+ if (net_lwip_eth_start() < 0 || ping_loop(eth_get_dev(), &addr) < 0) {
+ if (net_start_again() == 0)
+ goto restart;
+ else
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
diff --git a/cmd/lwip/sntp.c b/cmd/lwip/sntp.c
new file mode 100644
index 00000000000..608345c873b
--- /dev/null
+++ b/cmd/lwip/sntp.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2025 Linaro Ltd. */
+
+#include <command.h>
+#include <console.h>
+#include <dm/device.h>
+#include <env.h>
+#include <lwip/apps/sntp.h>
+#include <lwip/timeouts.h>
+#include <net.h>
+
+U_BOOT_CMD(sntp, 2, 1, do_sntp, "synchronize RTC via network",
+ "[NTPServerNameOrIp]");
+
+#define SNTP_TIMEOUT 10000
+
+static enum done_state {
+ NOT_DONE = 0,
+ SUCCESS,
+ ABORTED,
+ TIMED_OUT
+} sntp_state;
+
+static void no_response(void *arg)
+{
+ sntp_state = TIMED_OUT;
+}
+
+/* Called by lwIP via the SNTP_SET_SYSTEM_TIME() macro */
+void sntp_set_system_time(uint32_t seconds)
+{
+ char *toff;
+ int net_ntp_time_offset = 0;
+
+ toff = env_get("timeoffset");
+ if (toff)
+ net_ntp_time_offset = simple_strtol(toff, NULL, 10);
+
+ net_sntp_set_rtc(seconds + net_ntp_time_offset);
+ sntp_state = SUCCESS;
+}
+
+static bool ntp_server_known(void)
+{
+ int i;
+
+ for (i = 0; i < SNTP_MAX_SERVERS; i++) {
+ const ip_addr_t *ip = sntp_getserver(i);
+
+ if (ip && ip->addr)
+ return true;
+ }
+
+ return false;
+}
+
+static int sntp_loop(struct udevice *udev, ip_addr_t *srvip)
+{
+ struct netif *netif;
+
+ netif = net_lwip_new_netif(udev);
+ if (!netif)
+ return -1;
+
+ sntp_state = NOT_DONE;
+
+ sntp_setoperatingmode(SNTP_OPMODE_POLL);
+ sntp_servermode_dhcp(CONFIG_IS_ENABLED(CMD_DHCP));
+ if (srvip) {
+ sntp_setserver(0, srvip);
+ } else {
+ if (!ntp_server_known()) {
+ log_err("error: ntpserverip not set\n");
+ return -1;
+ }
+ }
+ sntp_init();
+
+ sys_timeout(SNTP_TIMEOUT, no_response, NULL);
+ while (sntp_state == NOT_DONE) {
+ net_lwip_rx(udev, netif);
+ if (ctrlc()) {
+ printf("\nAbort\n");
+ sntp_state = ABORTED;
+ break;
+ }
+ }
+ sys_untimeout(no_response, NULL);
+
+ sntp_stop();
+ net_lwip_remove_netif(netif);
+
+ if (sntp_state == SUCCESS)
+ return 0;
+
+ return -1;
+}
+
+int do_sntp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ ip_addr_t *srvip;
+ char *server;
+ ip_addr_t ipaddr;
+
+ switch (argc) {
+ case 1:
+ srvip = NULL;
+ server = env_get("ntpserverip");
+ if (server) {
+ if (!ipaddr_aton(server, &ipaddr)) {
+ printf("ntpserverip is invalid\n");
+ return CMD_RET_FAILURE;
+ }
+ srvip = &ipaddr;
+ }
+ break;
+ case 2:
+ if (net_lwip_dns_resolve(argv[1], &ipaddr))
+ return CMD_RET_FAILURE;
+ srvip = &ipaddr;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ if (net_lwip_eth_start() < 0)
+ return CMD_RET_FAILURE;
+
+ if (sntp_loop(eth_get_dev(), srvip) < 0)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
diff --git a/cmd/lwip/tftp.c b/cmd/lwip/tftp.c
new file mode 100644
index 00000000000..6bb7a3733a2
--- /dev/null
+++ b/cmd/lwip/tftp.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024-2025 Linaro Ltd. */
+
+#include <command.h>
+#include <net.h>
+
+U_BOOT_CMD(tftpboot, 3, 0, do_tftpb,
+ "boot image via network using TFTP protocol",
+ "[loadAddress] [[hostIPaddr:]bootfilename]");
diff --git a/cmd/lwip/wget.c b/cmd/lwip/wget.c
new file mode 100644
index 00000000000..fc9bc11cd83
--- /dev/null
+++ b/cmd/lwip/wget.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024-2025 Linaro Ltd. */
+
+#include <command.h>
+#include <env.h>
+#include <image.h>
+#include <net.h>
+#include <lwip/altcp_tls.h>
+
+U_BOOT_CMD(wget, 4, 1, do_wget,
+ "boot image via network using HTTP/HTTPS protocol"
+#if defined(CONFIG_WGET_CACERT)
+ "\nwget cacert - configure wget root certificates"
+#endif
+ ,
+ "[loadAddress] url\n"
+ "wget [loadAddress] [host:]path\n"
+ " - load file"
+#if defined(CONFIG_WGET_CACERT)
+ "\nwget cacert <address> <length>\n"
+ " - provide CA certificates (0 0 to remove current)"
+ "\nwget cacert none|optional|required\n"
+ " - set server certificate verification mode (default: optional)"
+#if defined(CONFIG_WGET_BUILTIN_CACERT)
+ "\nwget cacert builtin\n"
+ " - use the builtin CA certificates"
+#endif
+#endif
+);
+
+#if CONFIG_IS_ENABLED(WGET_CACERT) || CONFIG_IS_ENABLED(WGET_BUILTIN_CACERT)
+char *cacert;
+size_t cacert_size;
+enum auth_mode cacert_auth_mode = AUTH_OPTIONAL;
+
+#if CONFIG_IS_ENABLED(WGET_BUILTIN_CACERT)
+extern const char builtin_cacert[];
+extern const size_t builtin_cacert_size;
+bool cacert_initialized;
+#endif
+
+static int _set_cacert(const void *addr, size_t sz)
+{
+ mbedtls_x509_crt crt;
+ void *p;
+ int ret;
+
+ if (cacert)
+ free(cacert);
+
+ if (!addr) {
+ cacert = NULL;
+ cacert_size = 0;
+ return CMD_RET_SUCCESS;
+ }
+
+ p = malloc(sz);
+ if (!p)
+ return CMD_RET_FAILURE;
+ cacert = p;
+ cacert_size = sz;
+
+ memcpy(cacert, (void *)addr, sz);
+
+ mbedtls_x509_crt_init(&crt);
+ ret = mbedtls_x509_crt_parse(&crt, cacert, cacert_size);
+ if (ret) {
+ if (!wget_info->silent)
+ printf("Could not parse certificates (%d)\n", ret);
+ free(cacert);
+ cacert = NULL;
+ cacert_size = 0;
+ return CMD_RET_FAILURE;
+ }
+
+#if CONFIG_IS_ENABLED(WGET_BUILTIN_CACERT)
+ cacert_initialized = true;
+#endif
+ return CMD_RET_SUCCESS;
+}
+
+#if CONFIG_IS_ENABLED(WGET_BUILTIN_CACERT)
+int set_cacert_builtin(void)
+{
+ cacert_auth_mode = AUTH_REQUIRED;
+ return _set_cacert(builtin_cacert, builtin_cacert_size);
+}
+#endif
+#endif /* CONFIG_WGET_CACERT || CONFIG_WGET_BUILTIN_CACERT */
+
+#if CONFIG_IS_ENABLED(WGET_CACERT)
+static int set_auth(enum auth_mode auth)
+{
+ cacert_auth_mode = auth;
+
+ return CMD_RET_SUCCESS;
+}
+
+static int set_cacert(char * const saddr, char * const ssz)
+{
+ ulong addr, sz;
+
+ addr = hextoul(saddr, NULL);
+ sz = hextoul(ssz, NULL);
+
+ return _set_cacert((void *)addr, sz);
+}
+#endif
+
+/*
+ * Legacy syntax support
+ * Convert [<server_name_or_ip>:]filename into a URL if needed
+ */
+static int parse_legacy_arg(char *arg, char *nurl, size_t rem)
+{
+ char *p = nurl;
+ size_t n;
+ char *col = strchr(arg, ':');
+ char *env;
+ char *server;
+ char *path;
+
+ if (strstr(arg, "http") == arg) {
+ n = snprintf(nurl, rem, "%s", arg);
+ if (n < 0 || n > rem)
+ return -1;
+ return 0;
+ }
+
+ n = snprintf(p, rem, "%s", "http://");
+ if (n < 0 || n > rem)
+ return -1;
+ p += n;
+ rem -= n;
+
+ if (col) {
+ n = col - arg;
+ server = arg;
+ path = col + 1;
+ } else {
+ env = env_get("httpserverip");
+ if (!env)
+ env = env_get("serverip");
+ if (!env) {
+ log_err("error: httpserver/serverip has to be set\n");
+ return -1;
+ }
+ n = strlen(env);
+ server = env;
+ path = arg;
+ }
+
+ if (rem < n)
+ return -1;
+ strncpy(p, server, n);
+ p += n;
+ rem -= n;
+ if (rem < 1)
+ return -1;
+ *p = '/';
+ p++;
+ rem--;
+ n = strlen(path);
+ if (rem < n)
+ return -1;
+ strncpy(p, path, n);
+ p += n;
+ rem -= n;
+ if (rem < 1)
+ return -1;
+ *p = '\0';
+
+ return 0;
+}
+
+int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ char *end;
+ char *url;
+ ulong dst_addr;
+ char nurl[1024];
+
+#if CONFIG_IS_ENABLED(WGET_CACERT)
+ if (argc == 4 && !strncmp(argv[1], "cacert", strlen("cacert")))
+ return set_cacert(argv[2], argv[3]);
+ if (argc == 3 && !strncmp(argv[1], "cacert", strlen("cacert"))) {
+#if CONFIG_IS_ENABLED(WGET_BUILTIN_CACERT)
+ if (!strncmp(argv[2], "builtin", strlen("builtin")))
+ return set_cacert_builtin();
+#endif
+ if (!strncmp(argv[2], "none", strlen("none")))
+ return set_auth(AUTH_NONE);
+ if (!strncmp(argv[2], "optional", strlen("optional")))
+ return set_auth(AUTH_OPTIONAL);
+ if (!strncmp(argv[2], "required", strlen("required")))
+ return set_auth(AUTH_REQUIRED);
+ return CMD_RET_USAGE;
+ }
+#endif
+
+ if (argc < 2 || argc > 3)
+ return CMD_RET_USAGE;
+
+ dst_addr = hextoul(argv[1], &end);
+ if (end == (argv[1] + strlen(argv[1]))) {
+ if (argc < 3)
+ return CMD_RET_USAGE;
+ url = argv[2];
+ } else {
+ dst_addr = image_load_addr;
+ url = argv[1];
+ }
+
+ if (parse_legacy_arg(url, nurl, sizeof(nurl)))
+ return CMD_RET_FAILURE;
+
+ wget_info = &default_wget_info;
+ if (wget_do_request(dst_addr, nurl))
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
diff --git a/cmd/lzmadec.c b/cmd/lzmadec.c
new file mode 100644
index 00000000000..c40b96941b4
--- /dev/null
+++ b/cmd/lzmadec.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2013 Patrice Bouchand <pbfwdlist_gmail_com>
+ * lzma uncompress command in Uboot
+ *
+ * made from existing cmd_unzip.c file of Uboot
+ *
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <env.h>
+#include <mapmem.h>
+#include <vsprintf.h>
+#include <asm/io.h>
+
+#include <lzma/LzmaTools.h>
+
+static int do_lzmadec(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ unsigned long src, dst;
+ SizeT src_len = ~0UL, dst_len = ~0UL;
+ int ret;
+
+ switch (argc) {
+ case 4:
+ dst_len = hextoul(argv[3], NULL);
+ /* fall through */
+ case 3:
+ src = hextoul(argv[1], NULL);
+ dst = hextoul(argv[2], NULL);
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ ret = lzmaBuffToBuffDecompress(map_sysmem(dst, dst_len), &src_len,
+ map_sysmem(src, 0), dst_len);
+
+ if (ret != SZ_OK)
+ return 1;
+ printf("Uncompressed size: %ld = %#lX\n", (ulong)src_len,
+ (ulong)src_len);
+ env_set_hex("filesize", src_len);
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ lzmadec, 4, 1, do_lzmadec,
+ "lzma uncompress a memory region",
+ "srcaddr dstaddr [dstsize]"
+);
diff --git a/cmd/mbr.c b/cmd/mbr.c
new file mode 100644
index 00000000000..7fe6c9e103a
--- /dev/null
+++ b/cmd/mbr.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * cmd_mbr.c -- MBR (Master Boot Record) handling command
+ *
+ * Copyright (C) 2020 Samsung Electronics
+ * author: Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * based on the gpt command.
+ */
+
+#include <blk.h>
+#include <command.h>
+#include <env.h>
+#include <malloc.h>
+#include <part.h>
+#include <vsprintf.h>
+
+/**
+ * extract_val() - Extract a value from the key=value pair list
+ * @str: pointer to string with key=values pairs
+ * @key: pointer to the key to search for
+ *
+ * The list of parameters is come separated, only a value for
+ * the given key is returend.
+ *
+ * Function allocates memory for the value, remember to free!
+ *
+ * Return: Pointer to allocated string with the value.
+ */
+static char *extract_val(const char *str, const char *key)
+{
+ char *v, *k;
+ char *s, *strcopy;
+ char *new = NULL;
+
+ strcopy = strdup(str);
+ if (strcopy == NULL)
+ return NULL;
+
+ s = strcopy;
+ while (s) {
+ v = strsep(&s, ",");
+ if (!v)
+ break;
+ k = strsep(&v, "=");
+ if (!k)
+ break;
+ if (strcmp(k, key) == 0) {
+ new = strdup(v);
+ break;
+ }
+ }
+
+ free(strcopy);
+
+ return new;
+}
+
+/**
+ * found_key() - Search for a key without a value in the parameter list
+ * @str: pointer to string with key
+ * @key: pointer to the key to search for
+ *
+ * The list of parameters is come separated.
+ *
+ * Return: True if key has been found.
+ */
+static bool found_key(const char *str, const char *key)
+{
+ char *k;
+ char *s, *strcopy;
+ bool result = false;
+
+ strcopy = strdup(str);
+ if (!strcopy)
+ return NULL;
+
+ s = strcopy;
+ while (s) {
+ k = strsep(&s, ",");
+ if (!k)
+ break;
+ if (strcmp(k, key) == 0) {
+ result = true;
+ break;
+ }
+ }
+
+ free(strcopy);
+
+ return result;
+}
+
+static int str_to_partitions(const char *str_part, int blksz,
+ unsigned long *disk_uuid, struct disk_partition **partitions,
+ int *parts_count)
+{
+ char *tok, *str, *s;
+ int i;
+ char *val, *p;
+ int p_count;
+ struct disk_partition *parts;
+ int errno = 0;
+ uint64_t size_ll, start_ll;
+
+ if (str_part == NULL)
+ return -1;
+
+ str = strdup(str_part);
+ if (str == NULL)
+ return -ENOMEM;
+
+ /* extract disk guid */
+ s = str;
+ val = extract_val(str, "uuid_disk");
+ if (val) {
+ val = strsep(&val, ";");
+ p = val;
+ *disk_uuid = ustrtoull(p, &p, 0);
+ free(val);
+ /* Move s to first partition */
+ strsep(&s, ";");
+ }
+ if (s == NULL) {
+ printf("Error: is the partitions string NULL-terminated?\n");
+ return -EINVAL;
+ }
+
+ /* remove the optional semicolon at the end of the string */
+ i = strlen(s) - 1;
+ if (s[i] == ';')
+ s[i] = '\0';
+
+ /* calculate expected number of partitions */
+ p_count = 1;
+ p = s;
+ while (*p) {
+ if (*p++ == ';')
+ p_count++;
+ }
+
+ /* allocate memory for partitions */
+ parts = calloc(sizeof(struct disk_partition), p_count);
+ if (parts == NULL)
+ return -ENOMEM;
+
+ /* retrieve partitions data from string */
+ for (i = 0; i < p_count; i++) {
+ tok = strsep(&s, ";");
+
+ if (tok == NULL)
+ break;
+
+ /* size */
+ val = extract_val(tok, "size");
+ if (!val) { /* 'size' is mandatory */
+ errno = -4;
+ goto err;
+ }
+ p = val;
+ if ((strcmp(p, "-") == 0)) {
+ /* auto extend the size */
+ parts[i].size = 0;
+ } else {
+ size_ll = ustrtoull(p, &p, 0);
+ parts[i].size = size_ll / blksz;
+ }
+ free(val);
+
+ /* start address */
+ val = extract_val(tok, "start");
+ if (val) { /* start address is optional */
+ p = val;
+ start_ll = ustrtoull(p, &p, 0);
+ parts[i].start = start_ll / blksz;
+ free(val);
+ }
+
+ /* system id */
+ val = extract_val(tok, "id");
+ if (!val) { /* '' is mandatory */
+ errno = -4;
+ goto err;
+ }
+ p = val;
+ parts[i].sys_ind = ustrtoul(p, &p, 0);
+ free(val);
+
+ /* bootable */
+ if (found_key(tok, "bootable"))
+ parts[i].bootable = PART_BOOTABLE;
+ }
+
+ *parts_count = p_count;
+ *partitions = parts;
+ free(str);
+
+ return 0;
+err:
+ free(str);
+ free(parts);
+
+ return errno;
+}
+
+static int do_write_mbr(struct blk_desc *dev, const char *str)
+{
+ unsigned long disk_uuid = 0;
+ struct disk_partition *partitions;
+ int blksz = dev->blksz;
+ int count;
+
+ if (str_to_partitions(str, blksz, &disk_uuid, &partitions, &count)) {
+ printf("MBR: failed to setup partitions from \"%s\"\n", str);
+ return -1;
+ }
+
+ if (layout_mbr_partitions(partitions, count, dev->lba)) {
+ printf("MBR: failed to layout partitions on the device\n");
+ free(partitions);
+ return -1;
+ }
+
+ if (write_mbr_partitions(dev, partitions, count, disk_uuid)) {
+ printf("MBR: failed to write partitions to the device\n");
+ free(partitions);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int do_verify_mbr(struct blk_desc *dev, const char *str)
+{
+ unsigned long disk_uuid = 0;
+ struct disk_partition *partitions;
+ int blksz = dev->blksz;
+ int count, i, ret = 1;
+
+ if (str_to_partitions(str, blksz, &disk_uuid, &partitions, &count)) {
+ printf("MBR: failed to setup partitions from \"%s\"\n", str);
+ return -1;
+ }
+
+ for (i = 0; i < count; i++) {
+ struct disk_partition p;
+
+ if (part_get_info_by_type(dev, i + 1, PART_TYPE_DOS, &p))
+ goto fail;
+
+ if ((partitions[i].size && p.size != partitions[i].size) ||
+ (partitions[i].start && p.start != partitions[i].start) ||
+ p.sys_ind != partitions[i].sys_ind)
+ goto fail;
+ }
+ ret = 0;
+fail:
+ free(partitions);
+ return ret;
+}
+
+static int do_mbr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ const char *parts = NULL;
+ int ret = CMD_RET_SUCCESS;
+ int dev = 0;
+ char *ep;
+ struct blk_desc *blk_dev_desc = NULL;
+
+ if (argc != 4 && argc != 5)
+ return CMD_RET_USAGE;
+
+ dev = (int)dectoul(argv[3], &ep);
+ if (!ep || ep[0] != '\0') {
+ printf("'%s' is not a number\n", argv[3]);
+ return CMD_RET_USAGE;
+ }
+ blk_dev_desc = blk_get_dev(argv[2], dev);
+ if (!blk_dev_desc) {
+ printf("%s: %s dev %d NOT available\n",
+ __func__, argv[2], dev);
+ return CMD_RET_FAILURE;
+ }
+
+ if ((strcmp(argv[1], "write") == 0)) {
+ parts = (argc == 5) ? argv[4] : env_get("mbr_parts");
+ printf("MBR: write ");
+ ret = do_write_mbr(blk_dev_desc, parts);
+ } else if ((strcmp(argv[1], "verify") == 0)) {
+ printf("MBR: verify ");
+ parts = (argc == 5) ? argv[4] : env_get("mbr_parts");
+ ret = do_verify_mbr(blk_dev_desc, parts);
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ if (ret) {
+ printf("error!\n");
+ return CMD_RET_FAILURE;
+ }
+
+ printf("success!\n");
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(mbr, CONFIG_SYS_MAXARGS, 1, do_mbr,
+ "MBR (Master Boot Record)",
+ "<command> <interface> <dev> <partitions_list>\n"
+ " - MBR partition table restoration utility\n"
+ " Restore or check partition information on a device connected\n"
+ " to the given block interface\n"
+ " Example usage:\n"
+ " mbr write mmc 0 [\"${mbr_parts}\"]\n"
+ " mbr verify mmc 0 [\"${partitions}\"]\n"
+);
diff --git a/cmd/md5sum.c b/cmd/md5sum.c
new file mode 100644
index 00000000000..ded3f9e1831
--- /dev/null
+++ b/cmd/md5sum.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2011
+ * Joe Hershberger, National Instruments, joe.hershberger@ni.com
+ *
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <env.h>
+#include <image.h>
+#include <hash.h>
+#include <mapmem.h>
+#include <u-boot/md5.h>
+#include <asm/io.h>
+
+static int do_md5sum(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int flags = HASH_FLAG_ENV;
+ int ac;
+ char *const *av;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ av = argv + 1;
+ ac = argc - 1;
+ if (IS_ENABLED(CONFIG_MD5SUM_VERIFY) && strcmp(*av, "-v") == 0) {
+ flags |= HASH_FLAG_VERIFY;
+ av++;
+ ac--;
+ }
+
+ return hash_command("md5", flags, cmdtp, flag, ac, av);
+}
+
+#if IS_ENABLED(CONFIG_MD5SUM_VERIFY)
+U_BOOT_CMD(
+ md5sum, 5, 1, do_md5sum,
+ "compute MD5 message digest",
+ "address count [[*]sum]\n"
+ " - compute MD5 message digest [save to sum]\n"
+ "md5sum -v address count [*]sum\n"
+ " - verify md5sum of memory area"
+);
+#else
+U_BOOT_CMD(
+ md5sum, 4, 1, do_md5sum,
+ "compute MD5 message digest",
+ "address count [[*]sum]\n"
+ " - compute MD5 message digest [save to sum]"
+);
+#endif /* IS_ENABLED(CONFIG_MD5SUM_VERIFY) */
diff --git a/cmd/mdio.c b/cmd/mdio.c
new file mode 100644
index 00000000000..c0a87087d31
--- /dev/null
+++ b/cmd/mdio.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2011 Freescale Semiconductor, Inc
+ * Andy Fleming
+ */
+
+/*
+ * MDIO Commands
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <miiphy.h>
+#include <phy.h>
+
+static char last_op[2];
+static uint last_data;
+static uint last_addr_lo;
+static uint last_addr_hi;
+static uint last_devad_lo;
+static uint last_devad_hi;
+static uint last_reg_lo;
+static uint last_reg_hi;
+
+static int extract_range(char *input, int *plo, int *phi)
+{
+ char *end;
+ *plo = simple_strtol(input, &end, 16);
+ if (end == input)
+ return -1;
+
+ if ((*end == '-') && *(++end))
+ *phi = simple_strtol(end, NULL, 16);
+ else if (*end == '\0')
+ *phi = *plo;
+ else
+ return -1;
+
+ return 0;
+}
+
+static int mdio_write_ranges(struct mii_dev *bus,
+ int addrlo,
+ int addrhi, int devadlo, int devadhi,
+ int reglo, int reghi, unsigned short data,
+ int extended)
+{
+ struct phy_device *phydev;
+ int addr, devad, reg;
+ int err = 0;
+
+ for (addr = addrlo; addr <= addrhi; addr++) {
+ phydev = bus->phymap[addr];
+
+ for (devad = devadlo; devad <= devadhi; devad++) {
+ for (reg = reglo; reg <= reghi; reg++) {
+ if (!phydev)
+ err = bus->write(bus, addr, devad,
+ reg, data);
+ else if (!extended)
+ err = phy_write_mmd(phydev, devad,
+ reg, data);
+ else
+ err = phydev->drv->writeext(phydev,
+ addr, devad, reg, data);
+
+ if (err)
+ goto err_out;
+ }
+ }
+ }
+
+err_out:
+ return err;
+}
+
+static int mdio_read_ranges(struct mii_dev *bus,
+ int addrlo,
+ int addrhi, int devadlo, int devadhi,
+ int reglo, int reghi, int extended)
+{
+ int addr, devad, reg;
+ struct phy_device *phydev;
+
+ printf("Reading from bus %s\n", bus->name);
+ for (addr = addrlo; addr <= addrhi; addr++) {
+ phydev = bus->phymap[addr];
+ printf("PHY at address %x:\n", addr);
+
+ for (devad = devadlo; devad <= devadhi; devad++) {
+ for (reg = reglo; reg <= reghi; reg++) {
+ int val;
+
+ if (!phydev)
+ val = bus->read(bus, addr, devad, reg);
+ else if (!extended)
+ val = phy_read_mmd(phydev, devad, reg);
+ else
+ val = phydev->drv->readext(phydev, addr,
+ devad, reg);
+
+ if (val < 0) {
+ printf("Error\n");
+
+ return val;
+ }
+
+ if (devad >= 0)
+ printf("%d.", devad);
+
+ printf("%d - 0x%x\n", reg, val & 0xffff);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* The register will be in the form [a[-b].]x[-y] */
+static int extract_reg_range(char *input, int *devadlo, int *devadhi,
+ int *reglo, int *reghi)
+{
+ char *regstr;
+
+ /* use strrchr to find the last string after a '.' */
+ regstr = strrchr(input, '.');
+
+ /* If it exists, extract the devad(s) */
+ if (regstr) {
+ char devadstr[32];
+
+ strncpy(devadstr, input, regstr - input);
+ devadstr[regstr - input] = '\0';
+
+ if (extract_range(devadstr, devadlo, devadhi))
+ return -1;
+
+ regstr++;
+ } else {
+ /* Otherwise, we have no devad, and we just got regs */
+ *devadlo = *devadhi = MDIO_DEVAD_NONE;
+
+ regstr = input;
+ }
+
+ return extract_range(regstr, reglo, reghi);
+}
+
+static int extract_phy_range(char *const argv[], int argc, struct mii_dev **bus,
+ struct phy_device **phydev,
+ int *addrlo, int *addrhi)
+{
+ struct phy_device *dev = *phydev;
+
+ if ((argc < 1) || (argc > 2))
+ return -1;
+
+ /* If there are two arguments, it's busname addr */
+ if (argc == 2) {
+ *bus = miiphy_get_dev_by_name(argv[0]);
+
+ if (!*bus)
+ return -1;
+
+ return extract_range(argv[1], addrlo, addrhi);
+ }
+
+ /* It must be one argument, here */
+
+ /*
+ * This argument can be one of two things:
+ * 1) Ethernet device name
+ * 2) Just an address (use the previously-used bus)
+ *
+ * We check all buses for a PHY which is connected to an ethernet
+ * device by the given name. If none are found, we call
+ * extract_range() on the string, and see if it's an address range.
+ */
+ dev = mdio_phydev_for_ethname(argv[0]);
+
+ if (dev) {
+ *addrlo = *addrhi = dev->addr;
+ *bus = dev->bus;
+
+ return 0;
+ }
+
+ /* It's an address or nothing useful */
+ return extract_range(argv[0], addrlo, addrhi);
+}
+
+/* ---------------------------------------------------------------- */
+static int do_mdio(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char op[2];
+ int addrlo, addrhi, reglo, reghi, devadlo, devadhi;
+ unsigned short data;
+ int pos = argc - 1;
+ struct mii_dev *bus;
+ struct phy_device *phydev = NULL;
+ int extended = 0;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+#ifdef CONFIG_DM_MDIO
+ /* probe DM MII device before any operation so they are all accesible */
+ dm_mdio_probe_devices();
+#endif
+
+ /*
+ * We use the last specified parameters, unless new ones are
+ * entered.
+ */
+ op[0] = argv[1][0];
+ addrlo = last_addr_lo;
+ addrhi = last_addr_hi;
+ devadlo = last_devad_lo;
+ devadhi = last_devad_hi;
+ reglo = last_reg_lo;
+ reghi = last_reg_hi;
+ data = last_data;
+
+ bus = mdio_get_current_dev();
+
+ if (flag & CMD_FLAG_REPEAT)
+ op[0] = last_op[0];
+
+ if (strlen(argv[1]) > 1) {
+ op[1] = argv[1][1];
+ if (op[1] == 'x') {
+ phydev = mdio_phydev_for_ethname(argv[2]);
+
+ if (phydev) {
+ addrlo = phydev->addr;
+ addrhi = addrlo;
+ bus = phydev->bus;
+ extended = 1;
+ } else {
+ return CMD_RET_FAILURE;
+ }
+
+ if (!phydev->drv ||
+ (!phydev->drv->writeext && (op[0] == 'w')) ||
+ (!phydev->drv->readext && (op[0] == 'r'))) {
+ puts("PHY does not have extended functions\n");
+ return CMD_RET_FAILURE;
+ }
+ }
+ }
+
+ switch (op[0]) {
+ case 'w':
+ if (pos > 1)
+ data = hextoul(argv[pos--], NULL);
+ /* Intentional fall-through - Get reg for read and write */
+ case 'r':
+ if (pos > 1)
+ if (extract_reg_range(argv[pos--], &devadlo, &devadhi,
+ &reglo, &reghi))
+ return CMD_RET_FAILURE;
+ /* Intentional fall-through - Get phy for all commands */
+ default:
+ if (pos > 1)
+ if (extract_phy_range(&argv[2], pos - 1, &bus,
+ &phydev, &addrlo, &addrhi))
+ return CMD_RET_FAILURE;
+
+ break;
+ }
+
+ if (!bus) {
+ puts("No MDIO bus found\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (op[0] == 'l') {
+ mdio_list_devices();
+
+ return 0;
+ }
+
+ /* Save the chosen bus */
+ miiphy_set_current_dev(bus->name);
+
+ switch (op[0]) {
+ case 'w':
+ mdio_write_ranges(bus, addrlo, addrhi, devadlo, devadhi,
+ reglo, reghi, data, extended);
+ break;
+
+ case 'r':
+ mdio_read_ranges(bus, addrlo, addrhi, devadlo, devadhi,
+ reglo, reghi, extended);
+ break;
+ }
+
+ /*
+ * Save the parameters for repeats.
+ */
+ last_op[0] = op[0];
+ last_addr_lo = addrlo;
+ last_addr_hi = addrhi;
+ last_devad_lo = devadlo;
+ last_devad_hi = devadhi;
+ last_reg_lo = reglo;
+ last_reg_hi = reghi;
+ last_data = data;
+
+ return 0;
+}
+
+/***************************************************/
+
+U_BOOT_CMD(
+ mdio, 6, 1, do_mdio,
+ "MDIO utility commands",
+ "list - List MDIO buses\n"
+ "mdio read <phydev> [<devad>.]<reg> - "
+ "read PHY's register at <devad>.<reg>\n"
+ "mdio write <phydev> [<devad>.]<reg> <data> - "
+ "write PHY's register at <devad>.<reg>\n"
+ "mdio rx <phydev> [<devad>.]<reg> - "
+ "read PHY's extended register at <devad>.<reg>\n"
+ "mdio wx <phydev> [<devad>.]<reg> <data> - "
+ "write PHY's extended register at <devad>.<reg>\n"
+ "<phydev> may be:\n"
+ " <busname> <addr>\n"
+ " <addr>\n"
+ " <eth name>\n"
+ "<addr> <devad>, and <reg> may be ranges, e.g. 1-5.4-0x1f.\n"
+);
diff --git a/cmd/mem.c b/cmd/mem.c
new file mode 100644
index 00000000000..d5d7ca2790b
--- /dev/null
+++ b/cmd/mem.c
@@ -0,0 +1,1436 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+/*
+ * Memory Functions
+ *
+ * Copied from FADS ROM, Dan Malek (dmalek@jlc.net)
+ */
+
+#include <console.h>
+#include <bootretry.h>
+#include <cli.h>
+#include <command.h>
+#include <compiler.h>
+#include <console.h>
+#include <display_options.h>
+#include <env.h>
+#ifdef CONFIG_MTD_NOR_FLASH
+#include <flash.h>
+#endif
+#include <hash.h>
+#include <log.h>
+#include <mapmem.h>
+#include <rand.h>
+#include <time.h>
+#include <watchdog.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Create a compile-time value */
+#if MEM_SUPPORT_64BIT_DATA
+#define HELP_Q ", .q"
+#else
+#define HELP_Q ""
+#endif
+
+static int mod_mem(struct cmd_tbl *, int, int, int, char * const []);
+
+/* Display values from last command.
+ * Memory modify remembered values are different from display memory.
+ */
+static ulong dp_last_addr, dp_last_size;
+static ulong dp_last_length = 0x40;
+static ulong mm_last_addr, mm_last_size;
+
+static ulong base_address = 0;
+#ifdef CONFIG_CMD_MEM_SEARCH
+static ulong dp_last_ms_length;
+static u8 search_buf[64];
+static uint search_len;
+#endif
+
+/* Memory Display
+ *
+ * Syntax:
+ * md{.b, .w, .l, .q} {addr} {len}
+ */
+#define DISP_LINE_LEN 16
+static int do_mem_md(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr, length, bytes;
+ const void *buf;
+ int size;
+ int rc = 0;
+
+ /* We use the last specified parameters, unless new ones are
+ * entered.
+ */
+ addr = dp_last_addr;
+ size = dp_last_size;
+ length = dp_last_length;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ /* New command specified. Check for a size specification.
+ * Defaults to long if no or incorrect specification.
+ */
+ if ((size = cmd_get_data_size(argv[0], 4)) < 0)
+ return 1;
+
+ /* Address is specified since argc > 1
+ */
+ addr = hextoul(argv[1], NULL);
+ addr += base_address;
+
+ /* If another parameter, it is the length to display.
+ * Length is the number of objects, not number of bytes.
+ */
+ if (argc > 2)
+ length = hextoul(argv[2], NULL);
+ }
+
+ bytes = size * length;
+ buf = map_sysmem(addr, bytes);
+
+ /* Print the lines. */
+ print_buffer(addr, buf, size, length, DISP_LINE_LEN / size);
+ addr += bytes;
+ unmap_sysmem(buf);
+
+ dp_last_addr = addr;
+ dp_last_length = length;
+ dp_last_size = size;
+ return (rc);
+}
+
+static int do_mem_mm(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return mod_mem (cmdtp, 1, flag, argc, argv);
+}
+
+static int do_mem_nm(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return mod_mem (cmdtp, 0, flag, argc, argv);
+}
+
+static int do_mem_mw(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong writeval; /* 64-bit if MEM_SUPPORT_64BIT_DATA */
+ ulong addr, count;
+ int size;
+ void *buf, *start;
+ ulong bytes;
+
+ if ((argc < 3) || (argc > 4))
+ return CMD_RET_USAGE;
+
+ /* Check for size specification.
+ */
+ if ((size = cmd_get_data_size(argv[0], 4)) < 1)
+ return 1;
+
+ /* Address is specified since argc > 1
+ */
+ addr = hextoul(argv[1], NULL);
+ addr += base_address;
+
+ /* Get the value to write.
+ */
+ if (MEM_SUPPORT_64BIT_DATA)
+ writeval = simple_strtoull(argv[2], NULL, 16);
+ else
+ writeval = hextoul(argv[2], NULL);
+
+ /* Count ? */
+ if (argc == 4) {
+ count = hextoul(argv[3], NULL);
+ } else {
+ count = 1;
+ }
+
+ bytes = size * count;
+ start = map_sysmem(addr, bytes);
+ buf = start;
+ while (count-- > 0) {
+ if (size == 4)
+ *((u32 *)buf) = (u32)writeval;
+ else if (MEM_SUPPORT_64BIT_DATA && size == 8)
+ *((ulong *)buf) = writeval;
+ else if (size == 2)
+ *((u16 *)buf) = (u16)writeval;
+ else
+ *((u8 *)buf) = (u8)writeval;
+ buf += size;
+ }
+ unmap_sysmem(start);
+ return 0;
+}
+
+#ifdef CONFIG_CMD_MX_CYCLIC
+static int do_mem_mdc(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int i;
+ ulong count;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ count = dectoul(argv[3], NULL);
+
+ for (;;) {
+ do_mem_md (NULL, 0, 3, argv);
+
+ /* delay for <count> ms... */
+ for (i=0; i<count; i++)
+ udelay(1000);
+
+ /* check for ctrl-c to abort... */
+ if (ctrlc()) {
+ puts("Abort\n");
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int do_mem_mwc(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int i;
+ ulong count;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ count = dectoul(argv[3], NULL);
+
+ for (;;) {
+ do_mem_mw (NULL, 0, 3, argv);
+
+ /* delay for <count> ms... */
+ for (i=0; i<count; i++)
+ udelay(1000);
+
+ /* check for ctrl-c to abort... */
+ if (ctrlc()) {
+ puts("Abort\n");
+ return 0;
+ }
+ }
+
+ return 0;
+}
+#endif /* CONFIG_CMD_MX_CYCLIC */
+
+static int do_mem_cmp(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr1, addr2, count, ngood, bytes;
+ int size;
+ int rcode = 0;
+ const char *type;
+ const void *buf1, *buf2, *base, *ptr1, *ptr2;
+ ulong word1, word2; /* 64-bit if MEM_SUPPORT_64BIT_DATA */
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ /* Check for size specification.
+ */
+ if ((size = cmd_get_data_size(argv[0], 4)) < 0)
+ return 1;
+ type = size == 8 ? "double word" :
+ size == 4 ? "word" :
+ size == 2 ? "halfword" : "byte";
+
+ addr1 = hextoul(argv[1], NULL);
+ addr1 += base_address;
+
+ addr2 = hextoul(argv[2], NULL);
+ addr2 += base_address;
+
+ count = hextoul(argv[3], NULL);
+
+ bytes = size * count;
+ base = buf1 = map_sysmem(addr1, bytes);
+ buf2 = map_sysmem(addr2, bytes);
+ for (ngood = 0, ptr1 = buf1, ptr2 = buf2; ngood < count; ++ngood) {
+ if (size == 4) {
+ word1 = *(u32 *)ptr1;
+ word2 = *(u32 *)ptr2;
+ } else if (MEM_SUPPORT_64BIT_DATA && size == 8) {
+ word1 = *(ulong *)ptr1;
+ word2 = *(ulong *)ptr2;
+ } else if (size == 2) {
+ word1 = *(u16 *)ptr1;
+ word2 = *(u16 *)ptr2;
+ } else {
+ word1 = *(u8 *)ptr1;
+ word2 = *(u8 *)ptr2;
+ }
+ if (word1 != word2) {
+ ulong offset = ptr1 - base;
+ printf("%s at 0x%08lx (%#0*lx) != %s at 0x%08lx (%#0*lx)\n",
+ type, (ulong)(addr1 + offset), size, word1,
+ type, (ulong)(addr2 + offset), size, word2);
+ rcode = 1;
+ break;
+ }
+
+ ptr1 += size;
+ ptr2 += size;
+
+ /* reset watchdog from time to time */
+ if ((ngood % (64 << 10)) == 0)
+ schedule();
+ }
+ unmap_sysmem(buf1);
+ unmap_sysmem(buf2);
+
+ printf("Total of %ld %s(s) were the same\n", ngood, type);
+ return rcode;
+}
+
+static int do_mem_cp(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr, dest, count;
+ void *src, *dst;
+ int size;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ /* Check for size specification.
+ */
+ if ((size = cmd_get_data_size(argv[0], 4)) < 0)
+ return 1;
+
+ addr = hextoul(argv[1], NULL);
+ addr += base_address;
+
+ dest = hextoul(argv[2], NULL);
+ dest += base_address;
+
+ count = hextoul(argv[3], NULL);
+
+ if (count == 0) {
+ puts ("Zero length ???\n");
+ return 1;
+ }
+
+ src = map_sysmem(addr, count * size);
+ dst = map_sysmem(dest, count * size);
+
+#ifdef CONFIG_MTD_NOR_FLASH
+ /* check if we are copying to Flash */
+ if (addr2info((ulong)dst)) {
+ int rc;
+
+ puts ("Copy to Flash... ");
+
+ rc = flash_write((char *)src, (ulong)dst, count * size);
+ if (rc != 0) {
+ flash_perror(rc);
+ unmap_sysmem(src);
+ unmap_sysmem(dst);
+ return (1);
+ }
+ puts ("done\n");
+ unmap_sysmem(src);
+ unmap_sysmem(dst);
+ return 0;
+ }
+#endif
+
+ memmove(dst, src, count * size);
+
+ unmap_sysmem(src);
+ unmap_sysmem(dst);
+ return 0;
+}
+
+#ifdef CONFIG_CMD_MEM_SEARCH
+static int do_mem_search(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr, length, bytes, offset;
+ u8 *ptr, *end, *buf;
+ bool quiet = false;
+ ulong last_pos; /* Offset of last match in 'size' units*/
+ ulong last_addr; /* Address of last displayed line */
+ int limit = 10;
+ int used_len;
+ int count;
+ int size;
+ int i;
+
+ /* We use the last specified parameters, unless new ones are entered */
+ addr = dp_last_addr;
+ size = dp_last_size;
+ length = dp_last_ms_length;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ if (!(flag & CMD_FLAG_REPEAT)) {
+ /*
+ * Check for a size specification.
+ * Defaults to long if no or incorrect specification.
+ */
+ size = cmd_get_data_size(argv[0], 4);
+ if (size < 0 && size != CMD_DATA_SIZE_STR)
+ return 1;
+
+ argc--;
+ argv++;
+ while (argc && *argv[0] == '-') {
+ int ch = argv[0][1];
+
+ if (ch == 'q')
+ quiet = true;
+ else if (ch == 'l' && isxdigit(argv[0][2]))
+ limit = hextoul(argv[0] + 2, NULL);
+ else
+ return CMD_RET_USAGE;
+ argc--;
+ argv++;
+ }
+
+ /* Address is specified since argc > 1 */
+ addr = hextoul(argv[0], NULL);
+ addr += base_address;
+
+ /* Length is the number of objects, not number of bytes */
+ length = hextoul(argv[1], NULL);
+
+ /* Read the bytes to search for */
+ end = search_buf + sizeof(search_buf);
+ for (i = 2, ptr = search_buf; i < argc && ptr < end; i++) {
+ if (MEM_SUPPORT_64BIT_DATA && size == 8) {
+ u64 val = simple_strtoull(argv[i], NULL, 16);
+
+ *(u64 *)ptr = val;
+ } else if (size == -2) { /* string */
+ int len = min(strlen(argv[i]),
+ (size_t)(end - ptr));
+
+ memcpy(ptr, argv[i], len);
+ ptr += len;
+ continue;
+ } else {
+ u32 val = hextoul(argv[i], NULL);
+
+ switch (size) {
+ case 1:
+ *ptr = val;
+ break;
+ case 2:
+ *(u16 *)ptr = val;
+ break;
+ case 4:
+ *(u32 *)ptr = val;
+ break;
+ }
+ }
+ ptr += size;
+ }
+ search_len = ptr - search_buf;
+ }
+
+ /* Do the search */
+ if (size == -2)
+ size = 1;
+ bytes = size * length;
+ buf = map_sysmem(addr, bytes);
+ last_pos = 0;
+ last_addr = 0;
+ count = 0;
+ for (offset = 0;
+ offset < bytes && offset <= bytes - search_len && count < limit;
+ offset += size) {
+ void *ptr = buf + offset;
+
+ if (!memcmp(ptr, search_buf, search_len)) {
+ uint align = (addr + offset) & 0xf;
+ ulong match = addr + offset;
+
+ if (!count || (last_addr & ~0xf) != (match & ~0xf)) {
+ if (!quiet) {
+ if (count)
+ printf("--\n");
+ print_buffer(match - align, ptr - align,
+ size,
+ ALIGN(search_len + align,
+ 16) / size, 0);
+ }
+ last_addr = match;
+ last_pos = offset / size;
+ }
+ count++;
+ }
+ }
+ if (!quiet) {
+ printf("%d match%s", count, count == 1 ? "" : "es");
+ if (count == limit)
+ printf(" (repeat command to check for more)");
+ printf("\n");
+ }
+ env_set_hex("memmatches", count);
+ env_set_hex("memaddr", last_addr);
+ env_set_hex("mempos", last_pos);
+
+ unmap_sysmem(buf);
+
+ used_len = offset / size;
+ dp_last_addr = addr + used_len;
+ dp_last_size = size;
+ dp_last_ms_length = length < used_len ? 0 : length - used_len;
+
+ return count ? 0 : CMD_RET_FAILURE;
+}
+#endif
+
+static int do_mem_base(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (argc > 1) {
+ /* Set new base address.
+ */
+ base_address = hextoul(argv[1], NULL);
+ }
+ /* Print the current base address.
+ */
+ printf("Base Address: 0x%08lx\n", base_address);
+ return 0;
+}
+
+static int do_mem_loop(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr, length, i, bytes;
+ int size;
+ volatile ulong *llp; /* 64-bit if MEM_SUPPORT_64BIT_DATA */
+ volatile u32 *longp;
+ volatile u16 *shortp;
+ volatile u8 *cp;
+ const void *buf;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ /*
+ * Check for a size specification.
+ * Defaults to long if no or incorrect specification.
+ */
+ if ((size = cmd_get_data_size(argv[0], 4)) < 0)
+ return 1;
+
+ /* Address is always specified.
+ */
+ addr = hextoul(argv[1], NULL);
+
+ /* Length is the number of objects, not number of bytes.
+ */
+ length = hextoul(argv[2], NULL);
+
+ bytes = size * length;
+ buf = map_sysmem(addr, bytes);
+
+ /* We want to optimize the loops to run as fast as possible.
+ * If we have only one object, just run infinite loops.
+ */
+ if (length == 1) {
+ if (MEM_SUPPORT_64BIT_DATA && size == 8) {
+ llp = (ulong *)buf;
+ for (;;)
+ i = *llp;
+ }
+ if (size == 4) {
+ longp = (u32 *)buf;
+ for (;;)
+ i = *longp;
+ }
+ if (size == 2) {
+ shortp = (u16 *)buf;
+ for (;;)
+ i = *shortp;
+ }
+ cp = (u8 *)buf;
+ for (;;)
+ i = *cp;
+ }
+
+ if (MEM_SUPPORT_64BIT_DATA && size == 8) {
+ for (;;) {
+ llp = (ulong *)buf;
+ i = length;
+ while (i-- > 0)
+ *llp++;
+ }
+ }
+ if (size == 4) {
+ for (;;) {
+ longp = (u32 *)buf;
+ i = length;
+ while (i-- > 0)
+ *longp++;
+ }
+ }
+ if (size == 2) {
+ for (;;) {
+ shortp = (u16 *)buf;
+ i = length;
+ while (i-- > 0)
+ *shortp++;
+ }
+ }
+ for (;;) {
+ cp = (u8 *)buf;
+ i = length;
+ while (i-- > 0)
+ *cp++;
+ }
+ unmap_sysmem(buf);
+
+ return 0;
+}
+
+#ifdef CONFIG_LOOPW
+static int do_mem_loopw(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr, length, i, bytes;
+ int size;
+ volatile ulong *llp; /* 64-bit if MEM_SUPPORT_64BIT_DATA */
+ ulong data; /* 64-bit if MEM_SUPPORT_64BIT_DATA */
+ volatile u32 *longp;
+ volatile u16 *shortp;
+ volatile u8 *cp;
+ void *buf;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ /*
+ * Check for a size specification.
+ * Defaults to long if no or incorrect specification.
+ */
+ if ((size = cmd_get_data_size(argv[0], 4)) < 0)
+ return 1;
+
+ /* Address is always specified.
+ */
+ addr = hextoul(argv[1], NULL);
+
+ /* Length is the number of objects, not number of bytes.
+ */
+ length = hextoul(argv[2], NULL);
+
+ /* data to write */
+ if (MEM_SUPPORT_64BIT_DATA)
+ data = simple_strtoull(argv[3], NULL, 16);
+ else
+ data = hextoul(argv[3], NULL);
+
+ bytes = size * length;
+ buf = map_sysmem(addr, bytes);
+
+ /* We want to optimize the loops to run as fast as possible.
+ * If we have only one object, just run infinite loops.
+ */
+ if (length == 1) {
+ if (MEM_SUPPORT_64BIT_DATA && size == 8) {
+ llp = (ulong *)buf;
+ for (;;)
+ *llp = data;
+ }
+ if (size == 4) {
+ longp = (u32 *)buf;
+ for (;;)
+ *longp = data;
+ }
+ if (size == 2) {
+ shortp = (u16 *)buf;
+ for (;;)
+ *shortp = data;
+ }
+ cp = (u8 *)buf;
+ for (;;)
+ *cp = data;
+ }
+
+ if (MEM_SUPPORT_64BIT_DATA && size == 8) {
+ for (;;) {
+ llp = (ulong *)buf;
+ i = length;
+ while (i-- > 0)
+ *llp++ = data;
+ }
+ }
+ if (size == 4) {
+ for (;;) {
+ longp = (u32 *)buf;
+ i = length;
+ while (i-- > 0)
+ *longp++ = data;
+ }
+ }
+ if (size == 2) {
+ for (;;) {
+ shortp = (u16 *)buf;
+ i = length;
+ while (i-- > 0)
+ *shortp++ = data;
+ }
+ }
+ for (;;) {
+ cp = (u8 *)buf;
+ i = length;
+ while (i-- > 0)
+ *cp++ = data;
+ }
+}
+#endif /* CONFIG_LOOPW */
+
+#ifdef CONFIG_CMD_MEMTEST
+static ulong mem_test_alt(volatile ulong *buf, ulong start_addr, ulong end_addr,
+ volatile ulong *dummy)
+{
+ volatile ulong *addr;
+ ulong errs = 0;
+ ulong val, readback;
+ int j;
+ ulong offset, test_offset;
+ ulong pattern, anti_pattern;
+ ulong temp, num_words;
+ static const ulong bitpattern[] = {
+ 0x00000001, /* single bit */
+ 0x00000003, /* two adjacent bits */
+ 0x00000007, /* three adjacent bits */
+ 0x0000000F, /* four adjacent bits */
+ 0x00000005, /* two non-adjacent bits */
+ 0x00000015, /* three non-adjacent bits */
+ 0x00000055, /* four non-adjacent bits */
+ 0xaaaaaaaa, /* alternating 1/0 */
+ };
+ /* Rate-limit schedule() calls to one for every 256 words. */
+ u8 count = 0;
+
+ num_words = (end_addr - start_addr) / sizeof(ulong);
+
+ /*
+ * Data line test: write a pattern to the first
+ * location, write the 1's complement to a 'parking'
+ * address (changes the state of the data bus so a
+ * floating bus doesn't give a false OK), and then
+ * read the value back. Note that we read it back
+ * into a variable because the next time we read it,
+ * it might be right (been there, tough to explain to
+ * the quality guys why it prints a failure when the
+ * "is" and "should be" are obviously the same in the
+ * error message).
+ *
+ * Rather than exhaustively testing, we test some
+ * patterns by shifting '1' bits through a field of
+ * '0's and '0' bits through a field of '1's (i.e.
+ * pattern and ~pattern).
+ */
+ addr = buf;
+ for (j = 0; j < sizeof(bitpattern) / sizeof(bitpattern[0]); j++) {
+ val = bitpattern[j];
+ for (; val != 0; val <<= 1) {
+ *addr = val;
+ *dummy = ~val; /* clear the test data off the bus */
+ readback = *addr;
+ if (readback != val) {
+ printf("FAILURE (data line): "
+ "expected %08lx, actual %08lx\n",
+ val, readback);
+ errs++;
+ if (ctrlc())
+ return -1;
+ }
+ *addr = ~val;
+ *dummy = val;
+ readback = *addr;
+ if (readback != ~val) {
+ printf("FAILURE (data line): "
+ "Is %08lx, should be %08lx\n",
+ readback, ~val);
+ errs++;
+ if (ctrlc())
+ return -1;
+ }
+ }
+ }
+
+ /*
+ * Based on code whose Original Author and Copyright
+ * information follows: Copyright (c) 1998 by Michael
+ * Barr. This software is placed into the public
+ * domain and may be used for any purpose. However,
+ * this notice must not be changed or removed and no
+ * warranty is either expressed or implied by its
+ * publication or distribution.
+ */
+
+ /*
+ * Address line test
+
+ * Description: Test the address bus wiring in a
+ * memory region by performing a walking
+ * 1's test on the relevant bits of the
+ * address and checking for aliasing.
+ * This test will find single-bit
+ * address failures such as stuck-high,
+ * stuck-low, and shorted pins. The base
+ * address and size of the region are
+ * selected by the caller.
+
+ * Notes: For best results, the selected base
+ * address should have enough LSB 0's to
+ * guarantee single address bit changes.
+ * For example, to test a 64-Kbyte
+ * region, select a base address on a
+ * 64-Kbyte boundary. Also, select the
+ * region size as a power-of-two if at
+ * all possible.
+ *
+ * Returns: 0 if the test succeeds, 1 if the test fails.
+ */
+ pattern = (ulong)0xaaaaaaaaaaaaaaaa;
+ anti_pattern = (ulong)0x5555555555555555;
+
+ debug("%s:%d: length = 0x%.8lx\n", __func__, __LINE__, num_words);
+ /*
+ * Write the default pattern at each of the
+ * power-of-two offsets.
+ */
+ for (offset = 1; offset < num_words; offset <<= 1)
+ addr[offset] = pattern;
+
+ /*
+ * Check for address bits stuck high.
+ */
+ test_offset = 0;
+ addr[test_offset] = anti_pattern;
+
+ for (offset = 1; offset < num_words; offset <<= 1) {
+ temp = addr[offset];
+ if (temp != pattern) {
+ printf("\nFAILURE: Address bit stuck high @ 0x%.8lx:"
+ " expected 0x%.8lx, actual 0x%.8lx\n",
+ start_addr + offset*sizeof(ulong),
+ pattern, temp);
+ errs++;
+ if (ctrlc())
+ return -1;
+ }
+ }
+ addr[test_offset] = pattern;
+ schedule();
+
+ /*
+ * Check for addr bits stuck low or shorted.
+ */
+ for (test_offset = 1; test_offset < num_words; test_offset <<= 1) {
+ addr[test_offset] = anti_pattern;
+
+ for (offset = 1; offset < num_words; offset <<= 1) {
+ temp = addr[offset];
+ if ((temp != pattern) && (offset != test_offset)) {
+ printf("\nFAILURE: Address bit stuck low or"
+ " shorted @ 0x%.8lx: expected 0x%.8lx,"
+ " actual 0x%.8lx\n",
+ start_addr + offset*sizeof(ulong),
+ pattern, temp);
+ errs++;
+ if (ctrlc())
+ return -1;
+ }
+ }
+ addr[test_offset] = pattern;
+ }
+
+ /*
+ * Description: Test the integrity of a physical
+ * memory device by performing an
+ * increment/decrement test over the
+ * entire region. In the process every
+ * storage bit in the device is tested
+ * as a zero and a one. The base address
+ * and the size of the region are
+ * selected by the caller.
+ *
+ * Returns: 0 if the test succeeds, 1 if the test fails.
+ */
+ num_words++;
+
+ /*
+ * Fill memory with a known pattern.
+ */
+ for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
+ if (!count++)
+ schedule();
+ addr[offset] = pattern;
+ }
+
+ /*
+ * Check each location and invert it for the second pass.
+ */
+ for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
+ if (!count++)
+ schedule();
+ temp = addr[offset];
+ if (temp != pattern) {
+ printf("\nFAILURE (read/write) @ 0x%.8lx:"
+ " expected 0x%.8lx, actual 0x%.8lx)\n",
+ start_addr + offset*sizeof(ulong),
+ pattern, temp);
+ errs++;
+ if (ctrlc())
+ return -1;
+ }
+
+ anti_pattern = ~pattern;
+ addr[offset] = anti_pattern;
+ }
+
+ /*
+ * Check each location for the inverted pattern and zero it.
+ */
+ for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
+ if (!count++)
+ schedule();
+ anti_pattern = ~pattern;
+ temp = addr[offset];
+ if (temp != anti_pattern) {
+ printf("\nFAILURE (read/write): @ 0x%.8lx:"
+ " expected 0x%.8lx, actual 0x%.8lx)\n",
+ start_addr + offset*sizeof(ulong),
+ anti_pattern, temp);
+ errs++;
+ if (ctrlc())
+ return -1;
+ }
+ addr[offset] = 0;
+ }
+
+ return errs;
+}
+
+static int compare_regions(volatile unsigned long *bufa,
+ volatile unsigned long *bufb, size_t count)
+{
+ volatile unsigned long *p1 = bufa;
+ volatile unsigned long *p2 = bufb;
+ int errs = 0;
+ size_t i;
+
+ for (i = 0; i < count; i++, p1++, p2++) {
+ if (*p1 != *p2) {
+ printf("FAILURE: 0x%08lx != 0x%08lx (delta=0x%08lx -> bit %ld) at offset 0x%08lx\n",
+ (unsigned long)*p1, (unsigned long)*p2,
+ *p1 ^ *p2, __ffs(*p1 ^ *p2),
+ (unsigned long)(i * sizeof(unsigned long)));
+ errs++;
+ }
+ }
+
+ return errs;
+}
+
+static ulong test_bitflip_comparison(volatile unsigned long *bufa,
+ volatile unsigned long *bufb, size_t count)
+{
+ volatile unsigned long *p1 = bufa;
+ volatile unsigned long *p2 = bufb;
+ unsigned int j, k;
+ unsigned long q;
+ size_t i;
+ int max;
+ int errs = 0;
+
+ max = sizeof(unsigned long) * 8;
+ for (k = 0; k < max; k++) {
+ q = 1UL << k;
+ for (j = 0; j < 8; j++) {
+ schedule();
+ q = ~q;
+ p1 = (volatile unsigned long *)bufa;
+ p2 = (volatile unsigned long *)bufb;
+ for (i = 0; i < count; i++)
+ *p1++ = *p2++ = (i % 2) == 0 ? q : ~q;
+
+ errs += compare_regions(bufa, bufb, count);
+ }
+
+ if (ctrlc())
+ return -1UL;
+ }
+
+ return errs;
+}
+
+static ulong mem_test_bitflip(vu_long *buf, ulong start, ulong end)
+{
+ /*
+ * Split the specified range into two halves.
+ * Note that mtest range is inclusive of start,end.
+ * Bitflip test instead uses a count (of 32-bit words).
+ */
+ ulong half_size = (end - start + 1) / 2 / sizeof(unsigned long);
+
+ return test_bitflip_comparison(buf, buf + half_size, half_size);
+}
+
+static ulong mem_test_quick(vu_long *buf, ulong start_addr, ulong end_addr,
+ vu_long pattern, int iteration)
+{
+ vu_long *end;
+ vu_long *addr;
+ ulong errs = 0;
+ ulong incr, length;
+ ulong val, readback;
+ const int plen = 2 * sizeof(ulong);
+
+ /* Alternate the pattern */
+ incr = 1;
+ if (iteration & 1) {
+ incr = -incr;
+ /*
+ * Flip the pattern each time to make lots of zeros and
+ * then, the next time, lots of ones. We decrement
+ * the "negative" patterns and increment the "positive"
+ * patterns to preserve this feature.
+ */
+ if (pattern > (ulong)LONG_MAX)
+ pattern = -pattern; /* complement & increment */
+ else
+ pattern = ~pattern;
+ }
+ length = (end_addr - start_addr) / sizeof(ulong);
+ end = buf + length;
+ printf("\rPattern %0*lX Writing..."
+ "%12s"
+ "\b\b\b\b\b\b\b\b\b\b",
+ plen, pattern, "");
+
+ for (addr = buf, val = pattern; addr < end; addr++) {
+ schedule();
+ *addr = val;
+ val += incr;
+ }
+
+ puts("Reading...");
+
+ for (addr = buf, val = pattern; addr < end; addr++) {
+ schedule();
+ readback = *addr;
+ if (readback != val) {
+ ulong offset = addr - buf;
+
+ printf("\nMem error @ 0x%0*lX: found %0*lX, expected %0*lX\n",
+ plen, start_addr + offset * sizeof(vu_long),
+ plen, readback, plen, val);
+ errs++;
+ if (ctrlc())
+ return -1;
+ }
+ val += incr;
+ }
+
+ return errs;
+}
+
+/*
+ * Perform a memory test. A more complete alternative test can be
+ * configured using CONFIG_SYS_ALT_MEMTEST. The complete test loops until
+ * interrupted by ctrl-c or by a failure of one of the sub-tests.
+ */
+static int do_mem_mtest(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong start, end;
+ vu_long scratch_space;
+ vu_long *buf, *dummy = &scratch_space;
+ ulong iteration_limit = 0;
+ ulong count = 0;
+ ulong errs = 0; /* number of errors, or -1 if interrupted */
+ ulong pattern = 0;
+ int iteration;
+
+ start = CONFIG_SYS_MEMTEST_START;
+ end = CONFIG_SYS_MEMTEST_END;
+
+ if (argc > 1)
+ if (strict_strtoul(argv[1], 16, &start) < 0)
+ return CMD_RET_USAGE;
+
+ if (argc > 2)
+ if (strict_strtoul(argv[2], 16, &end) < 0)
+ return CMD_RET_USAGE;
+
+ if (argc > 3)
+ if (strict_strtoul(argv[3], 16, &pattern) < 0)
+ return CMD_RET_USAGE;
+
+ if (argc > 4)
+ if (strict_strtoul(argv[4], 16, &iteration_limit) < 0)
+ return CMD_RET_USAGE;
+
+ if (end < start) {
+ printf("Refusing to do empty test\n");
+ return -1;
+ }
+
+ printf("Testing %08lx ... %08lx:\n", start, end);
+ debug("%s:%d: start %#08lx end %#08lx\n", __func__, __LINE__,
+ start, end);
+
+ buf = map_sysmem(start, end - start);
+ for (iteration = 0;
+ !iteration_limit || iteration < iteration_limit;
+ iteration++) {
+ if (ctrlc()) {
+ errs = -1UL;
+ break;
+ }
+
+ printf("Iteration: %6d\r", iteration + 1);
+ debug("\n");
+ if (IS_ENABLED(CONFIG_SYS_ALT_MEMTEST)) {
+ errs = mem_test_alt(buf, start, end, dummy);
+ if (errs == -1UL)
+ break;
+ if (IS_ENABLED(CONFIG_SYS_ALT_MEMTEST_BITFLIP)) {
+ count += errs;
+ errs = mem_test_bitflip(buf, start, end);
+ }
+ } else {
+ errs = mem_test_quick(buf, start, end, pattern,
+ iteration);
+ }
+ if (errs == -1UL)
+ break;
+ count += errs;
+ }
+
+ unmap_sysmem((void *)buf);
+
+ printf("\nTested %d iteration(s) with %lu errors.\n", iteration, count);
+
+ return errs != 0;
+}
+#endif /* CONFIG_CMD_MEMTEST */
+
+/* Modify memory.
+ *
+ * Syntax:
+ * mm{.b, .w, .l, .q} {addr}
+ */
+static int
+mod_mem(struct cmd_tbl *cmdtp, int incrflag, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr;
+ ulong i; /* 64-bit if MEM_SUPPORT_64BIT_DATA */
+ int nbytes, size;
+ void *ptr = NULL;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ bootretry_reset_cmd_timeout(); /* got a good command to get here */
+ /* We use the last specified parameters, unless new ones are
+ * entered.
+ */
+ addr = mm_last_addr;
+ size = mm_last_size;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ /* New command specified. Check for a size specification.
+ * Defaults to long if no or incorrect specification.
+ */
+ if ((size = cmd_get_data_size(argv[0], 4)) < 0)
+ return 1;
+
+ /* Address is specified since argc > 1
+ */
+ addr = hextoul(argv[1], NULL);
+ addr += base_address;
+ }
+
+ /* Print the address, followed by value. Then accept input for
+ * the next value. A non-converted value exits.
+ */
+ do {
+ ptr = map_sysmem(addr, size);
+ printf("%08lx:", addr);
+ if (size == 4)
+ printf(" %08x", *((u32 *)ptr));
+ else if (MEM_SUPPORT_64BIT_DATA && size == 8)
+ printf(" %0lx", *((ulong *)ptr));
+ else if (size == 2)
+ printf(" %04x", *((u16 *)ptr));
+ else
+ printf(" %02x", *((u8 *)ptr));
+
+ nbytes = cli_readline(" ? ");
+ if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) {
+ /* <CR> pressed as only input, don't modify current
+ * location and move to next. "-" pressed will go back.
+ */
+ if (incrflag)
+ addr += nbytes ? -size : size;
+ nbytes = 1;
+ /* good enough to not time out */
+ bootretry_reset_cmd_timeout();
+ }
+#ifdef CONFIG_BOOT_RETRY_TIME
+ else if (nbytes == -2) {
+ break; /* timed out, exit the command */
+ }
+#endif
+ else {
+ char *endp;
+ if (MEM_SUPPORT_64BIT_DATA)
+ i = simple_strtoull(console_buffer, &endp, 16);
+ else
+ i = hextoul(console_buffer, &endp);
+ nbytes = endp - console_buffer;
+ if (nbytes) {
+ /* good enough to not time out
+ */
+ bootretry_reset_cmd_timeout();
+ if (size == 4)
+ *((u32 *)ptr) = i;
+ else if (MEM_SUPPORT_64BIT_DATA && size == 8)
+ *((ulong *)ptr) = i;
+ else if (size == 2)
+ *((u16 *)ptr) = i;
+ else
+ *((u8 *)ptr) = i;
+ if (incrflag)
+ addr += size;
+ }
+ }
+ } while (nbytes);
+ if (ptr)
+ unmap_sysmem(ptr);
+
+ mm_last_addr = addr;
+ mm_last_size = size;
+ return 0;
+}
+
+#ifdef CONFIG_CMD_CRC32
+
+static int do_mem_crc(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int flags = 0;
+ int ac;
+ char * const *av;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ av = argv + 1;
+ ac = argc - 1;
+#ifdef CONFIG_CRC32_VERIFY
+ if (strcmp(*av, "-v") == 0) {
+ flags |= HASH_FLAG_VERIFY | HASH_FLAG_ENV;
+ av++;
+ ac--;
+ }
+#endif
+
+ return hash_command("crc32", flags, cmdtp, flag, ac, av);
+}
+
+#endif
+
+#ifdef CONFIG_CMD_RANDOM
+static int do_random(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ unsigned long addr, len;
+ unsigned long seed; // NOT INITIALIZED ON PURPOSE
+ unsigned int *buf, *start;
+ unsigned char *buf8;
+ unsigned int i;
+
+ if (argc < 3 || argc > 4)
+ return CMD_RET_USAGE;
+
+ len = hextoul(argv[2], NULL);
+ addr = hextoul(argv[1], NULL);
+
+ if (argc == 4) {
+ seed = hextoul(argv[3], NULL);
+ if (seed == 0) {
+ printf("The seed cannot be 0. Using 0xDEADBEEF.\n");
+ seed = 0xDEADBEEF;
+ }
+ } else {
+ seed = get_timer(0) ^ rand();
+ }
+
+ srand(seed);
+ start = map_sysmem(addr, len);
+ buf = start;
+ for (i = 0; i < (len / 4); i++)
+ *buf++ = rand();
+
+ buf8 = (unsigned char *)buf;
+ for (i = 0; i < (len % 4); i++)
+ *buf8++ = rand() & 0xFF;
+
+ unmap_sysmem(start);
+ printf("%lu bytes filled with random data\n", len);
+
+ return CMD_RET_SUCCESS;
+}
+#endif
+
+/**************************************************/
+U_BOOT_CMD(
+ md, 3, 1, do_mem_md,
+ "memory display",
+ "[.b, .w, .l" HELP_Q "] address [# of objects]"
+);
+
+U_BOOT_CMD(
+ mm, 2, 1, do_mem_mm,
+ "memory modify (auto-incrementing address)",
+ "[.b, .w, .l" HELP_Q "] address"
+);
+
+U_BOOT_CMD(
+ nm, 2, 1, do_mem_nm,
+ "memory modify (constant address)",
+ "[.b, .w, .l" HELP_Q "] address"
+);
+
+U_BOOT_CMD(
+ mw, 4, 1, do_mem_mw,
+ "memory write (fill)",
+ "[.b, .w, .l" HELP_Q "] address value [count]"
+);
+
+U_BOOT_CMD(
+ cp, 4, 1, do_mem_cp,
+ "memory copy",
+ "[.b, .w, .l" HELP_Q "] source target count"
+);
+
+U_BOOT_CMD(
+ cmp, 4, 1, do_mem_cmp,
+ "memory compare",
+ "[.b, .w, .l" HELP_Q "] addr1 addr2 count"
+);
+
+#ifdef CONFIG_CMD_MEM_SEARCH
+/**************************************************/
+U_BOOT_CMD(
+ ms, 255, 1, do_mem_search,
+ "memory search",
+ "[.b, .w, .l" HELP_Q ", .s] [-q | -<n>] address #-of-objects <value>..."
+ " -q = quiet, -l<val> = match limit"
+);
+#endif
+
+#ifdef CONFIG_CMD_CRC32
+
+#ifndef CONFIG_CRC32_VERIFY
+
+U_BOOT_CMD(
+ crc32, 4, 1, do_mem_crc,
+ "checksum calculation",
+ "address count [addr]\n - compute CRC32 checksum [save at addr]"
+);
+
+#else /* CONFIG_CRC32_VERIFY */
+
+U_BOOT_CMD(
+ crc32, 5, 1, do_mem_crc,
+ "checksum calculation",
+ "address count [addr]\n - compute CRC32 checksum [save at addr]\n"
+ "-v address count crc\n - verify crc of memory area"
+);
+
+#endif /* CONFIG_CRC32_VERIFY */
+
+#endif
+
+U_BOOT_CMD(
+ base, 2, 1, do_mem_base,
+ "print or set address offset",
+ "\n - print address offset for memory commands\n"
+ "base off\n - set address offset for memory commands to 'off'"
+);
+
+U_BOOT_CMD(
+ loop, 3, 1, do_mem_loop,
+ "infinite loop on address range",
+ "[.b, .w, .l" HELP_Q "] address number_of_objects"
+);
+
+#ifdef CONFIG_LOOPW
+U_BOOT_CMD(
+ loopw, 4, 1, do_mem_loopw,
+ "infinite write loop on address range",
+ "[.b, .w, .l" HELP_Q "] address number_of_objects data_to_write"
+);
+#endif /* CONFIG_LOOPW */
+
+#ifdef CONFIG_CMD_MEMTEST
+U_BOOT_CMD(
+ mtest, 5, 1, do_mem_mtest,
+ "simple RAM read/write test",
+ "[start [end [pattern [iterations]]]]"
+);
+#endif /* CONFIG_CMD_MEMTEST */
+
+#ifdef CONFIG_CMD_MX_CYCLIC
+U_BOOT_CMD(
+ mdc, 4, 1, do_mem_mdc,
+ "memory display cyclic",
+ "[.b, .w, .l" HELP_Q "] address count delay(ms)"
+);
+
+U_BOOT_CMD(
+ mwc, 4, 1, do_mem_mwc,
+ "memory write cyclic",
+ "[.b, .w, .l" HELP_Q "] address value delay(ms)"
+);
+#endif /* CONFIG_CMD_MX_CYCLIC */
+
+#ifdef CONFIG_CMD_RANDOM
+U_BOOT_CMD(
+ random, 4, 0, do_random,
+ "fill memory with random pattern",
+ "<addr> <len> [<seed>]\n"
+ " - Fill 'len' bytes of memory starting at 'addr' with random data\n"
+);
+#endif
diff --git a/cmd/meminfo.c b/cmd/meminfo.c
new file mode 100644
index 00000000000..aa3b5bafe17
--- /dev/null
+++ b/cmd/meminfo.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2024 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <bloblist.h>
+#include <bootstage.h>
+#include <command.h>
+#include <display_options.h>
+#include <lmb.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void __weak arch_dump_mem_attrs(void)
+{
+}
+
+static void print_region(const char *name, ulong base, ulong size, ulong *uptop)
+{
+ ulong end = base + size;
+
+ printf("%-12s %8lx %8lx %8lx", name, base, size, end);
+ if (*uptop)
+ printf(" %8lx", *uptop - end);
+ putc('\n');
+ *uptop = base;
+}
+
+static void show_lmb(const struct lmb *lmb, ulong *uptop)
+{
+ int i;
+
+ for (i = lmb->used_mem.count - 1; i >= 0; i--) {
+ const struct lmb_region *rgn = alist_get(&lmb->used_mem, i,
+ struct lmb_region);
+
+ /*
+ * Assume that the top lmb region is the U-Boot region, so just
+ * take account of the memory not already reported
+ */
+ if (lmb->used_mem.count - 1)
+ print_region("lmb", rgn->base, *uptop - rgn->base,
+ uptop);
+ else
+ print_region("lmb", rgn->base, rgn->size, uptop);
+ *uptop = rgn->base;
+ }
+}
+
+static int do_meminfo(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong upto, stk_bot;
+
+ puts("DRAM: ");
+ print_size(gd->ram_size, "\n");
+
+ if (!IS_ENABLED(CONFIG_CMD_MEMINFO_MAP))
+ return 0;
+
+ arch_dump_mem_attrs();
+
+ printf("\n%-12s %8s %8s %8s %8s\n", "Region", "Base", "Size", "End",
+ "Gap");
+ printf("------------------------------------------------\n");
+ upto = 0;
+ if (IS_ENABLED(CONFIG_VIDEO))
+ print_region("video", gd_video_bottom(),
+ gd_video_size(), &upto);
+ if (IS_ENABLED(CONFIG_TRACE))
+ print_region("trace", map_to_sysmem(gd_trace_buff()),
+ gd_trace_size(), &upto);
+ print_region("code", gd->relocaddr, gd->mon_len, &upto);
+ print_region("malloc", map_to_sysmem((void *)mem_malloc_start),
+ mem_malloc_end - mem_malloc_start, &upto);
+ print_region("board_info", map_to_sysmem(gd->bd),
+ sizeof(struct bd_info), &upto);
+ print_region("global_data", map_to_sysmem((void *)gd),
+ sizeof(struct global_data), &upto);
+ print_region("devicetree", map_to_sysmem(gd->fdt_blob),
+ fdt_totalsize(gd->fdt_blob), &upto);
+ if (IS_ENABLED(CONFIG_BOOTSTAGE))
+ print_region("bootstage", map_to_sysmem(gd_bootstage()),
+ bootstage_get_size(false), &upto);
+ if (IS_ENABLED(CONFIG_BLOBLIST))
+ print_region("bloblist", map_to_sysmem(gd_bloblist()),
+ bloblist_get_total_size(), &upto);
+ stk_bot = gd->start_addr_sp - CONFIG_STACK_SIZE;
+ print_region("stack", stk_bot, CONFIG_STACK_SIZE, &upto);
+ if (IS_ENABLED(CONFIG_LMB))
+ show_lmb(lmb_get(), &upto);
+ print_region("free", gd->ram_base, upto - gd->ram_base, &upto);
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ meminfo, 1, 1, do_meminfo,
+ "display memory information",
+ ""
+);
diff --git a/cmd/meson/Makefile b/cmd/meson/Makefile
new file mode 100644
index 00000000000..ee26c175cfe
--- /dev/null
+++ b/cmd/meson/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2022, SberDevices. All rights reserved.
+
+obj-y += sm.o
diff --git a/cmd/meson/sm.c b/cmd/meson/sm.c
new file mode 100644
index 00000000000..b69f8123ee2
--- /dev/null
+++ b/cmd/meson/sm.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2016 Beniamino Galvani
+ *
+ * Author: Beniamino Galvani <b.galvani@gmail.com>
+ * Author: Vyacheslav Bocharov <adeep@lexina.in>
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ * Author: Alexey Romanov <avromanov@sberdevices.ru>
+ */
+
+#include <command.h>
+#include <env.h>
+#include <asm/arch/sm.h>
+#include <stdlib.h>
+#include <display_options.h>
+#include <vsprintf.h>
+
+static int do_sm_serial(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong address;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ address = simple_strtoul(argv[1], NULL, 0);
+
+ ret = meson_sm_get_serial((void *)address, SM_SERIAL_SIZE);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+#define MAX_REBOOT_REASONS 14
+
+static const char *reboot_reasons[MAX_REBOOT_REASONS] = {
+ [REBOOT_REASON_COLD] = "cold_boot",
+ [REBOOT_REASON_NORMAL] = "normal",
+ [REBOOT_REASON_RECOVERY] = "recovery",
+ [REBOOT_REASON_UPDATE] = "update",
+ [REBOOT_REASON_FASTBOOT] = "fastboot",
+ [REBOOT_REASON_SUSPEND_OFF] = "suspend_off",
+ [REBOOT_REASON_HIBERNATE] = "hibernate",
+ [REBOOT_REASON_BOOTLOADER] = "bootloader",
+ [REBOOT_REASON_SHUTDOWN_REBOOT] = "shutdown_reboot",
+ [REBOOT_REASON_RPMBP] = "rpmbp",
+ [REBOOT_REASON_CRASH_DUMP] = "crash_dump",
+ [REBOOT_REASON_KERNEL_PANIC] = "kernel_panic",
+ [REBOOT_REASON_WATCHDOG_REBOOT] = "watchdog_reboot",
+};
+
+static int do_sm_reboot_reason(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *reason_str;
+ char *destarg = NULL;
+ int reason;
+
+ if (argc > 1)
+ destarg = argv[1];
+
+ reason = meson_sm_get_reboot_reason();
+ if (reason < 0)
+ return CMD_RET_FAILURE;
+
+ if (reason >= MAX_REBOOT_REASONS ||
+ !reboot_reasons[reason])
+ reason_str = "unknown";
+ else
+ reason_str = reboot_reasons[reason];
+
+ if (destarg)
+ env_set(destarg, reason_str);
+ else
+ printf("reboot reason: %s (%x)\n", reason_str, reason);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_efuse_read(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong address, offset, size;
+ int ret;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ offset = simple_strtoul(argv[1], NULL, 0);
+ size = simple_strtoul(argv[2], NULL, 0);
+
+ address = simple_strtoul(argv[3], NULL, 0);
+
+ ret = meson_sm_read_efuse(offset, (void *)address, size);
+ if (ret != size)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_efuse_write(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong address, offset, size;
+ int ret;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ offset = simple_strtoul(argv[1], NULL, 0);
+ size = simple_strtoul(argv[2], NULL, 0);
+
+ address = simple_strtoul(argv[3], NULL, 0);
+
+ ret = meson_sm_write_efuse(offset, (void *)address, size);
+ if (ret != size)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_efuse_dump(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong offset, size;
+ u8 *buffer;
+ int ret;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ offset = simple_strtoul(argv[1], NULL, 0);
+ size = simple_strtoul(argv[2], NULL, 0);
+ buffer = malloc(size);
+ if (!buffer) {
+ pr_err("Failed to allocate %lu bytes\n", size);
+ return CMD_RET_FAILURE;
+ }
+
+ ret = meson_sm_read_efuse(offset, (void *)buffer, size);
+ if (ret != size) {
+ ret = CMD_RET_FAILURE;
+ goto free_buffer;
+ }
+
+ print_buffer(0, buffer, 1, size, 0);
+
+free_buffer:
+ free(buffer);
+ return ret;
+}
+
+static struct cmd_tbl cmd_sm_sub[] = {
+ U_BOOT_CMD_MKENT(serial, 2, 1, do_sm_serial, "", ""),
+ U_BOOT_CMD_MKENT(reboot_reason, 1, 1, do_sm_reboot_reason, "", ""),
+ U_BOOT_CMD_MKENT(efuseread, 4, 1, do_efuse_read, "", ""),
+ U_BOOT_CMD_MKENT(efusewrite, 4, 0, do_efuse_write, "", ""),
+ U_BOOT_CMD_MKENT(efusedump, 3, 1, do_efuse_dump, "", ""),
+};
+
+static int do_sm(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *c;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* Strip off leading 'sm' command argument */
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], &cmd_sm_sub[0], ARRAY_SIZE(cmd_sm_sub));
+
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+ else
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+ sm, 5, 0, do_sm,
+ "Secure Monitor Control",
+ "serial <address> - read chip unique id to memory address\n"
+ "sm reboot_reason [name] - get reboot reason and store to environment\n"
+ "sm efuseread <offset> <size> <address> - read efuse to memory address\n"
+ "sm efusewrite <offset> <size> <address> - write into efuse from memory address\n"
+ "sm efusedump <offset> <size> - dump efuse data range to console"
+);
diff --git a/cmd/mii.c b/cmd/mii.c
new file mode 100644
index 00000000000..ce372489692
--- /dev/null
+++ b/cmd/mii.c
@@ -0,0 +1,478 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2001
+ * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
+ */
+
+/*
+ * MII Utilities
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <miiphy.h>
+
+typedef struct _MII_field_desc_t {
+ ushort hi;
+ ushort lo;
+ ushort mask;
+ const char *name;
+} MII_field_desc_t;
+
+static const MII_field_desc_t reg_0_desc_tbl[] = {
+ { 15, 15, 0x01, "reset" },
+ { 14, 14, 0x01, "loopback" },
+ { 13, 6, 0x81, "speed selection" }, /* special */
+ { 12, 12, 0x01, "A/N enable" },
+ { 11, 11, 0x01, "power-down" },
+ { 10, 10, 0x01, "isolate" },
+ { 9, 9, 0x01, "restart A/N" },
+ { 8, 8, 0x01, "duplex" }, /* special */
+ { 7, 7, 0x01, "collision test enable" },
+ { 5, 0, 0x3f, "(reserved)" }
+};
+
+static const MII_field_desc_t reg_1_desc_tbl[] = {
+ { 15, 15, 0x01, "100BASE-T4 able" },
+ { 14, 14, 0x01, "100BASE-X full duplex able" },
+ { 13, 13, 0x01, "100BASE-X half duplex able" },
+ { 12, 12, 0x01, "10 Mbps full duplex able" },
+ { 11, 11, 0x01, "10 Mbps half duplex able" },
+ { 10, 10, 0x01, "100BASE-T2 full duplex able" },
+ { 9, 9, 0x01, "100BASE-T2 half duplex able" },
+ { 8, 8, 0x01, "extended status" },
+ { 7, 7, 0x01, "(reserved)" },
+ { 6, 6, 0x01, "MF preamble suppression" },
+ { 5, 5, 0x01, "A/N complete" },
+ { 4, 4, 0x01, "remote fault" },
+ { 3, 3, 0x01, "A/N able" },
+ { 2, 2, 0x01, "link status" },
+ { 1, 1, 0x01, "jabber detect" },
+ { 0, 0, 0x01, "extended capabilities" },
+};
+
+static const MII_field_desc_t reg_2_desc_tbl[] = {
+ { 15, 0, 0xffff, "OUI portion" },
+};
+
+static const MII_field_desc_t reg_3_desc_tbl[] = {
+ { 15, 10, 0x3f, "OUI portion" },
+ { 9, 4, 0x3f, "manufacturer part number" },
+ { 3, 0, 0x0f, "manufacturer rev. number" },
+};
+
+static const MII_field_desc_t reg_4_desc_tbl[] = {
+ { 15, 15, 0x01, "next page able" },
+ { 14, 14, 0x01, "(reserved)" },
+ { 13, 13, 0x01, "remote fault" },
+ { 12, 12, 0x01, "(reserved)" },
+ { 11, 11, 0x01, "asymmetric pause" },
+ { 10, 10, 0x01, "pause enable" },
+ { 9, 9, 0x01, "100BASE-T4 able" },
+ { 8, 8, 0x01, "100BASE-TX full duplex able" },
+ { 7, 7, 0x01, "100BASE-TX able" },
+ { 6, 6, 0x01, "10BASE-T full duplex able" },
+ { 5, 5, 0x01, "10BASE-T able" },
+ { 4, 0, 0x1f, "selector" },
+};
+
+static const MII_field_desc_t reg_5_desc_tbl[] = {
+ { 15, 15, 0x01, "next page able" },
+ { 14, 14, 0x01, "acknowledge" },
+ { 13, 13, 0x01, "remote fault" },
+ { 12, 12, 0x01, "(reserved)" },
+ { 11, 11, 0x01, "asymmetric pause able" },
+ { 10, 10, 0x01, "pause able" },
+ { 9, 9, 0x01, "100BASE-T4 able" },
+ { 8, 8, 0x01, "100BASE-X full duplex able" },
+ { 7, 7, 0x01, "100BASE-TX able" },
+ { 6, 6, 0x01, "10BASE-T full duplex able" },
+ { 5, 5, 0x01, "10BASE-T able" },
+ { 4, 0, 0x1f, "partner selector" },
+};
+
+static const MII_field_desc_t reg_9_desc_tbl[] = {
+ { 15, 13, 0x07, "test mode" },
+ { 12, 12, 0x01, "manual master/slave enable" },
+ { 11, 11, 0x01, "manual master/slave value" },
+ { 10, 10, 0x01, "multi/single port" },
+ { 9, 9, 0x01, "1000BASE-T full duplex able" },
+ { 8, 8, 0x01, "1000BASE-T half duplex able" },
+ { 7, 7, 0x01, "automatic TDR on link down" },
+ { 6, 6, 0x7f, "(reserved)" },
+};
+
+static const MII_field_desc_t reg_10_desc_tbl[] = {
+ { 15, 15, 0x01, "master/slave config fault" },
+ { 14, 14, 0x01, "master/slave config result" },
+ { 13, 13, 0x01, "local receiver status OK" },
+ { 12, 12, 0x01, "remote receiver status OK" },
+ { 11, 11, 0x01, "1000BASE-T full duplex able" },
+ { 10, 10, 0x01, "1000BASE-T half duplex able" },
+ { 9, 8, 0x03, "(reserved)" },
+ { 7, 0, 0xff, "1000BASE-T idle error counter"},
+};
+
+typedef struct _MII_reg_desc_t {
+ ushort regno;
+ const MII_field_desc_t *pdesc;
+ ushort len;
+ const char *name;
+} MII_reg_desc_t;
+
+static const MII_reg_desc_t mii_reg_desc_tbl[] = {
+ { MII_BMCR, reg_0_desc_tbl, ARRAY_SIZE(reg_0_desc_tbl),
+ "PHY control register" },
+ { MII_BMSR, reg_1_desc_tbl, ARRAY_SIZE(reg_1_desc_tbl),
+ "PHY status register" },
+ { MII_PHYSID1, reg_2_desc_tbl, ARRAY_SIZE(reg_2_desc_tbl),
+ "PHY ID 1 register" },
+ { MII_PHYSID2, reg_3_desc_tbl, ARRAY_SIZE(reg_3_desc_tbl),
+ "PHY ID 2 register" },
+ { MII_ADVERTISE, reg_4_desc_tbl, ARRAY_SIZE(reg_4_desc_tbl),
+ "Autonegotiation advertisement register" },
+ { MII_LPA, reg_5_desc_tbl, ARRAY_SIZE(reg_5_desc_tbl),
+ "Autonegotiation partner abilities register" },
+ { MII_CTRL1000, reg_9_desc_tbl, ARRAY_SIZE(reg_9_desc_tbl),
+ "1000BASE-T control register" },
+ { MII_STAT1000, reg_10_desc_tbl, ARRAY_SIZE(reg_10_desc_tbl),
+ "1000BASE-T status register" },
+};
+
+static void dump_reg(
+ ushort regval,
+ const MII_reg_desc_t *prd);
+
+static bool special_field(ushort regno, const MII_field_desc_t *pdesc,
+ ushort regval);
+
+static void MII_dump(const ushort *regvals, uchar reglo, uchar reghi)
+{
+ ulong i;
+
+ for (i = 0; i < ARRAY_SIZE(mii_reg_desc_tbl); i++) {
+ const uchar reg = mii_reg_desc_tbl[i].regno;
+
+ if (reg >= reglo && reg <= reghi)
+ dump_reg(regvals[reg - reglo], &mii_reg_desc_tbl[i]);
+ }
+}
+
+/* Print out field position, value, name */
+static void dump_field(const MII_field_desc_t *pdesc, ushort regval)
+{
+ if (pdesc->hi == pdesc->lo)
+ printf("%2u ", pdesc->lo);
+ else
+ printf("%2u-%2u", pdesc->hi, pdesc->lo);
+
+ printf(" = %5u %s", (regval >> pdesc->lo) & pdesc->mask,
+ pdesc->name);
+}
+
+static void dump_reg(
+ ushort regval,
+ const MII_reg_desc_t *prd)
+{
+ ulong i;
+ ushort mask_in_place;
+ const MII_field_desc_t *pdesc;
+
+ printf("%u. (%04hx) -- %s --\n",
+ prd->regno, regval, prd->name);
+
+ for (i = 0; i < prd->len; i++) {
+ pdesc = &prd->pdesc[i];
+
+ mask_in_place = pdesc->mask << pdesc->lo;
+
+ printf(" (%04hx:%04x) %u.",
+ mask_in_place,
+ regval & mask_in_place,
+ prd->regno);
+
+ if (!special_field(prd->regno, pdesc, regval))
+ dump_field(pdesc, regval);
+ printf("\n");
+
+ }
+ printf("\n");
+}
+
+/* Special fields:
+** 0.6,13
+** 0.8
+** 2.15-0
+** 3.15-0
+** 4.4-0
+** 5.4-0
+*/
+
+static bool special_field(ushort regno, const MII_field_desc_t *pdesc,
+ ushort regval)
+{
+ const ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask;
+
+ if ((regno == MII_BMCR) && (pdesc->lo == 6)) {
+ ushort speed_bits = regval & (BMCR_SPEED1000 | BMCR_SPEED100);
+ printf("%2u,%2u = b%u%u speed selection = %s Mbps",
+ 6, 13,
+ (regval >> 6) & 1,
+ (regval >> 13) & 1,
+ speed_bits == BMCR_SPEED1000 ? "1000" :
+ speed_bits == BMCR_SPEED100 ? "100" :
+ "10");
+ return 1;
+ }
+
+ else if ((regno == MII_BMCR) && (pdesc->lo == 8)) {
+ dump_field(pdesc, regval);
+ printf(" = %s", ((regval >> pdesc->lo) & 1) ? "full" : "half");
+ return 1;
+ }
+
+ else if ((regno == MII_ADVERTISE) && (pdesc->lo == 0)) {
+ dump_field(pdesc, regval);
+ printf(" = %s",
+ sel_bits == PHY_ANLPAR_PSB_802_3 ? "IEEE 802.3 CSMA/CD" :
+ sel_bits == PHY_ANLPAR_PSB_802_9 ?
+ "IEEE 802.9 ISLAN-16T" : "???");
+ return 1;
+ }
+
+ else if ((regno == MII_LPA) && (pdesc->lo == 0)) {
+ dump_field(pdesc, regval);
+ printf(" = %s",
+ sel_bits == PHY_ANLPAR_PSB_802_3 ? "IEEE 802.3 CSMA/CD" :
+ sel_bits == PHY_ANLPAR_PSB_802_9 ?
+ "IEEE 802.9 ISLAN-16T" : "???");
+ return 1;
+ }
+
+ return 0;
+}
+
+static char last_op[2];
+static uint last_data;
+static uint last_addr_lo;
+static uint last_addr_hi;
+static uint last_reg_lo;
+static uint last_reg_hi;
+static uint last_mask;
+
+static void extract_range(
+ char * input,
+ unsigned char * plo,
+ unsigned char * phi)
+{
+ char * end;
+ *plo = hextoul(input, &end);
+ if (*end == '-') {
+ end++;
+ *phi = hextoul(end, NULL);
+ }
+ else {
+ *phi = *plo;
+ }
+}
+
+/* ---------------------------------------------------------------- */
+static int do_mii(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char op[2];
+ unsigned char addrlo, addrhi, reglo, reghi;
+ unsigned char addr, reg;
+ unsigned short data, mask;
+ int rcode = 0;
+ const char *devname;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+#if defined(CONFIG_MII_INIT)
+ mii_init ();
+#endif
+
+ /*
+ * We use the last specified parameters, unless new ones are
+ * entered.
+ */
+ op[0] = last_op[0];
+ op[1] = last_op[1];
+ addrlo = last_addr_lo;
+ addrhi = last_addr_hi;
+ reglo = last_reg_lo;
+ reghi = last_reg_hi;
+ data = last_data;
+ mask = last_mask;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ op[0] = argv[1][0];
+ if (strlen(argv[1]) > 1)
+ op[1] = argv[1][1];
+ else
+ op[1] = '\0';
+
+ if (argc >= 3)
+ extract_range(argv[2], &addrlo, &addrhi);
+ if (argc >= 4)
+ extract_range(argv[3], &reglo, &reghi);
+ if (argc >= 5)
+ data = hextoul(argv[4], NULL);
+ if (argc >= 6)
+ mask = hextoul(argv[5], NULL);
+ }
+
+ if (addrhi > 31 && strncmp(op, "de", 2)) {
+ printf("Incorrect PHY address. Range should be 0-31\n");
+ return CMD_RET_USAGE;
+ }
+
+ /* use current device */
+ devname = miiphy_get_current_dev();
+
+ /*
+ * check info/read/write.
+ */
+ if (op[0] == 'i') {
+ unsigned char j, start, end;
+ unsigned int oui;
+ unsigned char model;
+ unsigned char rev;
+
+ /*
+ * Look for any and all PHYs. Valid addresses are 0..31.
+ */
+ if (argc >= 3) {
+ start = addrlo; end = addrhi;
+ } else {
+ start = 0; end = 31;
+ }
+
+ for (j = start; j <= end; j++) {
+ if (miiphy_info (devname, j, &oui, &model, &rev) == 0) {
+ printf("PHY 0x%02X: "
+ "OUI = 0x%04X, "
+ "Model = 0x%02X, "
+ "Rev = 0x%02X, "
+ "%3dbase%s, %s\n",
+ j, oui, model, rev,
+ miiphy_speed (devname, j),
+ miiphy_is_1000base_x (devname, j)
+ ? "X" : "T",
+ (miiphy_duplex (devname, j) == FULL)
+ ? "FDX" : "HDX");
+ }
+ }
+ } else if (op[0] == 'r') {
+ for (addr = addrlo; addr <= addrhi; addr++) {
+ for (reg = reglo; reg <= reghi; reg++) {
+ data = 0xffff;
+ if (miiphy_read (devname, addr, reg, &data) != 0) {
+ printf(
+ "Error reading from the PHY addr=%02x reg=%02x\n",
+ addr, reg);
+ rcode = 1;
+ } else {
+ if ((addrlo != addrhi) || (reglo != reghi))
+ printf("addr=%02x reg=%02x data=",
+ (uint)addr, (uint)reg);
+ printf("%04X\n", data & 0x0000FFFF);
+ }
+ }
+ if ((addrlo != addrhi) && (reglo != reghi))
+ printf("\n");
+ }
+ } else if (op[0] == 'w') {
+ for (addr = addrlo; addr <= addrhi; addr++) {
+ for (reg = reglo; reg <= reghi; reg++) {
+ if (miiphy_write (devname, addr, reg, data) != 0) {
+ printf("Error writing to the PHY addr=%02x reg=%02x\n",
+ addr, reg);
+ rcode = 1;
+ }
+ }
+ }
+ } else if (op[0] == 'm') {
+ for (addr = addrlo; addr <= addrhi; addr++) {
+ for (reg = reglo; reg <= reghi; reg++) {
+ unsigned short val = 0;
+ if (miiphy_read(devname, addr,
+ reg, &val)) {
+ printf("Error reading from the PHY");
+ printf(" addr=%02x", addr);
+ printf(" reg=%02x\n", reg);
+ rcode = 1;
+ } else {
+ val = (val & ~mask) | (data & mask);
+ if (miiphy_write(devname, addr,
+ reg, val)) {
+ printf("Error writing to the PHY");
+ printf(" addr=%02x", addr);
+ printf(" reg=%02x\n", reg);
+ rcode = 1;
+ }
+ }
+ }
+ }
+ } else if (strncmp(op, "du", 2) == 0) {
+ ushort regs[MII_STAT1000 + 1]; /* Last reg is 0x0a */
+ int ok = 1;
+ if (reglo > MII_STAT1000 || reghi > MII_STAT1000) {
+ printf("The MII dump command only formats the standard MII registers, 0-5, 9-a.\n");
+ return 1;
+ }
+ for (addr = addrlo; addr <= addrhi; addr++) {
+ for (reg = reglo; reg <= reghi; reg++) {
+ if (miiphy_read(devname, addr, reg,
+ &regs[reg - reglo]) != 0) {
+ ok = 0;
+ printf(
+ "Error reading from the PHY addr=%02x reg=%02x\n",
+ addr, reg);
+ rcode = 1;
+ }
+ }
+ if (ok)
+ MII_dump(regs, reglo, reghi);
+ printf("\n");
+ }
+ } else if (strncmp(op, "de", 2) == 0) {
+ if (argc == 2)
+ miiphy_listdev ();
+ else
+ miiphy_set_current_dev (argv[2]);
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ /*
+ * Save the parameters for repeats.
+ */
+ last_op[0] = op[0];
+ last_op[1] = op[1];
+ last_addr_lo = addrlo;
+ last_addr_hi = addrhi;
+ last_reg_lo = reglo;
+ last_reg_hi = reghi;
+ last_data = data;
+ last_mask = mask;
+
+ return rcode;
+}
+
+/***************************************************/
+
+U_BOOT_CMD(
+ mii, 6, 1, do_mii,
+ "MII utility commands",
+ "device - list available devices\n"
+ "mii device <devname> - set current device\n"
+ "mii info <addr> - display MII PHY info\n"
+ "mii read <addr> <reg> - read MII PHY <addr> register <reg>\n"
+ "mii write <addr> <reg> <data> - write MII PHY <addr> register <reg>\n"
+ "mii modify <addr> <reg> <data> <mask> - modify MII PHY <addr> register <reg>\n"
+ " updating bits identified in <mask>\n"
+ "mii dump <addr> <reg> - pretty-print <addr> <reg> (0-5 only)\n"
+ "Addr and/or reg may be ranges, e.g. 2-7."
+);
diff --git a/cmd/misc.c b/cmd/misc.c
new file mode 100644
index 00000000000..792d9723c75
--- /dev/null
+++ b/cmd/misc.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020 Wind River Systems, Inc.
+ *
+ * Author:
+ * Bin Meng <bin.meng@windriver.com>
+ *
+ * A command interface to access misc devices with MISC uclass driver APIs.
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <errno.h>
+#include <misc.h>
+
+enum misc_op {
+ MISC_OP_READ,
+ MISC_OP_WRITE
+};
+
+static char *misc_op_str[] = {
+ "read",
+ "write"
+};
+
+static int do_misc_list(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct udevice *dev;
+
+ printf("Device Index Driver\n");
+ printf("-------------------------------------\n");
+ for (uclass_first_device(UCLASS_MISC, &dev);
+ dev;
+ uclass_next_device(&dev)) {
+ printf("%-20s %5d %10s\n", dev->name, dev_seq(dev),
+ dev->driver->name);
+ }
+
+ return 0;
+}
+
+static int do_misc_op(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[], enum misc_op op)
+{
+ struct udevice *dev;
+ int offset;
+ void *buf;
+ int size;
+ int ret;
+
+ ret = uclass_get_device_by_name(UCLASS_MISC, argv[0], &dev);
+ if (ret) {
+ printf("Unable to find device %s\n", argv[0]);
+ return ret;
+ }
+
+ offset = hextoul(argv[1], NULL);
+ buf = (void *)hextoul(argv[2], NULL);
+ size = hextoul(argv[3], NULL);
+
+ if (op == MISC_OP_READ)
+ ret = misc_read(dev, offset, buf, size);
+ else
+ ret = misc_write(dev, offset, buf, size);
+
+ if (ret < 0) {
+ if (ret == -ENOSYS) {
+ printf("The device does not support %s\n",
+ misc_op_str[op]);
+ ret = 0;
+ }
+ } else {
+ if (ret == size)
+ ret = 0;
+ else
+ printf("Partially %s %d bytes\n", misc_op_str[op], ret);
+ }
+
+ return ret;
+}
+
+static int do_misc_read(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ return do_misc_op(cmdtp, flag, argc, argv, MISC_OP_READ);
+}
+
+static int do_misc_write(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ return do_misc_op(cmdtp, flag, argc, argv, MISC_OP_WRITE);
+}
+
+static struct cmd_tbl misc_commands[] = {
+ U_BOOT_CMD_MKENT(list, 0, 1, do_misc_list, "", ""),
+ U_BOOT_CMD_MKENT(read, 4, 1, do_misc_read, "", ""),
+ U_BOOT_CMD_MKENT(write, 4, 1, do_misc_write, "", ""),
+};
+
+static int do_misc(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct cmd_tbl *misc_cmd;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ misc_cmd = find_cmd_tbl(argv[1], misc_commands,
+ ARRAY_SIZE(misc_commands));
+ argc -= 2;
+ argv += 2;
+ if (!misc_cmd || argc != misc_cmd->maxargs)
+ return CMD_RET_USAGE;
+
+ ret = misc_cmd->cmd(misc_cmd, flag, argc, argv);
+
+ return cmd_process_error(misc_cmd, ret);
+}
+
+U_BOOT_CMD(
+ misc, 6, 1, do_misc,
+ "Access miscellaneous devices with MISC uclass driver APIs",
+ "list - list all miscellaneous devices\n"
+ "misc read name offset addr len - read `len' bytes starting at\n"
+ " `offset' of device `name'\n"
+ " to memory at `addr'\n"
+ "misc write name offset addr len - write `len' bytes starting at\n"
+ " `offset' of device `name'\n"
+ " from memory at `addr'"
+);
diff --git a/cmd/mmc.c b/cmd/mmc.c
new file mode 100644
index 00000000000..5340a58be8e
--- /dev/null
+++ b/cmd/mmc.c
@@ -0,0 +1,1368 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2003
+ * Kyle Harris, kharris@nexus-tech.net
+ */
+
+#include <blk.h>
+#include <command.h>
+#include <console.h>
+#include <display_options.h>
+#include <env.h>
+#include <mapmem.h>
+#include <memalign.h>
+#include <mmc.h>
+#include <part.h>
+#include <sparse_format.h>
+#include <image-sparse.h>
+#include <vsprintf.h>
+#include <linux/ctype.h>
+
+static int curr_device = -1;
+
+static void print_mmcinfo(struct mmc *mmc)
+{
+ int i;
+
+ printf("Device: %s\n", mmc->cfg->name);
+ printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24);
+ if (IS_SD(mmc)) {
+ printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff);
+ printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff,
+ (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
+ (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);
+ } else {
+ printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xff);
+ printf("Name: %c%c%c%c%c%c \n", mmc->cid[0] & 0xff,
+ (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
+ (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff,
+ (mmc->cid[2] >> 24));
+ }
+
+ printf("Bus Speed: %d\n", mmc->clock);
+#if CONFIG_IS_ENABLED(MMC_VERBOSE)
+ printf("Mode: %s\n", mmc_mode_name(mmc->selected_mode));
+ mmc_dump_capabilities("card capabilities", mmc->card_caps);
+ mmc_dump_capabilities("host capabilities", mmc->host_caps);
+#endif
+ printf("Rd Block Len: %d\n", mmc->read_bl_len);
+
+ printf("%s version %d.%d", IS_SD(mmc) ? "SD" : "MMC",
+ EXTRACT_SDMMC_MAJOR_VERSION(mmc->version),
+ EXTRACT_SDMMC_MINOR_VERSION(mmc->version));
+ if (EXTRACT_SDMMC_CHANGE_VERSION(mmc->version) != 0)
+ printf(".%d", EXTRACT_SDMMC_CHANGE_VERSION(mmc->version));
+ printf("\n");
+
+ printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No");
+ puts("Capacity: ");
+ print_size(mmc->capacity, "\n");
+
+ printf("Bus Width: %d-bit%s\n", mmc->bus_width,
+ mmc->ddr_mode ? " DDR" : "");
+
+#if CONFIG_IS_ENABLED(MMC_WRITE)
+ puts("Erase Group Size: ");
+ print_size(((u64)mmc->erase_grp_size) << 9, "\n");
+#endif
+
+ if (!IS_SD(mmc) && mmc->version >= MMC_VERSION_4_41) {
+ bool has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0;
+ bool usr_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_USR);
+ ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
+ u8 wp;
+ int ret;
+
+#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING)
+ puts("HC WP Group Size: ");
+ print_size(((u64)mmc->hc_wp_grp_size) << 9, "\n");
+#endif
+
+ puts("User Capacity: ");
+ print_size(mmc->capacity_user, usr_enh ? " ENH" : "");
+ if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_USR)
+ puts(" WRREL\n");
+ else
+ putc('\n');
+ if (usr_enh) {
+ puts("User Enhanced Start: ");
+ print_size(mmc->enh_user_start, "\n");
+ puts("User Enhanced Size: ");
+ print_size(mmc->enh_user_size, "\n");
+ }
+ puts("Boot Capacity: ");
+ print_size(mmc->capacity_boot, has_enh ? " ENH\n" : "\n");
+ puts("RPMB Capacity: ");
+ print_size(mmc->capacity_rpmb, has_enh ? " ENH\n" : "\n");
+
+ for (i = 0; i < ARRAY_SIZE(mmc->capacity_gp); i++) {
+ bool is_enh = has_enh &&
+ (mmc->part_attr & EXT_CSD_ENH_GP(i));
+ if (mmc->capacity_gp[i]) {
+ printf("GP%i Capacity: ", i+1);
+ print_size(mmc->capacity_gp[i],
+ is_enh ? " ENH" : "");
+ if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_GP(i))
+ puts(" WRREL\n");
+ else
+ putc('\n');
+ }
+ }
+ ret = mmc_send_ext_csd(mmc, ext_csd);
+ if (ret)
+ return;
+ wp = ext_csd[EXT_CSD_BOOT_WP_STATUS];
+ for (i = 0; i < 2; ++i) {
+ printf("Boot area %d is ", i);
+ switch (wp & 3) {
+ case 0:
+ printf("not write protected\n");
+ break;
+ case 1:
+ printf("power on protected\n");
+ break;
+ case 2:
+ printf("permanently protected\n");
+ break;
+ default:
+ printf("in reserved protection state\n");
+ break;
+ }
+ wp >>= 2;
+ }
+ }
+}
+
+static struct mmc *__init_mmc_device(int dev, bool force_init,
+ enum bus_mode speed_mode)
+{
+ struct mmc *mmc;
+ mmc = find_mmc_device(dev);
+ if (!mmc) {
+ printf("no mmc device at slot %x\n", dev);
+ return NULL;
+ }
+
+ if (!mmc_getcd(mmc))
+ force_init = true;
+
+ if (force_init)
+ mmc->has_init = 0;
+
+ if (IS_ENABLED(CONFIG_MMC_SPEED_MODE_SET))
+ mmc->user_speed_mode = speed_mode;
+
+ if (mmc_init(mmc))
+ return NULL;
+
+#ifdef CONFIG_BLOCK_CACHE
+ struct blk_desc *bd = mmc_get_blk_desc(mmc);
+ blkcache_invalidate(bd->uclass_id, bd->devnum);
+#endif
+
+ return mmc;
+}
+
+static struct mmc *init_mmc_device(int dev, bool force_init)
+{
+ return __init_mmc_device(dev, force_init, MMC_MODES_END);
+}
+
+static int do_mmcinfo(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct mmc *mmc;
+
+ if (curr_device < 0) {
+ if (get_mmc_num() > 0)
+ curr_device = 0;
+ else {
+ puts("No MMC device available\n");
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ mmc = init_mmc_device(curr_device, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ print_mmcinfo(mmc);
+ return CMD_RET_SUCCESS;
+}
+
+#if CONFIG_IS_ENABLED(CMD_MMC_RPMB)
+static int confirm_key_prog(void)
+{
+ puts("Warning: Programming authentication key can be done only once !\n"
+ " Use this command only if you are sure of what you are doing,\n"
+ "Really perform the key programming? <y/N> ");
+ if (confirm_yesno())
+ return 1;
+
+ puts("Authentication key programming aborted\n");
+ return 0;
+}
+
+static int do_mmcrpmb_key(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ void *key_addr;
+ struct mmc *mmc = find_mmc_device(curr_device);
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ key_addr = (void *)hextoul(argv[1], NULL);
+ if (!confirm_key_prog())
+ return CMD_RET_FAILURE;
+ if (mmc_rpmb_set_key(mmc, key_addr)) {
+ printf("ERROR - Key already programmed ?\n");
+ return CMD_RET_FAILURE;
+ }
+ return CMD_RET_SUCCESS;
+}
+
+static int do_mmcrpmb_read(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ u16 blk, cnt;
+ void *addr;
+ int n;
+ void *key_addr = NULL;
+ struct mmc *mmc = find_mmc_device(curr_device);
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ addr = (void *)hextoul(argv[1], NULL);
+ blk = hextoul(argv[2], NULL);
+ cnt = hextoul(argv[3], NULL);
+
+ if (argc == 5)
+ key_addr = (void *)hextoul(argv[4], NULL);
+
+ printf("MMC RPMB read: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
+ n = mmc_rpmb_read(mmc, addr, blk, cnt, key_addr);
+
+ printf("%d RPMB blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR");
+ if (n != cnt)
+ return CMD_RET_FAILURE;
+ return CMD_RET_SUCCESS;
+}
+
+static int do_mmcrpmb_write(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ u16 blk, cnt;
+ void *addr;
+ int n;
+ void *key_addr;
+ struct mmc *mmc = find_mmc_device(curr_device);
+
+ if (argc != 5)
+ return CMD_RET_USAGE;
+
+ addr = (void *)hextoul(argv[1], NULL);
+ blk = hextoul(argv[2], NULL);
+ cnt = hextoul(argv[3], NULL);
+ key_addr = (void *)hextoul(argv[4], NULL);
+
+ printf("MMC RPMB write: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
+ n = mmc_rpmb_write(mmc, addr, blk, cnt, key_addr);
+
+ printf("%d RPMB blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR");
+ if (n != cnt)
+ return CMD_RET_FAILURE;
+ return CMD_RET_SUCCESS;
+}
+
+static int do_mmcrpmb_counter(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ unsigned long counter;
+ struct mmc *mmc = find_mmc_device(curr_device);
+
+ if (mmc_rpmb_get_counter(mmc, &counter))
+ return CMD_RET_FAILURE;
+ printf("RPMB Write counter= %lx\n", counter);
+ return CMD_RET_SUCCESS;
+}
+
+static struct cmd_tbl cmd_rpmb[] = {
+ U_BOOT_CMD_MKENT(key, 2, 0, do_mmcrpmb_key, "", ""),
+ U_BOOT_CMD_MKENT(read, 5, 1, do_mmcrpmb_read, "", ""),
+ U_BOOT_CMD_MKENT(write, 5, 0, do_mmcrpmb_write, "", ""),
+ U_BOOT_CMD_MKENT(counter, 1, 1, do_mmcrpmb_counter, "", ""),
+};
+
+static int do_mmcrpmb(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct cmd_tbl *cp;
+ struct mmc *mmc;
+ char original_part;
+ int ret;
+
+ cp = find_cmd_tbl(argv[1], cmd_rpmb, ARRAY_SIZE(cmd_rpmb));
+
+ /* Drop the rpmb subcommand */
+ argc--;
+ argv++;
+
+ if (cp == NULL || argc > cp->maxargs)
+ return CMD_RET_USAGE;
+ if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp))
+ return CMD_RET_SUCCESS;
+
+ mmc = init_mmc_device(curr_device, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (!(mmc->version & MMC_VERSION_MMC)) {
+ printf("It is not an eMMC device\n");
+ return CMD_RET_FAILURE;
+ }
+ if (mmc->version < MMC_VERSION_4_41) {
+ printf("RPMB not supported before version 4.41\n");
+ return CMD_RET_FAILURE;
+ }
+ /* Switch to the RPMB partition */
+#ifndef CONFIG_BLK
+ original_part = mmc->block_dev.hwpart;
+#else
+ original_part = mmc_get_blk_desc(mmc)->hwpart;
+#endif
+ if (blk_select_hwpart_devnum(UCLASS_MMC, curr_device, MMC_PART_RPMB) !=
+ 0)
+ return CMD_RET_FAILURE;
+ ret = cp->cmd(cmdtp, flag, argc, argv);
+
+ /* Return to original partition */
+ if (blk_select_hwpart_devnum(UCLASS_MMC, curr_device, original_part) !=
+ 0)
+ return CMD_RET_FAILURE;
+ return ret;
+}
+#endif
+
+static int do_mmc_read(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct mmc *mmc;
+ u32 blk, cnt, n;
+ void *ptr;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ ptr = map_sysmem(hextoul(argv[1], NULL), 0);
+ blk = hextoul(argv[2], NULL);
+ cnt = hextoul(argv[3], NULL);
+
+ mmc = init_mmc_device(curr_device, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ printf("MMC read: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
+
+ n = blk_dread(mmc_get_blk_desc(mmc), blk, cnt, ptr);
+ printf("%d blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR");
+ unmap_sysmem(ptr);
+
+ return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+
+#if CONFIG_IS_ENABLED(CMD_MMC_SWRITE)
+static lbaint_t mmc_sparse_write(struct sparse_storage *info, lbaint_t blk,
+ lbaint_t blkcnt, const void *buffer)
+{
+ struct blk_desc *dev_desc = info->priv;
+
+ return blk_dwrite(dev_desc, blk, blkcnt, buffer);
+}
+
+static lbaint_t mmc_sparse_reserve(struct sparse_storage *info,
+ lbaint_t blk, lbaint_t blkcnt)
+{
+ return blkcnt;
+}
+
+static int do_mmc_sparse_write(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct sparse_storage sparse;
+ struct blk_desc *dev_desc;
+ struct mmc *mmc;
+ char dest[11];
+ void *addr;
+ u32 blk;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ addr = (void *)hextoul(argv[1], NULL);
+ blk = hextoul(argv[2], NULL);
+
+ if (!is_sparse_image(addr)) {
+ printf("Not a sparse image\n");
+ return CMD_RET_FAILURE;
+ }
+
+ mmc = init_mmc_device(curr_device, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ printf("MMC Sparse write: dev # %d, block # %d ... ",
+ curr_device, blk);
+
+ if (mmc_getwp(mmc) == 1) {
+ printf("Error: card is write protected!\n");
+ return CMD_RET_FAILURE;
+ }
+
+ dev_desc = mmc_get_blk_desc(mmc);
+ sparse.priv = dev_desc;
+ sparse.blksz = 512;
+ sparse.start = blk;
+ sparse.size = dev_desc->lba - blk;
+ sparse.write = mmc_sparse_write;
+ sparse.reserve = mmc_sparse_reserve;
+ sparse.mssg = NULL;
+ sprintf(dest, "0x" LBAF, sparse.start * sparse.blksz);
+
+ if (write_sparse_image(&sparse, dest, addr, NULL))
+ return CMD_RET_FAILURE;
+ else
+ return CMD_RET_SUCCESS;
+}
+#endif
+
+#if CONFIG_IS_ENABLED(MMC_WRITE)
+static int do_mmc_write(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct mmc *mmc;
+ u32 blk, cnt, n;
+ void *ptr;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ ptr = map_sysmem(hextoul(argv[1], NULL), 0);
+ blk = hextoul(argv[2], NULL);
+ cnt = hextoul(argv[3], NULL);
+
+ mmc = init_mmc_device(curr_device, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ printf("MMC write: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
+
+ if (mmc_getwp(mmc) == 1) {
+ printf("Error: card is write protected!\n");
+ return CMD_RET_FAILURE;
+ }
+ n = blk_dwrite(mmc_get_blk_desc(mmc), blk, cnt, ptr);
+ printf("%d blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR");
+ unmap_sysmem(ptr);
+
+ return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+
+static int do_mmc_erase(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct mmc *mmc;
+ struct disk_partition info;
+ u32 blk, cnt, n;
+
+ if (argc < 2 || argc > 3)
+ return CMD_RET_USAGE;
+
+ mmc = init_mmc_device(curr_device, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (argc == 3) {
+ blk = hextoul(argv[1], NULL);
+ cnt = hextoul(argv[2], NULL);
+ } else if (part_get_info_by_name(mmc_get_blk_desc(mmc), argv[1], &info) >= 0) {
+ blk = info.start;
+ cnt = info.size;
+ } else {
+ return CMD_RET_FAILURE;
+ }
+
+ printf("MMC erase: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
+
+ if (mmc_getwp(mmc) == 1) {
+ printf("Error: card is write protected!\n");
+ return CMD_RET_FAILURE;
+ }
+ n = blk_derase(mmc_get_blk_desc(mmc), blk, cnt);
+ printf("%d blocks erased: %s\n", n, (n == cnt) ? "OK" : "ERROR");
+
+ return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+#endif
+
+static int do_mmc_rescan(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct mmc *mmc;
+
+ if (argc == 1) {
+ mmc = init_mmc_device(curr_device, true);
+ } else if (argc == 2) {
+ enum bus_mode speed_mode;
+
+ speed_mode = (int)dectoul(argv[1], NULL);
+ mmc = __init_mmc_device(curr_device, true, speed_mode);
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_mmc_part(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct blk_desc *mmc_dev;
+ struct mmc *mmc;
+
+ mmc = init_mmc_device(curr_device, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ mmc_dev = blk_get_devnum_by_uclass_id(UCLASS_MMC, curr_device);
+ if (mmc_dev != NULL && mmc_dev->type != DEV_TYPE_UNKNOWN) {
+ part_print(mmc_dev);
+ return CMD_RET_SUCCESS;
+ }
+
+ puts("get mmc type error!\n");
+ return CMD_RET_FAILURE;
+}
+
+static int do_mmc_dev(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ int dev, part = 0, ret;
+ struct mmc *mmc;
+
+ if (argc == 1) {
+ dev = curr_device;
+ mmc = init_mmc_device(dev, true);
+ } else if (argc == 2) {
+ dev = (int)dectoul(argv[1], NULL);
+ mmc = init_mmc_device(dev, true);
+ } else if (argc == 3) {
+ dev = (int)dectoul(argv[1], NULL);
+ part = (int)dectoul(argv[2], NULL);
+ if (part > PART_ACCESS_MASK) {
+ printf("#part_num shouldn't be larger than %d\n",
+ PART_ACCESS_MASK);
+ return CMD_RET_FAILURE;
+ }
+ mmc = init_mmc_device(dev, true);
+ } else if (argc == 4) {
+ enum bus_mode speed_mode;
+
+ dev = (int)dectoul(argv[1], NULL);
+ part = (int)dectoul(argv[2], NULL);
+ if (part > PART_ACCESS_MASK) {
+ printf("#part_num shouldn't be larger than %d\n",
+ PART_ACCESS_MASK);
+ return CMD_RET_FAILURE;
+ }
+ speed_mode = (int)dectoul(argv[3], NULL);
+ mmc = __init_mmc_device(dev, true, speed_mode);
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ ret = blk_select_hwpart_devnum(UCLASS_MMC, dev, part);
+ printf("switch to partitions #%d, %s\n",
+ part, (!ret) ? "OK" : "ERROR");
+ if (ret)
+ return 1;
+
+ curr_device = dev;
+ if (mmc->part_config == MMCPART_NOAVAILABLE)
+ printf("mmc%d is current device\n", curr_device);
+ else
+ printf("mmc%d(part %d) is current device\n",
+ curr_device, mmc_get_blk_desc(mmc)->hwpart);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_mmc_list(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ print_mmc_devices('\n');
+ return CMD_RET_SUCCESS;
+}
+
+#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING)
+static void parse_hwpart_user_enh_size(struct mmc *mmc,
+ struct mmc_hwpart_conf *pconf,
+ char *argv)
+{
+ int i, ret;
+
+ pconf->user.enh_size = 0;
+
+ if (!strcmp(argv, "-")) { /* The rest of eMMC */
+ ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
+ ret = mmc_send_ext_csd(mmc, ext_csd);
+ if (ret)
+ return;
+ /* The enh_size value is in 512B block units */
+ pconf->user.enh_size =
+ ((ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT + 2] << 16) +
+ (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT + 1] << 8) +
+ ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT]) * 1024 *
+ ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
+ ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+ pconf->user.enh_size -= pconf->user.enh_start;
+ for (i = 0; i < ARRAY_SIZE(mmc->capacity_gp); i++) {
+ /*
+ * If the eMMC already has GP partitions set,
+ * subtract their size from the maximum USER
+ * partition size.
+ *
+ * Else, if the command was used to configure new
+ * GP partitions, subtract their size from maximum
+ * USER partition size.
+ */
+ if (mmc->capacity_gp[i]) {
+ /* The capacity_gp is in 1B units */
+ pconf->user.enh_size -= mmc->capacity_gp[i] >> 9;
+ } else if (pconf->gp_part[i].size) {
+ /* The gp_part[].size is in 512B units */
+ pconf->user.enh_size -= pconf->gp_part[i].size;
+ }
+ }
+ } else {
+ pconf->user.enh_size = dectoul(argv, NULL);
+ }
+}
+
+static int parse_hwpart_user(struct mmc *mmc, struct mmc_hwpart_conf *pconf,
+ int argc, char *const argv[])
+{
+ int i = 0;
+
+ memset(&pconf->user, 0, sizeof(pconf->user));
+
+ while (i < argc) {
+ if (!strcmp(argv[i], "enh")) {
+ if (i + 2 >= argc)
+ return -1;
+ pconf->user.enh_start =
+ dectoul(argv[i + 1], NULL);
+ parse_hwpart_user_enh_size(mmc, pconf, argv[i + 2]);
+ i += 3;
+ } else if (!strcmp(argv[i], "wrrel")) {
+ if (i + 1 >= argc)
+ return -1;
+ pconf->user.wr_rel_change = 1;
+ if (!strcmp(argv[i+1], "on"))
+ pconf->user.wr_rel_set = 1;
+ else if (!strcmp(argv[i+1], "off"))
+ pconf->user.wr_rel_set = 0;
+ else
+ return -1;
+ i += 2;
+ } else {
+ break;
+ }
+ }
+ return i;
+}
+
+static int parse_hwpart_gp(struct mmc_hwpart_conf *pconf, int pidx,
+ int argc, char *const argv[])
+{
+ int i;
+
+ memset(&pconf->gp_part[pidx], 0, sizeof(pconf->gp_part[pidx]));
+
+ if (1 >= argc)
+ return -1;
+ pconf->gp_part[pidx].size = dectoul(argv[0], NULL);
+
+ i = 1;
+ while (i < argc) {
+ if (!strcmp(argv[i], "enh")) {
+ pconf->gp_part[pidx].enhanced = 1;
+ i += 1;
+ } else if (!strcmp(argv[i], "wrrel")) {
+ if (i + 1 >= argc)
+ return -1;
+ pconf->gp_part[pidx].wr_rel_change = 1;
+ if (!strcmp(argv[i+1], "on"))
+ pconf->gp_part[pidx].wr_rel_set = 1;
+ else if (!strcmp(argv[i+1], "off"))
+ pconf->gp_part[pidx].wr_rel_set = 0;
+ else
+ return -1;
+ i += 2;
+ } else {
+ break;
+ }
+ }
+ return i;
+}
+
+static int do_mmc_hwpartition(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct mmc *mmc;
+ struct mmc_hwpart_conf pconf = { };
+ enum mmc_hwpart_conf_mode mode = MMC_HWPART_CONF_CHECK;
+ int i, r, pidx;
+
+ mmc = init_mmc_device(curr_device, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ puts("SD doesn't support partitioning\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc < 1)
+ return CMD_RET_USAGE;
+ i = 1;
+ while (i < argc) {
+ if (!strcmp(argv[i], "user")) {
+ i++;
+ r = parse_hwpart_user(mmc, &pconf, argc - i, &argv[i]);
+ if (r < 0)
+ return CMD_RET_USAGE;
+ i += r;
+ } else if (!strncmp(argv[i], "gp", 2) &&
+ strlen(argv[i]) == 3 &&
+ argv[i][2] >= '1' && argv[i][2] <= '4') {
+ pidx = argv[i][2] - '1';
+ i++;
+ r = parse_hwpart_gp(&pconf, pidx, argc-i, &argv[i]);
+ if (r < 0)
+ return CMD_RET_USAGE;
+ i += r;
+ } else if (!strcmp(argv[i], "check")) {
+ mode = MMC_HWPART_CONF_CHECK;
+ i++;
+ } else if (!strcmp(argv[i], "set")) {
+ mode = MMC_HWPART_CONF_SET;
+ i++;
+ } else if (!strcmp(argv[i], "complete")) {
+ mode = MMC_HWPART_CONF_COMPLETE;
+ i++;
+ } else {
+ return CMD_RET_USAGE;
+ }
+ }
+
+ puts("Partition configuration:\n");
+ if (pconf.user.enh_size) {
+ puts("\tUser Enhanced Start: ");
+ print_size(((u64)pconf.user.enh_start) << 9, "\n");
+ puts("\tUser Enhanced Size: ");
+ print_size(((u64)pconf.user.enh_size) << 9, "\n");
+ } else {
+ puts("\tNo enhanced user data area\n");
+ }
+ if (pconf.user.wr_rel_change)
+ printf("\tUser partition write reliability: %s\n",
+ pconf.user.wr_rel_set ? "on" : "off");
+ for (pidx = 0; pidx < 4; pidx++) {
+ if (pconf.gp_part[pidx].size) {
+ printf("\tGP%i Capacity: ", pidx+1);
+ print_size(((u64)pconf.gp_part[pidx].size) << 9,
+ pconf.gp_part[pidx].enhanced ?
+ " ENH\n" : "\n");
+ } else {
+ printf("\tNo GP%i partition\n", pidx+1);
+ }
+ if (pconf.gp_part[pidx].wr_rel_change)
+ printf("\tGP%i write reliability: %s\n", pidx+1,
+ pconf.gp_part[pidx].wr_rel_set ? "on" : "off");
+ }
+
+ if (!mmc_hwpart_config(mmc, &pconf, mode)) {
+ if (mode == MMC_HWPART_CONF_COMPLETE)
+ puts("Partitioning successful, "
+ "power-cycle to make effective\n");
+ return CMD_RET_SUCCESS;
+ } else {
+ puts("Failed!\n");
+ return CMD_RET_FAILURE;
+ }
+}
+#endif
+
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+static int do_mmc_bootbus(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ int dev;
+ struct mmc *mmc;
+ u8 width, reset, mode;
+
+ if (argc != 5)
+ return CMD_RET_USAGE;
+ dev = dectoul(argv[1], NULL);
+ width = dectoul(argv[2], NULL);
+ reset = dectoul(argv[3], NULL);
+ mode = dectoul(argv[4], NULL);
+
+ mmc = init_mmc_device(dev, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ puts("BOOT_BUS_WIDTH only exists on eMMC\n");
+ return CMD_RET_FAILURE;
+ }
+
+ /*
+ * BOOT_BUS_CONDITIONS[177]
+ * BOOT_MODE[4:3]
+ * 0x0 : Use SDR + Backward compatible timing in boot operation
+ * 0x1 : Use SDR + High Speed Timing in boot operation mode
+ * 0x2 : Use DDR in boot operation
+ * RESET_BOOT_BUS_CONDITIONS
+ * 0x0 : Reset bus width to x1, SDR, Backward compatible
+ * 0x1 : Retain BOOT_BUS_WIDTH and BOOT_MODE
+ * BOOT_BUS_WIDTH
+ * 0x0 : x1(sdr) or x4 (ddr) buswidth
+ * 0x1 : x4(sdr/ddr) buswith
+ * 0x2 : x8(sdr/ddr) buswith
+ *
+ */
+ if (width >= 0x3) {
+ printf("boot_bus_width %d is invalid\n", width);
+ return CMD_RET_FAILURE;
+ }
+
+ if (reset >= 0x2) {
+ printf("reset_boot_bus_width %d is invalid\n", reset);
+ return CMD_RET_FAILURE;
+ }
+
+ if (mode >= 0x3) {
+ printf("reset_boot_bus_width %d is invalid\n", mode);
+ return CMD_RET_FAILURE;
+ }
+
+ /* acknowledge to be sent during boot operation */
+ if (mmc_set_boot_bus_width(mmc, width, reset, mode)) {
+ puts("BOOT_BUS_WIDTH is failed to change.\n");
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Set to BOOT_BUS_WIDTH = 0x%x, RESET = 0x%x, BOOT_MODE = 0x%x\n",
+ width, reset, mode);
+ return CMD_RET_SUCCESS;
+}
+
+static int do_mmc_boot_resize(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ int dev;
+ struct mmc *mmc;
+ u32 bootsize, rpmbsize;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+ dev = dectoul(argv[1], NULL);
+ bootsize = dectoul(argv[2], NULL);
+ rpmbsize = dectoul(argv[3], NULL);
+
+ mmc = init_mmc_device(dev, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ printf("It is not an eMMC device\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (mmc_boot_partition_size_change(mmc, bootsize, rpmbsize)) {
+ printf("EMMC boot partition Size change Failed.\n");
+ return CMD_RET_FAILURE;
+ }
+
+ printf("EMMC boot partition Size %d MB\n", bootsize);
+ printf("EMMC RPMB partition Size %d MB\n", rpmbsize);
+ return CMD_RET_SUCCESS;
+}
+
+static int mmc_partconf_print(struct mmc *mmc, const char *varname)
+{
+ u8 ack, access, part;
+
+ if (mmc->part_config == MMCPART_NOAVAILABLE) {
+ printf("No part_config info for ver. 0x%x\n", mmc->version);
+ return CMD_RET_FAILURE;
+ }
+
+ access = EXT_CSD_EXTRACT_PARTITION_ACCESS(mmc->part_config);
+ ack = EXT_CSD_EXTRACT_BOOT_ACK(mmc->part_config);
+ part = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config);
+
+ if(varname)
+ env_set_hex(varname, part);
+
+ printf("EXT_CSD[179], PARTITION_CONFIG:\n"
+ "BOOT_ACK: 0x%x\n"
+ "BOOT_PARTITION_ENABLE: 0x%x (%s)\n"
+ "PARTITION_ACCESS: 0x%x (%s)\n", ack, part, emmc_boot_part_names[part],
+ access, emmc_hwpart_names[access]);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_mmc_partconf(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ int ret, dev;
+ struct mmc *mmc;
+ u8 ack, part_num, access;
+
+ if (argc != 2 && argc != 3 && argc != 5)
+ return CMD_RET_USAGE;
+
+ dev = dectoul(argv[1], NULL);
+
+ mmc = init_mmc_device(dev, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ puts("PARTITION_CONFIG only exists on eMMC\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc == 2 || argc == 3)
+ return mmc_partconf_print(mmc, cmd_arg2(argc, argv));
+
+ /* BOOT_ACK */
+ ack = dectoul(argv[2], NULL);
+ /* BOOT_PARTITION_ENABLE */
+ if (!isdigit(*argv[3])) {
+ for (part_num = ARRAY_SIZE(emmc_boot_part_names) - 1; part_num > 0; part_num--) {
+ if (!strcmp(argv[3], emmc_boot_part_names[part_num]))
+ break;
+ }
+ } else {
+ part_num = dectoul(argv[3], NULL);
+ }
+ /* PARTITION_ACCESS */
+ if (!isdigit(*argv[4])) {
+ for (access = ARRAY_SIZE(emmc_hwpart_names) - 1; access > 0; access--) {
+ if (!strcmp(argv[4], emmc_hwpart_names[access]))
+ break;
+ }
+ } else {
+ access = dectoul(argv[4], NULL);
+ }
+
+ /* acknowledge to be sent during boot operation */
+ ret = mmc_set_part_conf(mmc, ack, part_num, access);
+ if (ret != 0)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_mmc_rst_func(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ int ret, dev;
+ struct mmc *mmc;
+ u8 enable;
+
+ /*
+ * Set the RST_n_ENABLE bit of RST_n_FUNCTION
+ * The only valid values are 0x0, 0x1 and 0x2 and writing
+ * a value of 0x1 or 0x2 sets the value permanently.
+ */
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ dev = dectoul(argv[1], NULL);
+ enable = dectoul(argv[2], NULL);
+
+ if (enable > 2) {
+ puts("Invalid RST_n_ENABLE value\n");
+ return CMD_RET_USAGE;
+ }
+
+ mmc = init_mmc_device(dev, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ puts("RST_n_FUNCTION only exists on eMMC\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = mmc_set_rst_n_function(mmc, enable);
+ if (ret != 0)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+#endif
+static int do_mmc_setdsr(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct mmc *mmc;
+ u32 val;
+ int ret;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+ val = hextoul(argv[1], NULL);
+
+ mmc = find_mmc_device(curr_device);
+ if (!mmc) {
+ printf("no mmc device at slot %x\n", curr_device);
+ return CMD_RET_FAILURE;
+ }
+ ret = mmc_set_dsr(mmc, val);
+ printf("set dsr %s\n", (!ret) ? "OK, force rescan" : "ERROR");
+ if (!ret) {
+ mmc->has_init = 0;
+ if (mmc_init(mmc))
+ return CMD_RET_FAILURE;
+ else
+ return CMD_RET_SUCCESS;
+ }
+ return ret;
+}
+
+#ifdef CONFIG_CMD_BKOPS_ENABLE
+static int mmc_bkops_common(char *device, bool autobkops, bool enable)
+{
+ struct mmc *mmc;
+ int dev;
+
+ dev = dectoul(device, NULL);
+
+ mmc = init_mmc_device(dev, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ puts("BKOPS_EN only exists on eMMC\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return mmc_set_bkops_enable(mmc, autobkops, enable);
+}
+
+static int do_mmc_bkops(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ bool autobkops, enable;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ if (!strcmp(argv[2], "manual"))
+ autobkops = false;
+ else if (!strcmp(argv[2], "auto"))
+ autobkops = true;
+ else
+ return CMD_RET_FAILURE;
+
+ if (!strcmp(argv[3], "disable"))
+ enable = false;
+ else if (!strcmp(argv[3], "enable"))
+ enable = true;
+ else
+ return CMD_RET_FAILURE;
+
+ return mmc_bkops_common(argv[1], autobkops, enable);
+}
+
+static int do_mmc_bkops_enable(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ return mmc_bkops_common(argv[1], false, true);
+}
+#endif
+
+static int do_mmc_boot_wp(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ int err;
+ struct mmc *mmc;
+ int part;
+
+ mmc = init_mmc_device(curr_device, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+ if (IS_SD(mmc)) {
+ printf("It is not an eMMC device\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc == 2) {
+ part = dectoul(argv[1], NULL);
+ err = mmc_boot_wp_single_partition(mmc, part);
+ } else {
+ err = mmc_boot_wp(mmc);
+ }
+
+ if (err)
+ return CMD_RET_FAILURE;
+ printf("boot areas protected\n");
+ return CMD_RET_SUCCESS;
+}
+
+#if CONFIG_IS_ENABLED(CMD_MMC_REG)
+static int do_mmc_reg(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
+ struct mmc *mmc;
+ int i, ret;
+ u32 off;
+
+ if (argc < 3 || argc > 5)
+ return CMD_RET_USAGE;
+
+ mmc = find_mmc_device(curr_device);
+ if (!mmc) {
+ printf("no mmc device at slot %x\n", curr_device);
+ return CMD_RET_FAILURE;
+ }
+
+ if (IS_SD(mmc)) {
+ printf("SD registers are not supported\n");
+ return CMD_RET_FAILURE;
+ }
+
+ off = simple_strtoul(argv[3], NULL, 10);
+ if (!strcmp(argv[2], "cid")) {
+ if (off > 3)
+ return CMD_RET_USAGE;
+ printf("CID[%i]: 0x%08x\n", off, mmc->cid[off]);
+ if (argv[4])
+ env_set_hex(argv[4], mmc->cid[off]);
+ return CMD_RET_SUCCESS;
+ }
+ if (!strcmp(argv[2], "csd")) {
+ if (off > 3)
+ return CMD_RET_USAGE;
+ printf("CSD[%i]: 0x%08x\n", off, mmc->csd[off]);
+ if (argv[4])
+ env_set_hex(argv[4], mmc->csd[off]);
+ return CMD_RET_SUCCESS;
+ }
+ if (!strcmp(argv[2], "dsr")) {
+ printf("DSR: 0x%08x\n", mmc->dsr);
+ if (argv[4])
+ env_set_hex(argv[4], mmc->dsr);
+ return CMD_RET_SUCCESS;
+ }
+ if (!strcmp(argv[2], "ocr")) {
+ printf("OCR: 0x%08x\n", mmc->ocr);
+ if (argv[4])
+ env_set_hex(argv[4], mmc->ocr);
+ return CMD_RET_SUCCESS;
+ }
+ if (!strcmp(argv[2], "rca")) {
+ printf("RCA: 0x%08x\n", mmc->rca);
+ if (argv[4])
+ env_set_hex(argv[4], mmc->rca);
+ return CMD_RET_SUCCESS;
+ }
+ if (!strcmp(argv[2], "extcsd") &&
+ mmc->version >= MMC_VERSION_4_41) {
+ ret = mmc_send_ext_csd(mmc, ext_csd);
+ if (ret)
+ return CMD_RET_FAILURE;
+ if (!strcmp(argv[3], "all")) {
+ /* Dump the entire register */
+ printf("EXT_CSD:");
+ for (i = 0; i < MMC_MAX_BLOCK_LEN; i++) {
+ if (!(i % 10))
+ printf("\n%03i: ", i);
+ printf(" %02x", ext_csd[i]);
+ }
+ printf("\n");
+ return CMD_RET_SUCCESS;
+ }
+ off = simple_strtoul(argv[3], NULL, 10);
+ if (off > 512)
+ return CMD_RET_USAGE;
+ printf("EXT_CSD[%i]: 0x%02x\n", off, ext_csd[off]);
+ if (argv[4])
+ env_set_hex(argv[4], ext_csd[off]);
+ return CMD_RET_SUCCESS;
+ }
+
+ return CMD_RET_FAILURE;
+}
+#endif
+
+static struct cmd_tbl cmd_mmc[] = {
+ U_BOOT_CMD_MKENT(info, 1, 0, do_mmcinfo, "", ""),
+ U_BOOT_CMD_MKENT(read, 4, 1, do_mmc_read, "", ""),
+ U_BOOT_CMD_MKENT(wp, 2, 0, do_mmc_boot_wp, "", ""),
+#if CONFIG_IS_ENABLED(MMC_WRITE)
+ U_BOOT_CMD_MKENT(write, 4, 0, do_mmc_write, "", ""),
+ U_BOOT_CMD_MKENT(erase, 3, 0, do_mmc_erase, "", ""),
+#endif
+#if CONFIG_IS_ENABLED(CMD_MMC_SWRITE)
+ U_BOOT_CMD_MKENT(swrite, 3, 0, do_mmc_sparse_write, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(rescan, 2, 1, do_mmc_rescan, "", ""),
+ U_BOOT_CMD_MKENT(part, 1, 1, do_mmc_part, "", ""),
+ U_BOOT_CMD_MKENT(dev, 4, 0, do_mmc_dev, "", ""),
+ U_BOOT_CMD_MKENT(list, 1, 1, do_mmc_list, "", ""),
+#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING)
+ U_BOOT_CMD_MKENT(hwpartition, 28, 0, do_mmc_hwpartition, "", ""),
+#endif
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+ U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""),
+ U_BOOT_CMD_MKENT(bootpart-resize, 4, 0, do_mmc_boot_resize, "", ""),
+ U_BOOT_CMD_MKENT(partconf, 5, 0, do_mmc_partconf, "", ""),
+ U_BOOT_CMD_MKENT(rst-function, 3, 0, do_mmc_rst_func, "", ""),
+#endif
+#if CONFIG_IS_ENABLED(CMD_MMC_RPMB)
+ U_BOOT_CMD_MKENT(rpmb, CONFIG_SYS_MAXARGS, 1, do_mmcrpmb, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(setdsr, 2, 0, do_mmc_setdsr, "", ""),
+#ifdef CONFIG_CMD_BKOPS_ENABLE
+ U_BOOT_CMD_MKENT(bkops-enable, 2, 0, do_mmc_bkops_enable, "", ""),
+ U_BOOT_CMD_MKENT(bkops, 4, 0, do_mmc_bkops, "", ""),
+#endif
+#if CONFIG_IS_ENABLED(CMD_MMC_REG)
+ U_BOOT_CMD_MKENT(reg, 5, 0, do_mmc_reg, "", ""),
+#endif
+};
+
+static int do_mmcops(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ cp = find_cmd_tbl(argv[1], cmd_mmc, ARRAY_SIZE(cmd_mmc));
+
+ /* Drop the mmc command */
+ argc--;
+ argv++;
+
+ if (cp == NULL || argc > cp->maxargs)
+ return CMD_RET_USAGE;
+ if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp))
+ return CMD_RET_SUCCESS;
+
+ if (curr_device < 0) {
+ if (get_mmc_num() > 0) {
+ curr_device = 0;
+ } else {
+ puts("No MMC device available\n");
+ return CMD_RET_FAILURE;
+ }
+ }
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(
+ mmc, 29, 1, do_mmcops,
+ "MMC sub system",
+ "info - display info of the current MMC device\n"
+ "mmc read addr blk# cnt\n"
+ "mmc write addr blk# cnt\n"
+#if CONFIG_IS_ENABLED(CMD_MMC_SWRITE)
+ "mmc swrite addr blk#\n"
+#endif
+ "mmc erase blk# cnt\n"
+ "mmc erase partname\n"
+ "mmc rescan [mode]\n"
+ "mmc part - lists available partition on current mmc device\n"
+ "mmc dev [dev] [part] [mode] - show or set current mmc device [partition] and set mode\n"
+ " - the required speed mode is passed as the index from the following list\n"
+ " [MMC_LEGACY, MMC_HS, SD_HS, MMC_HS_52, MMC_DDR_52, UHS_SDR12, UHS_SDR25,\n"
+ " UHS_SDR50, UHS_DDR50, UHS_SDR104, MMC_HS_200, MMC_HS_400, MMC_HS_400_ES]\n"
+ "mmc list - lists available devices\n"
+ "mmc wp [PART] - power on write protect boot partitions\n"
+ " arguments:\n"
+ " PART - [0|1]\n"
+ " : 0 - first boot partition, 1 - second boot partition\n"
+ " if not assigned, write protect all boot partitions\n"
+#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING)
+ "mmc hwpartition <USER> <GP> <MODE> - does hardware partitioning\n"
+ " arguments (sizes in 512-byte blocks):\n"
+ " USER - <user> <enh> <start> <cnt> <wrrel> <{on|off}>\n"
+ " : sets user data area attributes\n"
+ " GP - <{gp1|gp2|gp3|gp4}> <cnt> <enh> <wrrel> <{on|off}>\n"
+ " : general purpose partition\n"
+ " MODE - <{check|set|complete}>\n"
+ " : mode, complete set partitioning completed\n"
+ " WARNING: Partitioning is a write-once setting once it is set to complete.\n"
+ " Power cycling is required to initialize partitions after set to complete.\n"
+#endif
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+ "mmc bootbus <dev> <boot_bus_width> <reset_boot_bus_width> <boot_mode>\n"
+ " - Set the BOOT_BUS_WIDTH field of the specified device\n"
+ "mmc bootpart-resize <dev> <boot part size MB> <RPMB part size MB>\n"
+ " - Change sizes of boot and RPMB partitions of specified device\n"
+ "mmc partconf <dev> [[varname] | [<boot_ack> <boot_partition> <partition_access>]]\n"
+ " - Show or change the bits of the PARTITION_CONFIG field of the specified device\n"
+ " If showing the bits, optionally store the boot_partition field into varname\n"
+ "mmc rst-function <dev> <value>\n"
+ " - Change the RST_n_FUNCTION field of the specified device\n"
+ " WARNING: This is a write-once field and 0 / 1 / 2 are the only valid values.\n"
+#endif
+#if CONFIG_IS_ENABLED(CMD_MMC_RPMB)
+ "mmc rpmb read addr blk# cnt [address of auth-key] - block size is 256 bytes\n"
+ "mmc rpmb write addr blk# cnt <address of auth-key> - block size is 256 bytes\n"
+ "mmc rpmb key <address of auth-key> - program the RPMB authentication key.\n"
+ "mmc rpmb counter - read the value of the write counter\n"
+#endif
+ "mmc setdsr <value> - set DSR register value\n"
+#ifdef CONFIG_CMD_BKOPS_ENABLE
+ "mmc bkops-enable <dev> - enable background operations handshake on device\n"
+ " WARNING: This is a write-once setting.\n"
+ "mmc bkops <dev> [auto|manual] [enable|disable]\n"
+ " - configure background operations handshake on device\n"
+#endif
+#if CONFIG_IS_ENABLED(CMD_MMC_REG)
+ "mmc reg read <reg> <offset> [env] - read card register <reg> offset <offset>\n"
+ " (optionally into [env] variable)\n"
+ " - reg: cid/csd/dsr/ocr/rca/extcsd\n"
+ " - offset: for cid/csd [0..3], for extcsd [0..511,all]\n"
+#endif
+ );
+
+/* Old command kept for compatibility. Same as 'mmc info' */
+U_BOOT_CMD(
+ mmcinfo, 1, 0, do_mmcinfo,
+ "display MMC info",
+ "- display info of the current MMC device"
+);
diff --git a/cmd/mp.c b/cmd/mp.c
new file mode 100644
index 00000000000..686e1f8a82f
--- /dev/null
+++ b/cmd/mp.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc.
+ */
+
+#include <command.h>
+#include <cpu_func.h>
+#include <vsprintf.h>
+#include <linux/string.h>
+
+static int cpu_status_all(void)
+{
+ unsigned long cpuid;
+
+ for (cpuid = 0; ; cpuid++) {
+ if (!is_core_valid(cpuid)) {
+ if (cpuid == 0) {
+ printf("Core num: %lu is not valid\n", cpuid);
+ return 1;
+ }
+ break;
+ }
+ cpu_status(cpuid);
+ }
+
+ return 0;
+}
+
+static int
+cpu_cmd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ unsigned long cpuid;
+
+ if (argc == 2 && strncmp(argv[1], "status", 6) == 0)
+ return cpu_status_all();
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ cpuid = dectoul(argv[1], NULL);
+ if (!is_core_valid(cpuid)) {
+ printf ("Core num: %lu is not valid\n", cpuid);
+ return 1;
+ }
+
+ if (argc == 3) {
+ if (strncmp(argv[2], "reset", 5) == 0)
+ cpu_reset(cpuid);
+ else if (strncmp(argv[2], "status", 6) == 0)
+ cpu_status(cpuid);
+ else if (strncmp(argv[2], "disable", 7) == 0)
+ return cpu_disable(cpuid);
+ else
+ return CMD_RET_USAGE;
+
+ return 0;
+ }
+
+ /* 4 or greater, make sure its release */
+ if (strncmp(argv[2], "release", 7) != 0)
+ return CMD_RET_USAGE;
+
+ if (cpu_release(cpuid, argc - 3, argv + 3))
+ return CMD_RET_USAGE;
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(cpu,
+ "<num> reset - Reset cpu <num>\n"
+ "cpu status - Status of all cpus\n"
+ "cpu <num> status - Status of cpu <num>\n"
+ "cpu <num> disable - Disable cpu <num>\n"
+ "cpu <num> release <addr> [args] - Release cpu <num> at <addr> with [args]"
+#ifdef CONFIG_PPC
+ "\n"
+ " [args] : <pir> <r3> <r6>\n" \
+ " pir - processor id (if writeable)\n" \
+ " r3 - value for gpr 3\n" \
+ " r6 - value for gpr 6\n" \
+ "\n" \
+ " Use '-' for any arg if you want the default value.\n" \
+ " Default for r3 is <num> and r6 is 0\n" \
+ "\n" \
+ " When cpu <num> is released r4 and r5 = 0.\n" \
+ " r7 will contain the size of the initial mapped area"
+#endif
+ );
+
+U_BOOT_CMD(
+ cpu, CONFIG_SYS_MAXARGS, 1, cpu_cmd,
+ "Multiprocessor CPU boot manipulation and release", cpu_help_text
+);
diff --git a/cmd/mtd.c b/cmd/mtd.c
new file mode 100644
index 00000000000..2520b89eed2
--- /dev/null
+++ b/cmd/mtd.c
@@ -0,0 +1,831 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * mtd.c
+ *
+ * Generic command to handle basic operations on any memory device.
+ *
+ * Copyright: Bootlin, 2018
+ * Author: Miquèl Raynal <miquel.raynal@bootlin.com>
+ */
+
+#include <command.h>
+#include <console.h>
+#include <led.h>
+#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
+#include <hexdump.h>
+#endif
+#include <malloc.h>
+#include <mapmem.h>
+#include <mtd.h>
+#include <time.h>
+#include <dm/devres.h>
+#include <linux/err.h>
+
+#include <linux/ctype.h>
+
+static struct mtd_info *get_mtd_by_name(const char *name)
+{
+ struct mtd_info *mtd;
+
+ mtd_probe_devices();
+
+ mtd = get_mtd_device_nm(name);
+ if (IS_ERR_OR_NULL(mtd))
+ printf("MTD device %s not found, ret %ld\n", name,
+ PTR_ERR(mtd));
+
+ return mtd;
+}
+
+static uint mtd_len_to_pages(struct mtd_info *mtd, u64 len)
+{
+ do_div(len, mtd->writesize);
+
+ return len;
+}
+
+static bool mtd_is_aligned_with_min_io_size(struct mtd_info *mtd, u64 size)
+{
+ return !do_div(size, mtd->writesize);
+}
+
+static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
+{
+ return !do_div(size, mtd->erasesize);
+}
+
+static void mtd_dump_buf(const u8 *buf, uint len, uint offset)
+{
+ int i, j;
+
+ for (i = 0; i < len; ) {
+ printf("0x%08x:\t", offset + i);
+ for (j = 0; j < 8; j++)
+ printf("%02x ", buf[i + j]);
+ printf(" ");
+ i += 8;
+ for (j = 0; j < 8; j++)
+ printf("%02x ", buf[i + j]);
+ printf("\n");
+ i += 8;
+ }
+}
+
+static void mtd_dump_device_buf(struct mtd_info *mtd, u64 start_off,
+ const u8 *buf, u64 len, bool woob)
+{
+ bool has_pages = mtd->type == MTD_NANDFLASH ||
+ mtd->type == MTD_MLCNANDFLASH;
+ int npages = mtd_len_to_pages(mtd, len);
+ uint page;
+
+ if (has_pages) {
+ for (page = 0; page < npages; page++) {
+ u64 data_off = (u64)page * mtd->writesize;
+
+ printf("\nDump %d data bytes from 0x%08llx:\n",
+ mtd->writesize, start_off + data_off);
+ mtd_dump_buf(&buf[data_off],
+ mtd->writesize, start_off + data_off);
+
+ if (woob) {
+ u64 oob_off = (u64)page * mtd->oobsize;
+
+ printf("Dump %d OOB bytes from page at 0x%08llx:\n",
+ mtd->oobsize, start_off + data_off);
+ mtd_dump_buf(&buf[len + oob_off],
+ mtd->oobsize, 0);
+ }
+ }
+ } else {
+ printf("\nDump %lld data bytes from 0x%llx:\n",
+ len, start_off);
+ mtd_dump_buf(buf, len, start_off);
+ }
+}
+
+static void mtd_show_parts(struct mtd_info *mtd, int level)
+{
+ struct mtd_info *part;
+ int i;
+
+ list_for_each_entry(part, &mtd->partitions, node) {
+ for (i = 0; i < level; i++)
+ printf("\t");
+ printf(" - 0x%012llx-0x%012llx : \"%s\"\n",
+ part->offset, part->offset + part->size, part->name);
+
+ mtd_show_parts(part, level + 1);
+ }
+}
+
+static void mtd_show_device(struct mtd_info *mtd)
+{
+ /* Device */
+ printf("* %s\n", mtd->name);
+ if (mtd->dev) {
+ printf(" - device: %s\n", mtd->dev->name);
+ printf(" - parent: %s\n", mtd->dev->parent->name);
+ printf(" - driver: %s\n", mtd->dev->driver->name);
+ }
+ if (IS_ENABLED(CONFIG_OF_CONTROL) && mtd->dev) {
+ char buf[256];
+ int res;
+
+ res = ofnode_get_path(mtd_get_ofnode(mtd), buf, 256);
+ printf(" - path: %s\n", res == 0 ? buf : "unavailable");
+ }
+
+ /* MTD device information */
+ printf(" - type: ");
+ switch (mtd->type) {
+ case MTD_RAM:
+ printf("RAM\n");
+ break;
+ case MTD_ROM:
+ printf("ROM\n");
+ break;
+ case MTD_NORFLASH:
+ printf("NOR flash\n");
+ break;
+ case MTD_NANDFLASH:
+ printf("NAND flash\n");
+ break;
+ case MTD_DATAFLASH:
+ printf("Data flash\n");
+ break;
+ case MTD_UBIVOLUME:
+ printf("UBI volume\n");
+ break;
+ case MTD_MLCNANDFLASH:
+ printf("MLC NAND flash\n");
+ break;
+ case MTD_ABSENT:
+ default:
+ printf("Unknown\n");
+ break;
+ }
+
+ printf(" - block size: 0x%x bytes\n", mtd->erasesize);
+ printf(" - min I/O: 0x%x bytes\n", mtd->writesize);
+
+ if (mtd->oobsize) {
+ printf(" - OOB size: %u bytes\n", mtd->oobsize);
+ printf(" - OOB available: %u bytes\n", mtd->oobavail);
+ }
+
+ if (mtd->ecc_strength) {
+ printf(" - ECC strength: %u bits\n", mtd->ecc_strength);
+ printf(" - ECC step size: %u bytes\n", mtd->ecc_step_size);
+ printf(" - bitflip threshold: %u bits\n",
+ mtd->bitflip_threshold);
+ }
+
+ printf(" - 0x%012llx-0x%012llx : \"%s\"\n",
+ mtd->offset, mtd->offset + mtd->size, mtd->name);
+
+ /* MTD partitions, if any */
+ mtd_show_parts(mtd, 1);
+}
+
+/* Logic taken from fs/ubifs/recovery.c:is_empty() */
+static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op)
+{
+ int i;
+
+ for (i = 0; i < op->len; i++)
+ if (op->datbuf[i] != 0xff)
+ return false;
+
+ for (i = 0; i < op->ooblen; i++)
+ if (op->oobbuf[i] != 0xff)
+ return false;
+
+ return true;
+}
+
+#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
+static int do_mtd_otp_read(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct mtd_info *mtd;
+ size_t retlen;
+ off_t from;
+ size_t len;
+ bool user;
+ int ret;
+ u8 *buf;
+
+ if (argc != 5)
+ return CMD_RET_USAGE;
+
+ if (!strcmp(argv[2], "u"))
+ user = true;
+ else if (!strcmp(argv[2], "f"))
+ user = false;
+ else
+ return CMD_RET_USAGE;
+
+ mtd = get_mtd_by_name(argv[1]);
+ if (IS_ERR_OR_NULL(mtd))
+ return CMD_RET_FAILURE;
+
+ from = simple_strtoul(argv[3], NULL, 0);
+ len = simple_strtoul(argv[4], NULL, 0);
+
+ ret = CMD_RET_FAILURE;
+
+ buf = malloc(len);
+ if (!buf)
+ goto put_mtd;
+
+ printf("Reading %s OTP from 0x%lx, %zu bytes\n",
+ user ? "user" : "factory", from, len);
+
+ if (user)
+ ret = mtd_read_user_prot_reg(mtd, from, len, &retlen, buf);
+ else
+ ret = mtd_read_fact_prot_reg(mtd, from, len, &retlen, buf);
+ if (ret) {
+ free(buf);
+ pr_err("OTP read failed: %d\n", ret);
+ ret = CMD_RET_FAILURE;
+ goto put_mtd;
+ }
+
+ if (retlen != len)
+ pr_err("OTP read returns %zu, but %zu expected\n",
+ retlen, len);
+
+ print_hex_dump("", 0, 16, 1, buf, retlen, true);
+
+ free(buf);
+
+ ret = CMD_RET_SUCCESS;
+
+put_mtd:
+ put_mtd_device(mtd);
+
+ return ret;
+}
+
+static int do_mtd_otp_lock(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct mtd_info *mtd;
+ off_t from;
+ size_t len;
+ int ret;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ mtd = get_mtd_by_name(argv[1]);
+ if (IS_ERR_OR_NULL(mtd))
+ return CMD_RET_FAILURE;
+
+ from = simple_strtoul(argv[2], NULL, 0);
+ len = simple_strtoul(argv[3], NULL, 0);
+
+ ret = mtd_lock_user_prot_reg(mtd, from, len);
+ if (ret) {
+ pr_err("OTP lock failed: %d\n", ret);
+ ret = CMD_RET_FAILURE;
+ goto put_mtd;
+ }
+
+ ret = CMD_RET_SUCCESS;
+
+put_mtd:
+ put_mtd_device(mtd);
+
+ return ret;
+}
+
+static int do_mtd_otp_write(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct mtd_info *mtd;
+ size_t retlen;
+ size_t binlen;
+ u8 *binbuf;
+ off_t from;
+ int ret;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ mtd = get_mtd_by_name(argv[1]);
+ if (IS_ERR_OR_NULL(mtd))
+ return CMD_RET_FAILURE;
+
+ from = simple_strtoul(argv[2], NULL, 0);
+ binlen = strlen(argv[3]) / 2;
+
+ ret = CMD_RET_FAILURE;
+ binbuf = malloc(binlen);
+ if (!binbuf)
+ goto put_mtd;
+
+ hex2bin(binbuf, argv[3], binlen);
+
+ printf("Will write:\n");
+
+ print_hex_dump("", 0, 16, 1, binbuf, binlen, true);
+
+ printf("to 0x%lx\n", from);
+
+ printf("Continue (y/n)?\n");
+
+ if (confirm_yesno() != 1) {
+ pr_err("OTP write canceled\n");
+ ret = CMD_RET_SUCCESS;
+ goto put_mtd;
+ }
+
+ ret = mtd_write_user_prot_reg(mtd, from, binlen, &retlen, binbuf);
+ if (ret) {
+ pr_err("OTP write failed: %d\n", ret);
+ ret = CMD_RET_FAILURE;
+ goto put_mtd;
+ }
+
+ if (retlen != binlen)
+ pr_err("OTP write returns %zu, but %zu expected\n",
+ retlen, binlen);
+
+ ret = CMD_RET_SUCCESS;
+
+put_mtd:
+ free(binbuf);
+ put_mtd_device(mtd);
+
+ return ret;
+}
+
+static int do_mtd_otp_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct otp_info otp_info;
+ struct mtd_info *mtd;
+ size_t retlen;
+ bool user;
+ int ret;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ if (!strcmp(argv[2], "u"))
+ user = true;
+ else if (!strcmp(argv[2], "f"))
+ user = false;
+ else
+ return CMD_RET_USAGE;
+
+ mtd = get_mtd_by_name(argv[1]);
+ if (IS_ERR_OR_NULL(mtd))
+ return CMD_RET_FAILURE;
+
+ if (user)
+ ret = mtd_get_user_prot_info(mtd, sizeof(otp_info), &retlen,
+ &otp_info);
+ else
+ ret = mtd_get_fact_prot_info(mtd, sizeof(otp_info), &retlen,
+ &otp_info);
+ if (ret) {
+ pr_err("OTP info failed: %d\n", ret);
+ ret = CMD_RET_FAILURE;
+ goto put_mtd;
+ }
+
+ if (retlen != sizeof(otp_info)) {
+ pr_err("OTP info returns %zu, but %zu expected\n",
+ retlen, sizeof(otp_info));
+ ret = CMD_RET_FAILURE;
+ goto put_mtd;
+ }
+
+ printf("%s OTP region info:\n", user ? "User" : "Factory");
+ printf("\tstart: %u\n", otp_info.start);
+ printf("\tlength: %u\n", otp_info.length);
+ printf("\tlocked: %u\n", otp_info.locked);
+
+ ret = CMD_RET_SUCCESS;
+
+put_mtd:
+ put_mtd_device(mtd);
+
+ return ret;
+}
+#endif
+
+static int do_mtd_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct mtd_info *mtd;
+ int dev_nb = 0;
+
+ /* Ensure all devices (and their partitions) are probed */
+ mtd_probe_devices();
+
+ printf("List of MTD devices:\n");
+ mtd_for_each_device(mtd) {
+ if (!mtd_is_partition(mtd))
+ mtd_show_device(mtd);
+
+ dev_nb++;
+ }
+
+ if (!dev_nb) {
+ printf("No MTD device found\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int mtd_special_write_oob(struct mtd_info *mtd, u64 off,
+ struct mtd_oob_ops *io_op,
+ bool write_empty_pages, bool woob)
+{
+ int ret = 0;
+
+ /*
+ * By default, do not write an empty page.
+ * Skip it by simulating a successful write.
+ */
+ if (!write_empty_pages && mtd_oob_write_is_empty(io_op)) {
+ io_op->retlen = mtd->writesize;
+ io_op->oobretlen = woob ? mtd->oobsize : 0;
+ } else {
+ ret = mtd_write_oob(mtd, off, io_op);
+ }
+
+ return ret;
+}
+
+static int do_mtd_io(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool dump, read, raw, woob, benchmark, write_empty_pages, has_pages = false;
+ u64 start_off, off, len, remaining, default_len;
+ unsigned long bench_start, bench_end;
+ struct mtd_oob_ops io_op = {};
+ uint user_addr = 0, npages;
+ const char *cmd = argv[0];
+ struct mtd_info *mtd;
+ u32 oob_len;
+ u8 *buf;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ mtd = get_mtd_by_name(argv[1]);
+ if (IS_ERR_OR_NULL(mtd))
+ return CMD_RET_FAILURE;
+
+ if (mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH)
+ has_pages = true;
+
+ dump = !strncmp(cmd, "dump", 4);
+ read = dump || !strncmp(cmd, "read", 4);
+ raw = strstr(cmd, ".raw");
+ woob = strstr(cmd, ".oob");
+ benchmark = strstr(cmd, ".benchmark");
+ write_empty_pages = !has_pages || strstr(cmd, ".dontskipff");
+
+ argc -= 2;
+ argv += 2;
+
+ if (!dump) {
+ if (!argc) {
+ ret = CMD_RET_USAGE;
+ goto out_put_mtd;
+ }
+
+ user_addr = hextoul(argv[0], NULL);
+ argc--;
+ argv++;
+ }
+
+ start_off = argc > 0 ? hextoul(argv[0], NULL) : 0;
+ if (!mtd_is_aligned_with_min_io_size(mtd, start_off)) {
+ printf("Offset not aligned with a page (0x%x)\n",
+ mtd->writesize);
+ ret = CMD_RET_FAILURE;
+ goto out_put_mtd;
+ }
+
+ default_len = dump ? mtd->writesize : mtd->size;
+ len = argc > 1 ? hextoul(argv[1], NULL) : default_len;
+ if (!mtd_is_aligned_with_min_io_size(mtd, len)) {
+ len = round_up(len, mtd->writesize);
+ printf("Size not on a page boundary (0x%x), rounding to 0x%llx\n",
+ mtd->writesize, len);
+ }
+
+ remaining = len;
+ npages = mtd_len_to_pages(mtd, len);
+ oob_len = woob ? npages * mtd->oobsize : 0;
+
+ if (dump)
+ buf = kmalloc(len + oob_len, GFP_KERNEL);
+ else
+ buf = map_sysmem(user_addr, 0);
+
+ if (!buf) {
+ printf("Could not map/allocate the user buffer\n");
+ ret = CMD_RET_FAILURE;
+ goto out_put_mtd;
+ }
+
+ if (has_pages)
+ printf("%s %lld byte(s) (%d page(s)) at offset 0x%08llx%s%s%s\n",
+ read ? "Reading" : "Writing", len, npages, start_off,
+ raw ? " [raw]" : "", woob ? " [oob]" : "",
+ !read && write_empty_pages ? " [dontskipff]" : "");
+ else
+ printf("%s %lld byte(s) at offset 0x%08llx\n",
+ read ? "Reading" : "Writing", len, start_off);
+
+ io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB;
+ io_op.len = has_pages ? mtd->writesize : len;
+ io_op.ooblen = woob ? mtd->oobsize : 0;
+ io_op.datbuf = buf;
+ io_op.oobbuf = woob ? &buf[len] : NULL;
+
+ /* Search for the first good block after the given offset */
+ off = start_off;
+ while (mtd_block_isbad(mtd, off))
+ off += mtd->erasesize;
+
+ led_activity_blink();
+
+ if (benchmark)
+ bench_start = timer_get_us();
+
+ /* Loop over the pages to do the actual read/write */
+ while (remaining) {
+ /* Skip the block if it is bad */
+ if (mtd_is_aligned_with_block_size(mtd, off) &&
+ mtd_block_isbad(mtd, off)) {
+ off += mtd->erasesize;
+ continue;
+ }
+
+ if (read)
+ ret = mtd_read_oob(mtd, off, &io_op);
+ else
+ ret = mtd_special_write_oob(mtd, off, &io_op,
+ write_empty_pages, woob);
+
+ if (ret) {
+ printf("Failure while %s at offset 0x%llx\n",
+ read ? "reading" : "writing", off);
+ break;
+ }
+
+ off += io_op.retlen;
+ remaining -= io_op.retlen;
+ io_op.datbuf += io_op.retlen;
+ io_op.oobbuf += io_op.oobretlen;
+ }
+
+ if (benchmark && bench_start) {
+ bench_end = timer_get_us();
+ printf("%s speed: %lukiB/s\n",
+ read ? "Read" : "Write",
+ ((io_op.len * 1000000) / (bench_end - bench_start)) / 1024);
+ }
+
+ led_activity_off();
+
+ if (!ret && dump)
+ mtd_dump_device_buf(mtd, start_off, buf, len, woob);
+
+ if (dump)
+ kfree(buf);
+ else
+ unmap_sysmem(buf);
+
+ if (ret) {
+ printf("%s on %s failed with error %d\n",
+ read ? "Read" : "Write", mtd->name, ret);
+ ret = CMD_RET_FAILURE;
+ } else {
+ ret = CMD_RET_SUCCESS;
+ }
+
+out_put_mtd:
+ put_mtd_device(mtd);
+
+ return ret;
+}
+
+static int do_mtd_erase(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct erase_info erase_op = {};
+ struct mtd_info *mtd;
+ u64 off, len;
+ bool scrub;
+ int ret = 0;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ mtd = get_mtd_by_name(argv[1]);
+ if (IS_ERR_OR_NULL(mtd))
+ return CMD_RET_FAILURE;
+
+ scrub = strstr(argv[0], ".dontskipbad");
+
+ argc -= 2;
+ argv += 2;
+
+ off = argc > 0 ? hextoul(argv[0], NULL) : 0;
+ len = argc > 1 ? hextoul(argv[1], NULL) : mtd->size;
+
+ if (!mtd_is_aligned_with_block_size(mtd, off)) {
+ printf("Offset not aligned with a block (0x%x)\n",
+ mtd->erasesize);
+ ret = CMD_RET_FAILURE;
+ goto out_put_mtd;
+ }
+
+ if (!mtd_is_aligned_with_block_size(mtd, len)) {
+ printf("Size not a multiple of a block (0x%x)\n",
+ mtd->erasesize);
+ ret = CMD_RET_FAILURE;
+ goto out_put_mtd;
+ }
+
+ printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\n",
+ off, off + len - 1, mtd_div_by_eb(len, mtd));
+
+ erase_op.mtd = mtd;
+ erase_op.addr = off;
+ erase_op.len = mtd->erasesize;
+
+ led_activity_blink();
+
+ while (len) {
+ if (!scrub) {
+ ret = mtd_block_isbad(mtd, erase_op.addr);
+ if (ret < 0) {
+ printf("Failed to get bad block at 0x%08llx\n",
+ erase_op.addr);
+ ret = CMD_RET_FAILURE;
+ goto out_put_mtd;
+ }
+
+ if (ret > 0) {
+ printf("Skipping bad block at 0x%08llx\n",
+ erase_op.addr);
+ ret = 0;
+ len -= mtd->erasesize;
+ erase_op.addr += mtd->erasesize;
+ continue;
+ }
+ }
+
+ ret = mtd_erase(mtd, &erase_op);
+ if (ret && ret != -EIO)
+ break;
+
+ len -= mtd->erasesize;
+ erase_op.addr += mtd->erasesize;
+ }
+
+ led_activity_off();
+
+ if (ret && ret != -EIO)
+ ret = CMD_RET_FAILURE;
+ else
+ ret = CMD_RET_SUCCESS;
+
+out_put_mtd:
+ put_mtd_device(mtd);
+
+ return ret;
+}
+
+static int do_mtd_bad(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct mtd_info *mtd;
+ loff_t off;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ mtd = get_mtd_by_name(argv[1]);
+ if (IS_ERR_OR_NULL(mtd))
+ return CMD_RET_FAILURE;
+
+ if (!mtd_can_have_bb(mtd)) {
+ printf("Only NAND-based devices can have bad blocks\n");
+ goto out_put_mtd;
+ }
+
+ printf("MTD device %s bad blocks list:\n", mtd->name);
+ for (off = 0; off < mtd->size; off += mtd->erasesize) {
+ if (mtd_block_isbad(mtd, off))
+ printf("\t0x%08llx\n", off);
+ }
+
+out_put_mtd:
+ put_mtd_device(mtd);
+
+ return CMD_RET_SUCCESS;
+}
+
+#ifdef CONFIG_AUTO_COMPLETE
+static int mtd_name_complete(int argc, char *const argv[], char last_char,
+ int maxv, char *cmdv[])
+{
+ int len = 0, n_found = 0;
+ struct mtd_info *mtd;
+
+ argc--;
+ argv++;
+
+ if (argc > 1 ||
+ (argc == 1 && (last_char == '\0' || isblank(last_char))))
+ return 0;
+
+ if (argc)
+ len = strlen(argv[0]);
+
+ mtd_for_each_device(mtd) {
+ if (argc &&
+ (len > strlen(mtd->name) ||
+ strncmp(argv[0], mtd->name, len)))
+ continue;
+
+ if (n_found >= maxv - 2) {
+ cmdv[n_found++] = "...";
+ break;
+ }
+
+ cmdv[n_found++] = mtd->name;
+ }
+
+ cmdv[n_found] = NULL;
+
+ return n_found;
+}
+#endif /* CONFIG_AUTO_COMPLETE */
+
+U_BOOT_LONGHELP(mtd,
+ "- generic operations on memory technology devices\n\n"
+ "mtd list\n"
+ "mtd read[.raw][.oob] <name> <addr> [<off> [<size>]]\n"
+ "mtd dump[.raw][.oob] <name> [<off> [<size>]]\n"
+ "mtd write[.raw][.oob][.dontskipff] <name> <addr> [<off> [<size>]]\n"
+ "mtd erase[.dontskipbad] <name> [<off> [<size>]]\n"
+ "\n"
+ "Specific functions:\n"
+ "mtd bad <name>\n"
+#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
+ "mtd otpread <name> [u|f] <off> <size>\n"
+ "mtd otpwrite <name> <off> <hex string>\n"
+ "mtd otplock <name> <off> <size>\n"
+ "mtd otpinfo <name> [u|f]\n"
+#endif
+ "\n"
+ "With:\n"
+ "\t<name>: NAND partition/chip name (or corresponding DM device name or OF path)\n"
+ "\t<addr>: user address from/to which data will be retrieved/stored\n"
+ "\t<off>: offset in <name> in bytes (default: start of the part)\n"
+ "\t\t* must be block-aligned for erase\n"
+ "\t\t* must be page-aligned otherwise\n"
+ "\t<size>: length of the operation in bytes (default: the entire device)\n"
+ "\t\t* must be a multiple of a block for erase\n"
+ "\t\t* must be a multiple of a page otherwise (special case: default is a page with dump)\n"
+#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
+ "\t<hex string>: hex string without '0x' and spaces. Example: ABCD1234\n"
+ "\t[u|f]: user or factory OTP region\n"
+#endif
+ "\n"
+ "The .dontskipff option forces writing empty pages, don't use it if unsure.\n");
+
+U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text,
+#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
+ U_BOOT_SUBCMD_MKENT(otpread, 5, 1, do_mtd_otp_read),
+ U_BOOT_SUBCMD_MKENT(otpwrite, 4, 1, do_mtd_otp_write),
+ U_BOOT_SUBCMD_MKENT(otplock, 4, 1, do_mtd_otp_lock),
+ U_BOOT_SUBCMD_MKENT(otpinfo, 3, 1, do_mtd_otp_info),
+#endif
+ U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mtd_list),
+ U_BOOT_SUBCMD_MKENT_COMPLETE(read, 5, 0, do_mtd_io,
+ mtd_name_complete),
+ U_BOOT_SUBCMD_MKENT_COMPLETE(write, 5, 0, do_mtd_io,
+ mtd_name_complete),
+ U_BOOT_SUBCMD_MKENT_COMPLETE(dump, 4, 0, do_mtd_io,
+ mtd_name_complete),
+ U_BOOT_SUBCMD_MKENT_COMPLETE(erase, 4, 0, do_mtd_erase,
+ mtd_name_complete),
+ U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad,
+ mtd_name_complete));
diff --git a/cmd/mtdparts.c b/cmd/mtdparts.c
new file mode 100644
index 00000000000..571b79f091d
--- /dev/null
+++ b/cmd/mtdparts.c
@@ -0,0 +1,2126 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2002
+ * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de>
+ *
+ * (C) Copyright 2003
+ * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de>
+ *
+ * (C) Copyright 2005
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * Added support for reading flash partition table from environment.
+ * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4
+ * kernel tree.
+ *
+ * (C) Copyright 2008
+ * Harald Welte, OpenMoko, Inc., Harald Welte <laforge@openmoko.org>
+ *
+ * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $
+ * Copyright 2002 SYSGO Real-Time Solutions GmbH
+ */
+
+/*
+ * Three environment variables are used by the parsing routines:
+ *
+ * 'partition' - keeps current partition identifier
+ *
+ * partition := <part-id>
+ * <part-id> := <dev-id>,part_num
+ *
+ *
+ * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping
+ *
+ * mtdids=<idmap>[,<idmap>,...]
+ *
+ * <idmap> := <dev-id>=<mtd-id>
+ * <dev-id> := 'nand'|'nor'|'onenand'|'spi-nand'<dev-num>
+ * <dev-num> := mtd device number, 0...
+ * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)
+ *
+ *
+ * 'mtdparts' - partition list
+ *
+ * mtdparts=[mtdparts=]<mtd-def>[;<mtd-def>...]
+ *
+ * <mtd-def> := <mtd-id>:<part-def>[,<part-def>...]
+ * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)
+ * <part-def> := <size>[@<offset>][<name>][<ro-flag>]
+ * <size> := standard linux memsize OR '-' to denote all remaining space
+ * <offset> := partition start offset within the device
+ * <name> := '(' NAME ')'
+ * <ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)
+ *
+ * Notes:
+ * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping
+ * - if the above variables are not set defaults for a given target are used
+ *
+ * Examples:
+ *
+ * 1 NOR Flash, with 1 single writable partition:
+ * mtdids=nor0=edb7312-nor
+ * mtdparts=[mtdparts=]edb7312-nor:-
+ *
+ * 1 NOR Flash with 2 partitions, 1 NAND with one
+ * mtdids=nor0=edb7312-nor,nand0=edb7312-nand
+ * mtdparts=[mtdparts=]edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
+ *
+ */
+
+#include <command.h>
+#include <env.h>
+#include <log.h>
+#include <malloc.h>
+#include <mtd.h>
+#include <asm/global_data.h>
+#include <jffs2/load_kernel.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+
+#if defined(CONFIG_CMD_NAND)
+#include <linux/mtd/rawnand.h>
+#include <nand.h>
+#endif
+
+#if defined(CONFIG_CMD_ONENAND)
+#include <linux/mtd/onenand.h>
+#include <onenand_uboot.h>
+#endif
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* special size referring to all the remaining space in a partition */
+#define SIZE_REMAINING (~0llu)
+
+/* special offset value, it is used when not provided by user
+ *
+ * this value is used temporarily during parsing, later such offests
+ * are recalculated */
+#define OFFSET_NOT_SPECIFIED (~0llu)
+
+/* minimum partition size */
+#define MIN_PART_SIZE 4096
+
+/* this flag needs to be set in part_info struct mask_flags
+ * field for read-only partitions */
+#define MTD_WRITEABLE_CMD 1
+
+/* default values for mtdids and mtdparts variables */
+#ifdef CONFIG_MTDIDS_DEFAULT
+#define MTDIDS_DEFAULT CONFIG_MTDIDS_DEFAULT
+#else
+#define MTDIDS_DEFAULT NULL
+#endif
+#ifdef CONFIG_MTDPARTS_DEFAULT
+#define MTDPARTS_DEFAULT CONFIG_MTDPARTS_DEFAULT
+#else
+#define MTDPARTS_DEFAULT NULL
+#endif
+
+#if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
+extern void board_mtdparts_default(const char **mtdids, const char **mtdparts);
+#endif
+static const char *mtdids_default = MTDIDS_DEFAULT;
+static const char *mtdparts_default = MTDPARTS_DEFAULT;
+
+/* copies of last seen 'mtdids', 'mtdparts' and 'partition' env variables */
+#define MTDIDS_MAXLEN 128
+#define MTDPARTS_MAXLEN 512
+#define PARTITION_MAXLEN 16
+static char last_ids[MTDIDS_MAXLEN + 1];
+static char last_parts[MTDPARTS_MAXLEN + 1];
+static char last_partition[PARTITION_MAXLEN + 1];
+
+/* low level jffs2 cache cleaning routine */
+extern void jffs2_free_cache(struct part_info *part);
+
+/* mtdids mapping list, filled by parse_ids() */
+static struct list_head mtdids;
+
+/* device/partition list, parse_cmdline() parses into here */
+static struct list_head devices;
+
+/* current active device and partition number */
+struct mtd_device *current_mtd_dev = NULL;
+u8 current_mtd_partnum = 0;
+
+u8 use_defaults;
+
+static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num);
+
+/* command line only routines */
+static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len);
+static int device_del(struct mtd_device *dev);
+
+/**
+ * Parses a string into a number. The number stored at ptr is
+ * potentially suffixed with K (for kilobytes, or 1024 bytes),
+ * M (for megabytes, or 1048576 bytes), or G (for gigabytes, or
+ * 1073741824). If the number is suffixed with K, M, or G, then
+ * the return value is the number multiplied by one kilobyte, one
+ * megabyte, or one gigabyte, respectively.
+ *
+ * @param ptr where parse begins
+ * @param retptr output pointer to next char after parse completes (output)
+ * Return: resulting unsigned int
+ */
+static u64 memsize_parse (const char *const ptr, const char **retptr)
+{
+ u64 ret = simple_strtoull(ptr, (char **)retptr, 0);
+
+ switch (**retptr) {
+ case 'G':
+ case 'g':
+ ret <<= 10;
+ /* Fallthrough */
+ case 'M':
+ case 'm':
+ ret <<= 10;
+ /* Fallthrough */
+ case 'K':
+ case 'k':
+ ret <<= 10;
+ (*retptr)++;
+ /* Fallthrough */
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * Format string describing supplied size. This routine does the opposite job
+ * to memsize_parse(). Size in bytes is converted to string and if possible
+ * shortened by using k (kilobytes), m (megabytes) or g (gigabytes) suffix.
+ *
+ * Note, that this routine does not check for buffer overflow, it's the caller
+ * who must assure enough space.
+ *
+ * @param buf output buffer
+ * @param size size to be converted to string
+ */
+static void memsize_format(char *buf, u64 size)
+{
+#define SIZE_GB ((u32)1024*1024*1024)
+#define SIZE_MB ((u32)1024*1024)
+#define SIZE_KB ((u32)1024)
+
+ if ((size % SIZE_GB) == 0)
+ sprintf(buf, "%llug", size/SIZE_GB);
+ else if ((size % SIZE_MB) == 0)
+ sprintf(buf, "%llum", size/SIZE_MB);
+ else if (size % SIZE_KB == 0)
+ sprintf(buf, "%lluk", size/SIZE_KB);
+ else
+ sprintf(buf, "%llu", size);
+}
+
+/**
+ * This routine does global indexing of all partitions. Resulting index for
+ * current partition is saved in 'mtddevnum'. Current partition name in
+ * 'mtddevname'.
+ */
+static void index_partitions(void)
+{
+ u16 mtddevnum;
+ struct part_info *part;
+ struct list_head *dentry;
+ struct mtd_device *dev;
+
+ debug("--- index partitions ---\n");
+
+ if (current_mtd_dev) {
+ mtddevnum = 0;
+ list_for_each(dentry, &devices) {
+ dev = list_entry(dentry, struct mtd_device, link);
+ if (dev == current_mtd_dev) {
+ mtddevnum += current_mtd_partnum;
+ env_set_ulong("mtddevnum", mtddevnum);
+ debug("=> mtddevnum %d,\n", mtddevnum);
+ break;
+ }
+ mtddevnum += dev->num_parts;
+ }
+
+ part = mtd_part_info(current_mtd_dev, current_mtd_partnum);
+ if (part) {
+ env_set("mtddevname", part->name);
+
+ debug("=> mtddevname %s\n", part->name);
+ } else {
+ env_set("mtddevname", NULL);
+
+ debug("=> mtddevname NULL\n");
+ }
+ } else {
+ env_set("mtddevnum", NULL);
+ env_set("mtddevname", NULL);
+
+ debug("=> mtddevnum NULL\n=> mtddevname NULL\n");
+ }
+}
+
+/**
+ * Save current device and partition in environment variable 'partition'.
+ */
+static void current_save(void)
+{
+ char buf[16];
+
+ debug("--- current_save ---\n");
+
+ if (current_mtd_dev) {
+ sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_mtd_dev->id->type),
+ current_mtd_dev->id->num, current_mtd_partnum);
+
+ env_set("partition", buf);
+ strncpy(last_partition, buf, 16);
+
+ debug("=> partition %s\n", buf);
+ } else {
+ env_set("partition", NULL);
+ last_partition[0] = '\0';
+
+ debug("=> partition NULL\n");
+ }
+ index_partitions();
+}
+
+/**
+ * Produce a mtd_info given a type and num.
+ *
+ * @param type mtd type
+ * @param num mtd number
+ * @param mtd a pointer to an mtd_info instance (output)
+ * Return: 0 if device is valid, 1 otherwise
+ */
+static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd)
+{
+ char mtd_dev[16];
+
+ sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num);
+ *mtd = get_mtd_device_nm(mtd_dev);
+ if (IS_ERR(*mtd)) {
+ printf("Device %s not found!\n", mtd_dev);
+ return 1;
+ }
+ put_mtd_device(*mtd);
+
+ return 0;
+}
+
+/**
+ * Performs sanity check for supplied flash partition.
+ * Table of existing MTD flash devices is searched and partition device
+ * is located. Alignment with the granularity of nand erasesize is verified.
+ *
+ * @param id of the parent device
+ * @param part partition to validate
+ * Return: 0 if partition is valid, 1 otherwise
+ */
+static int part_validate_eraseblock(struct mtdids *id, struct part_info *part)
+{
+ struct mtd_info *mtd = NULL;
+ int i, j;
+ ulong start;
+ u64 offset, size;
+
+ if (get_mtd_info(id->type, id->num, &mtd))
+ return 1;
+
+ part->sector_size = mtd->erasesize;
+
+ if (!mtd->numeraseregions) {
+ /*
+ * Only one eraseregion (NAND, SPI-NAND, OneNAND or uniform NOR),
+ * checking for alignment is easy here
+ */
+ offset = part->offset;
+ if (do_div(offset, mtd->erasesize)) {
+ printf("%s%d: partition (%s) start offset"
+ "alignment incorrect\n",
+ MTD_DEV_TYPE(id->type), id->num, part->name);
+ return 1;
+ }
+
+ size = part->size;
+ if (do_div(size, mtd->erasesize)) {
+ printf("%s%d: partition (%s) size alignment incorrect\n",
+ MTD_DEV_TYPE(id->type), id->num, part->name);
+ return 1;
+ }
+ } else {
+ /*
+ * Multiple eraseregions (non-uniform NOR),
+ * checking for alignment is more complex here
+ */
+
+ /* Check start alignment */
+ for (i = 0; i < mtd->numeraseregions; i++) {
+ start = mtd->eraseregions[i].offset;
+ for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
+ if (part->offset == start)
+ goto start_ok;
+ start += mtd->eraseregions[i].erasesize;
+ }
+ }
+
+ printf("%s%d: partition (%s) start offset alignment incorrect\n",
+ MTD_DEV_TYPE(id->type), id->num, part->name);
+ return 1;
+
+ start_ok:
+
+ /* Check end/size alignment */
+ for (i = 0; i < mtd->numeraseregions; i++) {
+ start = mtd->eraseregions[i].offset;
+ for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
+ if ((part->offset + part->size) == start)
+ goto end_ok;
+ start += mtd->eraseregions[i].erasesize;
+ }
+ }
+ /* Check last sector alignment */
+ if ((part->offset + part->size) == start)
+ goto end_ok;
+
+ printf("%s%d: partition (%s) size alignment incorrect\n",
+ MTD_DEV_TYPE(id->type), id->num, part->name);
+ return 1;
+
+ end_ok:
+ return 0;
+ }
+
+ return 0;
+}
+
+/**
+ * Performs sanity check for supplied partition. Offset and size are
+ * verified to be within valid range. Partition type is checked and
+ * part_validate_eraseblock() is called with the argument of part.
+ *
+ * @param id of the parent device
+ * @param part partition to validate
+ * Return: 0 if partition is valid, 1 otherwise
+ */
+static int part_validate(struct mtdids *id, struct part_info *part)
+{
+ if (part->size == SIZE_REMAINING)
+ part->size = id->size - part->offset;
+
+ if (part->offset > id->size) {
+ printf("%s: offset %08llx beyond flash size %08llx\n",
+ id->mtd_id, part->offset, id->size);
+ return 1;
+ }
+
+ if ((part->offset + part->size) <= part->offset) {
+ printf("%s%d: partition (%s) size too big\n",
+ MTD_DEV_TYPE(id->type), id->num, part->name);
+ return 1;
+ }
+
+ if (part->offset + part->size > id->size) {
+ printf("%s: partitioning exceeds flash size\n", id->mtd_id);
+ return 1;
+ }
+
+ /*
+ * Now we need to check if the partition starts and ends on
+ * sector (eraseblock) regions
+ */
+ return part_validate_eraseblock(id, part);
+}
+
+/**
+ * Delete selected partition from the partition list of the specified device.
+ *
+ * @param dev device to delete partition from
+ * @param part partition to delete
+ * Return: 0 on success, 1 otherwise
+ */
+static int part_del(struct mtd_device *dev, struct part_info *part)
+{
+ u8 current_save_needed = 0;
+
+ /* if there is only one partition, remove whole device */
+ if (dev->num_parts == 1)
+ return device_del(dev);
+
+ /* otherwise just delete this partition */
+
+ if (dev == current_mtd_dev) {
+ /* we are modyfing partitions for the current device,
+ * update current */
+ struct part_info *curr_pi;
+ curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum);
+
+ if (curr_pi) {
+ if (curr_pi == part) {
+ printf("current partition deleted, resetting current to 0\n");
+ current_mtd_partnum = 0;
+ } else if (part->offset <= curr_pi->offset) {
+ current_mtd_partnum--;
+ }
+ current_save_needed = 1;
+ }
+ }
+
+ list_del(&part->link);
+ free(part);
+ dev->num_parts--;
+
+ if (current_save_needed > 0)
+ current_save();
+ else
+ index_partitions();
+
+ return 0;
+}
+
+/**
+ * Delete all partitions from parts head list, free memory.
+ *
+ * @param head list of partitions to delete
+ */
+static void part_delall(struct list_head *head)
+{
+ struct list_head *entry, *n;
+ struct part_info *part_tmp;
+
+ /* clean tmp_list and free allocated memory */
+ list_for_each_safe(entry, n, head) {
+ part_tmp = list_entry(entry, struct part_info, link);
+
+ list_del(entry);
+ free(part_tmp);
+ }
+}
+
+/**
+ * Add new partition to the supplied partition list. Make sure partitions are
+ * sorted by offset in ascending order.
+ *
+ * @param head list this partition is to be added to
+ * @param new partition to be added
+ */
+static int part_sort_add(struct mtd_device *dev, struct part_info *part)
+{
+ struct list_head *entry;
+ struct part_info *new_pi, *curr_pi;
+
+ /* link partition to parrent dev */
+ part->dev = dev;
+
+ if (list_empty(&dev->parts)) {
+ debug("part_sort_add: list empty\n");
+ list_add(&part->link, &dev->parts);
+ dev->num_parts++;
+ index_partitions();
+ return 0;
+ }
+
+ new_pi = list_entry(&part->link, struct part_info, link);
+
+ /* get current partition info if we are updating current device */
+ curr_pi = NULL;
+ if (dev == current_mtd_dev)
+ curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum);
+
+ list_for_each(entry, &dev->parts) {
+ struct part_info *pi;
+
+ pi = list_entry(entry, struct part_info, link);
+
+ /* be compliant with kernel cmdline, allow only one partition at offset zero */
+ if ((new_pi->offset == pi->offset) && (pi->offset == 0)) {
+ printf("cannot add second partition at offset 0\n");
+ return 1;
+ }
+
+ if (new_pi->offset <= pi->offset) {
+ list_add_tail(&part->link, entry);
+ dev->num_parts++;
+
+ if (curr_pi && (pi->offset <= curr_pi->offset)) {
+ /* we are modyfing partitions for the current
+ * device, update current */
+ current_mtd_partnum++;
+ current_save();
+ } else {
+ index_partitions();
+ }
+ return 0;
+ }
+ }
+
+ list_add_tail(&part->link, &dev->parts);
+ dev->num_parts++;
+ index_partitions();
+ return 0;
+}
+
+/**
+ * Add provided partition to the partition list of a given device.
+ *
+ * @param dev device to which partition is added
+ * @param part partition to be added
+ * Return: 0 on success, 1 otherwise
+ */
+static int part_add(struct mtd_device *dev, struct part_info *part)
+{
+ /* verify alignment and size */
+ if (part_validate(dev->id, part) != 0)
+ return 1;
+
+ /* partition is ok, add it to the list */
+ if (part_sort_add(dev, part) != 0)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * Parse one partition definition, allocate memory and return pointer to this
+ * location in retpart.
+ *
+ * @param partdef pointer to the partition definition string i.e. <part-def>
+ * @param ret output pointer to next char after parse completes (output)
+ * @param retpart pointer to the allocated partition (output)
+ * Return: 0 on success, 1 otherwise
+ */
+static int part_parse(const char *const partdef, const char **ret, struct part_info **retpart)
+{
+ struct part_info *part;
+ u64 size;
+ u64 offset;
+ const char *name;
+ int name_len;
+ unsigned int mask_flags;
+ const char *p;
+
+ p = partdef;
+ *retpart = NULL;
+ *ret = NULL;
+
+ /* fetch the partition size */
+ if (*p == '-') {
+ /* assign all remaining space to this partition */
+ debug("'-': remaining size assigned\n");
+ size = SIZE_REMAINING;
+ p++;
+ } else {
+ size = memsize_parse(p, &p);
+ if (size < MIN_PART_SIZE) {
+ printf("partition size too small (%llx)\n", size);
+ return 1;
+ }
+ }
+
+ /* check for offset */
+ offset = OFFSET_NOT_SPECIFIED;
+ if (*p == '@') {
+ p++;
+ offset = memsize_parse(p, &p);
+ }
+
+ /* now look for the name */
+ if (*p == '(') {
+ name = ++p;
+ if ((p = strchr(name, ')')) == NULL) {
+ printf("no closing ) found in partition name\n");
+ return 1;
+ }
+ name_len = p - name + 1;
+ if ((name_len - 1) == 0) {
+ printf("empty partition name\n");
+ return 1;
+ }
+ p++;
+ } else {
+ /* 0x00000000@0x00000000 */
+ name_len = 22;
+ name = NULL;
+ }
+
+ /* test for options */
+ mask_flags = 0;
+ if (strncmp(p, "ro", 2) == 0) {
+ mask_flags |= MTD_WRITEABLE_CMD;
+ p += 2;
+ }
+
+ /* check for next partition definition */
+ if (*p == ',') {
+ if (size == SIZE_REMAINING) {
+ *ret = NULL;
+ printf("no partitions allowed after a fill-up partition\n");
+ return 1;
+ }
+ *ret = ++p;
+ } else if ((*p == ';') || (*p == '\0')) {
+ *ret = p;
+ } else {
+ printf("unexpected character '%c' at the end of partition\n", *p);
+ *ret = NULL;
+ return 1;
+ }
+
+ /* allocate memory */
+ part = (struct part_info *)malloc(sizeof(struct part_info) + name_len);
+ if (!part) {
+ printf("out of memory\n");
+ return 1;
+ }
+ memset(part, 0, sizeof(struct part_info) + name_len);
+ part->size = size;
+ part->offset = offset;
+ part->mask_flags = mask_flags;
+ part->name = (char *)(part + 1);
+
+ if (name) {
+ /* copy user provided name */
+ strncpy(part->name, name, name_len - 1);
+ part->auto_name = 0;
+ } else {
+ /* auto generated name in form of size@offset */
+ snprintf(part->name, name_len, "0x%08llx@0x%08llx", size, offset);
+ part->auto_name = 1;
+ }
+
+ part->name[name_len - 1] = '\0';
+ INIT_LIST_HEAD(&part->link);
+
+ debug("+ partition: name %-22s size 0x%08llx offset 0x%08llx mask flags %d\n",
+ part->name, part->size,
+ part->offset, part->mask_flags);
+
+ *retpart = part;
+ return 0;
+}
+
+/**
+ * Check device number to be within valid range for given device type.
+ *
+ * @param type mtd type
+ * @param num mtd number
+ * @param size a pointer to the size of the mtd device (output)
+ * Return: 0 if device is valid, 1 otherwise
+ */
+static int mtd_device_validate(u8 type, u8 num, u64 *size)
+{
+ struct mtd_info *mtd = NULL;
+
+ if (get_mtd_info(type, num, &mtd))
+ return 1;
+
+ *size = mtd->size;
+
+ return 0;
+}
+
+/**
+ * Delete all mtd devices from a supplied devices list, free memory allocated for
+ * each device and delete all device partitions.
+ *
+ * Return: 0 on success, 1 otherwise
+ */
+static int device_delall(struct list_head *head)
+{
+ struct list_head *entry, *n;
+ struct mtd_device *dev_tmp;
+
+ /* clean devices list */
+ list_for_each_safe(entry, n, head) {
+ dev_tmp = list_entry(entry, struct mtd_device, link);
+ list_del(entry);
+ part_delall(&dev_tmp->parts);
+ free(dev_tmp);
+ }
+ INIT_LIST_HEAD(&devices);
+
+ return 0;
+}
+
+/**
+ * If provided device exists it's partitions are deleted, device is removed
+ * from device list and device memory is freed.
+ *
+ * @param dev device to be deleted
+ * Return: 0 on success, 1 otherwise
+ */
+static int device_del(struct mtd_device *dev)
+{
+ part_delall(&dev->parts);
+ list_del(&dev->link);
+ free(dev);
+
+ if (dev == current_mtd_dev) {
+ /* we just deleted current device */
+ if (list_empty(&devices)) {
+ current_mtd_dev = NULL;
+ } else {
+ /* reset first partition from first dev from the
+ * devices list as current */
+ current_mtd_dev = list_entry(devices.next, struct mtd_device, link);
+ current_mtd_partnum = 0;
+ }
+ current_save();
+ return 0;
+ }
+
+ index_partitions();
+ return 0;
+}
+
+/**
+ * Search global device list and return pointer to the device of type and num
+ * specified.
+ *
+ * @param type device type
+ * @param num device number
+ * Return: NULL if requested device does not exist
+ */
+struct mtd_device *device_find(u8 type, u8 num)
+{
+ struct list_head *entry;
+ struct mtd_device *dev_tmp;
+
+ list_for_each(entry, &devices) {
+ dev_tmp = list_entry(entry, struct mtd_device, link);
+
+ if ((dev_tmp->id->type == type) && (dev_tmp->id->num == num))
+ return dev_tmp;
+ }
+
+ return NULL;
+}
+
+/**
+ * Add specified device to the global device list.
+ *
+ * @param dev device to be added
+ */
+static void device_add(struct mtd_device *dev)
+{
+ u8 current_save_needed = 0;
+
+ if (list_empty(&devices)) {
+ current_mtd_dev = dev;
+ current_mtd_partnum = 0;
+ current_save_needed = 1;
+ }
+
+ list_add_tail(&dev->link, &devices);
+
+ if (current_save_needed > 0)
+ current_save();
+ else
+ index_partitions();
+}
+
+/**
+ * Parse device type, name and mtd-id. If syntax is ok allocate memory and
+ * return pointer to the device structure.
+ *
+ * @param mtd_dev pointer to the device definition string i.e. <mtd-dev>
+ * @param ret output pointer to next char after parse completes (output)
+ * @param retdev pointer to the allocated device (output)
+ * Return: 0 on success, 1 otherwise
+ */
+static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_device **retdev)
+{
+ struct mtd_device *dev;
+ struct part_info *part;
+ struct mtdids *id;
+ const char *mtd_id;
+ unsigned int mtd_id_len;
+ const char *p;
+ const char *pend;
+ LIST_HEAD(tmp_list);
+ struct list_head *entry, *n;
+ u16 num_parts;
+ u64 offset;
+ int err = 1;
+
+ debug("===device_parse===\n");
+
+ assert(retdev);
+ *retdev = NULL;
+
+ if (ret)
+ *ret = NULL;
+
+ /* fetch <mtd-id> */
+ mtd_id = p = mtd_dev;
+ if (!(p = strchr(mtd_id, ':'))) {
+ printf("no <mtd-id> identifier\n");
+ return 1;
+ }
+ mtd_id_len = p - mtd_id + 1;
+ p++;
+
+ /* verify if we have a valid device specified */
+ if ((id = id_find_by_mtd_id(mtd_id, mtd_id_len - 1)) == NULL) {
+ printf("invalid mtd device '%.*s'\n", mtd_id_len - 1, mtd_id);
+ return 1;
+ }
+
+ pend = strchr(p, ';');
+ debug("dev type = %d (%s), dev num = %d, mtd-id = %s\n",
+ id->type, MTD_DEV_TYPE(id->type),
+ id->num, id->mtd_id);
+ debug("parsing partitions %.*s\n", (int)(pend ? pend - p : strlen(p)), p);
+
+ /* parse partitions */
+ num_parts = 0;
+
+ offset = 0;
+ if ((dev = device_find(id->type, id->num)) != NULL) {
+ /* if device already exists start at the end of the last partition */
+ part = list_entry(dev->parts.prev, struct part_info, link);
+ offset = part->offset + part->size;
+ }
+
+ while (p && (*p != '\0') && (*p != ';')) {
+ err = 1;
+ if ((part_parse(p, &p, &part) != 0) || (!part))
+ break;
+
+ /* calculate offset when not specified */
+ if (part->offset == OFFSET_NOT_SPECIFIED)
+ part->offset = offset;
+ else
+ offset = part->offset;
+
+ /* verify alignment and size */
+ if (part_validate(id, part) != 0)
+ break;
+
+ offset += part->size;
+
+ /* partition is ok, add it to the list */
+ list_add_tail(&part->link, &tmp_list);
+ num_parts++;
+ err = 0;
+ }
+ if (err == 1) {
+ part_delall(&tmp_list);
+ return 1;
+ }
+
+ debug("\ntotal partitions: %d\n", num_parts);
+
+ /* check for next device presence */
+ if (p) {
+ if (*p == ';') {
+ if (ret)
+ *ret = ++p;
+ } else if (*p == '\0') {
+ if (ret)
+ *ret = p;
+ } else {
+ printf("unexpected character '%c' at the end of device\n", *p);
+ if (ret)
+ *ret = NULL;
+ return 1;
+ }
+ }
+
+ /* allocate memory for mtd_device structure */
+ if ((dev = (struct mtd_device *)malloc(sizeof(struct mtd_device))) == NULL) {
+ printf("out of memory\n");
+ return 1;
+ }
+ memset(dev, 0, sizeof(struct mtd_device));
+ dev->id = id;
+ dev->num_parts = 0; /* part_sort_add increments num_parts */
+ INIT_LIST_HEAD(&dev->parts);
+ INIT_LIST_HEAD(&dev->link);
+
+ /* move partitions from tmp_list to dev->parts */
+ list_for_each_safe(entry, n, &tmp_list) {
+ part = list_entry(entry, struct part_info, link);
+ list_del(entry);
+ if (part_sort_add(dev, part) != 0) {
+ device_del(dev);
+ return 1;
+ }
+ }
+
+ *retdev = dev;
+
+ debug("===\n\n");
+ return 0;
+}
+
+/**
+ * Initialize global device list.
+ *
+ * Return: 0 on success, 1 otherwise
+ */
+static int mtd_devices_init(void)
+{
+ last_parts[0] = '\0';
+ current_mtd_dev = NULL;
+ current_save();
+
+ return device_delall(&devices);
+}
+
+/*
+ * Search global mtdids list and find id of requested type and number.
+ *
+ * Return: pointer to the id if it exists, NULL otherwise
+ */
+static struct mtdids* id_find(u8 type, u8 num)
+{
+ struct list_head *entry;
+ struct mtdids *id;
+
+ list_for_each(entry, &mtdids) {
+ id = list_entry(entry, struct mtdids, link);
+
+ if ((id->type == type) && (id->num == num))
+ return id;
+ }
+
+ return NULL;
+}
+
+/**
+ * Search global mtdids list and find id of a requested mtd_id.
+ *
+ * Note: first argument is not null terminated.
+ *
+ * @param mtd_id string containing requested mtd_id
+ * @param mtd_id_len length of supplied mtd_id
+ * Return: pointer to the id if it exists, NULL otherwise
+ */
+static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len)
+{
+ struct list_head *entry;
+ struct mtdids *id;
+
+ debug("--- id_find_by_mtd_id: '%.*s' (len = %d)\n",
+ mtd_id_len, mtd_id, mtd_id_len);
+
+ list_for_each(entry, &mtdids) {
+ id = list_entry(entry, struct mtdids, link);
+
+ debug("entry: '%s' (len = %zu)\n",
+ id->mtd_id, strlen(id->mtd_id));
+
+ if (mtd_id_len != strlen(id->mtd_id))
+ continue;
+ if (strncmp(id->mtd_id, mtd_id, mtd_id_len) == 0)
+ return id;
+ }
+
+ return NULL;
+}
+
+/**
+ * Parse device id string <dev-id> := 'nand'|'nor'|'onenand'|'spi-nand'<dev-num>,
+ * return device type and number.
+ *
+ * @param id string describing device id
+ * @param ret_id output pointer to next char after parse completes (output)
+ * @param dev_type parsed device type (output)
+ * @param dev_num parsed device number (output)
+ * Return: 0 on success, 1 otherwise
+ */
+int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type,
+ u8 *dev_num)
+{
+ const char *p = id;
+
+ *dev_type = 0;
+ if (strncmp(p, "nand", 4) == 0) {
+ *dev_type = MTD_DEV_TYPE_NAND;
+ p += 4;
+ } else if (strncmp(p, "nor", 3) == 0) {
+ *dev_type = MTD_DEV_TYPE_NOR;
+ p += 3;
+ } else if (strncmp(p, "onenand", 7) == 0) {
+ *dev_type = MTD_DEV_TYPE_ONENAND;
+ p += 7;
+ } else if (strncmp(p, "spi-nand", 8) == 0) {
+ *dev_type = MTD_DEV_TYPE_SPINAND;
+ p += 8;
+ } else {
+ printf("incorrect device type in %s\n", id);
+ return 1;
+ }
+
+ if (!isdigit(*p)) {
+ printf("incorrect device number in %s\n", id);
+ return 1;
+ }
+
+ *dev_num = simple_strtoul(p, (char **)&p, 0);
+ if (ret_id)
+ *ret_id = p;
+ return 0;
+}
+
+/**
+ * Process all devices and generate corresponding mtdparts string describing
+ * all partitions on all devices.
+ *
+ * @param buf output buffer holding generated mtdparts string (output)
+ * @param buflen buffer size
+ * Return: 0 on success, 1 otherwise
+ */
+static int generate_mtdparts(char *buf, u32 buflen)
+{
+ struct list_head *pentry, *dentry;
+ struct mtd_device *dev;
+ struct part_info *part, *prev_part;
+ char *p = buf;
+ char tmpbuf[32];
+ u64 size, offset;
+ u32 len, part_cnt;
+ u32 maxlen = buflen - 1;
+
+ debug("--- generate_mtdparts ---\n");
+
+ if (list_empty(&devices)) {
+ buf[0] = '\0';
+ return 0;
+ }
+
+ list_for_each(dentry, &devices) {
+ dev = list_entry(dentry, struct mtd_device, link);
+
+ /* copy mtd_id */
+ len = strlen(dev->id->mtd_id) + 1;
+ if (len > maxlen)
+ goto cleanup;
+ memcpy(p, dev->id->mtd_id, len - 1);
+ p += len - 1;
+ *(p++) = ':';
+ maxlen -= len;
+
+ /* format partitions */
+ prev_part = NULL;
+ part_cnt = 0;
+ list_for_each(pentry, &dev->parts) {
+ part = list_entry(pentry, struct part_info, link);
+ size = part->size;
+ offset = part->offset;
+ part_cnt++;
+
+ /* partition size */
+ memsize_format(tmpbuf, size);
+ len = strlen(tmpbuf);
+ if (len > maxlen)
+ goto cleanup;
+ memcpy(p, tmpbuf, len);
+ p += len;
+ maxlen -= len;
+
+ /* add offset only when there is a gap between
+ * partitions */
+ if ((!prev_part && (offset != 0)) ||
+ (prev_part && ((prev_part->offset + prev_part->size) != part->offset))) {
+
+ memsize_format(tmpbuf, offset);
+ len = strlen(tmpbuf) + 1;
+ if (len > maxlen)
+ goto cleanup;
+ *(p++) = '@';
+ memcpy(p, tmpbuf, len - 1);
+ p += len - 1;
+ maxlen -= len;
+ }
+
+ /* copy name only if user supplied */
+ if(!part->auto_name) {
+ len = strlen(part->name) + 2;
+ if (len > maxlen)
+ goto cleanup;
+
+ *(p++) = '(';
+ memcpy(p, part->name, len - 2);
+ p += len - 2;
+ *(p++) = ')';
+ maxlen -= len;
+ }
+
+ /* ro mask flag */
+ if (part->mask_flags && MTD_WRITEABLE_CMD) {
+ len = 2;
+ if (len > maxlen)
+ goto cleanup;
+ *(p++) = 'r';
+ *(p++) = 'o';
+ maxlen -= 2;
+ }
+
+ /* print ',' separator if there are other partitions
+ * following */
+ if (dev->num_parts > part_cnt) {
+ if (1 > maxlen)
+ goto cleanup;
+ *(p++) = ',';
+ maxlen--;
+ }
+ prev_part = part;
+ }
+ /* print ';' separator if there are other devices following */
+ if (dentry->next != &devices) {
+ if (1 > maxlen)
+ goto cleanup;
+ *(p++) = ';';
+ maxlen--;
+ }
+ }
+
+ /* we still have at least one char left, as we decremented maxlen at
+ * the begining */
+ *p = '\0';
+
+ return 0;
+
+cleanup:
+ last_parts[0] = '\0';
+ return 1;
+}
+
+/**
+ * Call generate_mtdparts to process all devices and generate corresponding
+ * mtdparts string, save it in mtdparts environment variable.
+ *
+ * @param buf output buffer holding generated mtdparts string (output)
+ * @param buflen buffer size
+ * Return: 0 on success, 1 otherwise
+ */
+static int generate_mtdparts_save(char *buf, u32 buflen)
+{
+ int ret;
+
+ ret = generate_mtdparts(buf, buflen);
+
+ if ((buf[0] != '\0') && (ret == 0))
+ env_set("mtdparts", buf);
+ else
+ env_set("mtdparts", NULL);
+
+ return ret;
+}
+
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
+/**
+ * Get the net size (w/o bad blocks) of the given partition.
+ *
+ * @param mtd the mtd info
+ * @param part the partition
+ * Return: the calculated net size of this partition
+ */
+static uint64_t net_part_size(struct mtd_info *mtd, struct part_info *part)
+{
+ uint64_t i, net_size = 0;
+
+ if (!mtd->_block_isbad)
+ return part->size;
+
+ for (i = 0; i < part->size; i += mtd->erasesize) {
+ if (!mtd->_block_isbad(mtd, part->offset + i))
+ net_size += mtd->erasesize;
+ }
+
+ return net_size;
+}
+#endif
+
+static void print_partition_table(void)
+{
+ struct list_head *dentry, *pentry;
+ struct part_info *part;
+ struct mtd_device *dev;
+ int part_num;
+
+ list_for_each(dentry, &devices) {
+ dev = list_entry(dentry, struct mtd_device, link);
+ /* list partitions for given device */
+ part_num = 0;
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
+ struct mtd_info *mtd;
+
+ if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
+ return;
+
+ printf("\ndevice %s%d <%s>, # parts = %d\n",
+ MTD_DEV_TYPE(dev->id->type), dev->id->num,
+ dev->id->mtd_id, dev->num_parts);
+ printf(" #: name\t\tsize\t\tnet size\toffset\t\tmask_flags\n");
+
+ list_for_each(pentry, &dev->parts) {
+ u32 net_size;
+ char *size_note;
+
+ part = list_entry(pentry, struct part_info, link);
+ net_size = net_part_size(mtd, part);
+ size_note = part->size == net_size ? " " : " (!)";
+ printf("%2d: %-20s0x%08llx\t0x%08x%s\t0x%08llx\t%d\n",
+ part_num, part->name, part->size,
+ net_size, size_note, part->offset,
+ part->mask_flags);
+#else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
+ printf("\ndevice %s%d <%s>, # parts = %d\n",
+ MTD_DEV_TYPE(dev->id->type), dev->id->num,
+ dev->id->mtd_id, dev->num_parts);
+ printf(" #: name\t\tsize\t\toffset\t\tmask_flags\n");
+
+ list_for_each(pentry, &dev->parts) {
+ part = list_entry(pentry, struct part_info, link);
+ printf("%2d: %-20s0x%08llx\t0x%08llx\t%d\n",
+ part_num, part->name, part->size,
+ part->offset, part->mask_flags);
+#endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
+ part_num++;
+ }
+ }
+
+ if (list_empty(&devices))
+ printf("no partitions defined\n");
+}
+
+/**
+ * Format and print out a partition list for each device from global device
+ * list.
+ */
+static void list_partitions(void)
+{
+ struct part_info *part;
+
+ debug("\n---list_partitions---\n");
+ print_partition_table();
+
+ /* current_mtd_dev is not NULL only when we have non empty device list */
+ if (current_mtd_dev) {
+ part = mtd_part_info(current_mtd_dev, current_mtd_partnum);
+ if (part) {
+ printf("\nactive partition: %s%d,%d - (%s) 0x%08llx @ 0x%08llx\n",
+ MTD_DEV_TYPE(current_mtd_dev->id->type),
+ current_mtd_dev->id->num, current_mtd_partnum,
+ part->name, part->size, part->offset);
+ } else {
+ printf("could not get current partition info\n\n");
+ }
+ }
+
+ printf("\ndefaults:\n");
+ printf("mtdids : %s\n",
+ mtdids_default ? mtdids_default : "none");
+ /*
+ * Using printf() here results in printbuffer overflow
+ * if default mtdparts string is greater than console
+ * printbuffer. Use puts() to prevent system crashes.
+ */
+ puts("mtdparts: ");
+ puts(mtdparts_default ? mtdparts_default : "none");
+ puts("\n");
+}
+
+/**
+ * Given partition identifier in form of <dev_type><dev_num>,<part_num> find
+ * corresponding device and verify partition number.
+ *
+ * @param id string describing device and partition or partition name
+ * @param dev pointer to the requested device (output)
+ * @param part_num verified partition number (output)
+ * @param part pointer to requested partition (output)
+ * Return: 0 on success, 1 otherwise
+ */
+int find_dev_and_part(const char *id, struct mtd_device **dev,
+ u8 *part_num, struct part_info **part)
+{
+ struct list_head *dentry, *pentry;
+ u8 type, dnum, pnum;
+ const char *p;
+
+ debug("--- find_dev_and_part ---\nid = %s\n", id);
+
+ list_for_each(dentry, &devices) {
+ *part_num = 0;
+ *dev = list_entry(dentry, struct mtd_device, link);
+ list_for_each(pentry, &(*dev)->parts) {
+ *part = list_entry(pentry, struct part_info, link);
+ if (strcmp((*part)->name, id) == 0)
+ return 0;
+ (*part_num)++;
+ }
+ }
+
+ p = id;
+ *dev = NULL;
+ *part = NULL;
+ *part_num = 0;
+
+ if (mtd_id_parse(p, &p, &type, &dnum) != 0)
+ return 1;
+
+ if ((*p++ != ',') || (*p == '\0')) {
+ printf("no partition number specified\n");
+ return 1;
+ }
+ pnum = simple_strtoul(p, (char **)&p, 0);
+ if (*p != '\0') {
+ printf("unexpected trailing character '%c'\n", *p);
+ return 1;
+ }
+
+ if ((*dev = device_find(type, dnum)) == NULL) {
+ printf("no such device %s%d\n", MTD_DEV_TYPE(type), dnum);
+ return 1;
+ }
+
+ if ((*part = mtd_part_info(*dev, pnum)) == NULL) {
+ printf("no such partition\n");
+ *dev = NULL;
+ return 1;
+ }
+
+ *part_num = pnum;
+
+ return 0;
+}
+
+/**
+ * Find and delete partition. For partition id format see find_dev_and_part().
+ *
+ * @param id string describing device and partition
+ * Return: 0 on success, 1 otherwise
+ */
+static int delete_partition(const char *id)
+{
+ u8 pnum;
+ struct mtd_device *dev;
+ struct part_info *part;
+
+ if (find_dev_and_part(id, &dev, &pnum, &part) == 0) {
+
+ debug("delete_partition: device = %s%d, partition %d = (%s) 0x%08llx@0x%08llx\n",
+ MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum,
+ part->name, part->size, part->offset);
+
+ if (part_del(dev, part) != 0)
+ return 1;
+
+ if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
+ printf("generated mtdparts too long, resetting to null\n");
+ return 1;
+ }
+ return 0;
+ }
+
+ printf("partition %s not found\n", id);
+ return 1;
+}
+
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
+/**
+ * Increase the size of the given partition so that it's net size is at least
+ * as large as the size member and such that the next partition would start on a
+ * good block if it were adjacent to this partition.
+ *
+ * @param mtd the mtd device
+ * @param part the partition
+ * @param next_offset pointer to the offset of the next partition after this
+ * partition's size has been modified (output)
+ */
+static void spread_partition(struct mtd_info *mtd, struct part_info *part,
+ uint64_t *next_offset)
+{
+ uint64_t net_size, padding_size = 0;
+ int truncated;
+
+ mtd_get_len_incl_bad(mtd, part->offset, part->size, &net_size,
+ &truncated);
+
+ /*
+ * Absorb bad blocks immediately following this
+ * partition also into the partition, such that
+ * the next partition starts with a good block.
+ */
+ if (!truncated) {
+ mtd_get_len_incl_bad(mtd, part->offset + net_size,
+ mtd->erasesize, &padding_size, &truncated);
+ if (truncated)
+ padding_size = 0;
+ else
+ padding_size -= mtd->erasesize;
+ }
+
+ if (truncated) {
+ printf("truncated partition %s to %lld bytes\n", part->name,
+ (uint64_t) net_size + padding_size);
+ }
+
+ part->size = net_size + padding_size;
+ *next_offset = part->offset + part->size;
+}
+
+/**
+ * Adjust all of the partition sizes, such that all partitions are at least
+ * as big as their mtdparts environment variable sizes and they each start
+ * on a good block.
+ *
+ * Return: 0 on success, 1 otherwise
+ */
+static int spread_partitions(void)
+{
+ struct list_head *dentry, *pentry;
+ struct mtd_device *dev;
+ struct part_info *part;
+ struct mtd_info *mtd;
+ int part_num;
+ uint64_t cur_offs;
+
+ list_for_each(dentry, &devices) {
+ dev = list_entry(dentry, struct mtd_device, link);
+
+ if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
+ return 1;
+
+ part_num = 0;
+ cur_offs = 0;
+ list_for_each(pentry, &dev->parts) {
+ part = list_entry(pentry, struct part_info, link);
+
+ debug("spread_partitions: device = %s%d, partition %d ="
+ " (%s) 0x%08llx@0x%08llx\n",
+ MTD_DEV_TYPE(dev->id->type), dev->id->num,
+ part_num, part->name, part->size,
+ part->offset);
+
+ if (cur_offs > part->offset)
+ part->offset = cur_offs;
+
+ spread_partition(mtd, part, &cur_offs);
+
+ part_num++;
+ }
+ }
+
+ index_partitions();
+
+ if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
+ printf("generated mtdparts too long, resetting to null\n");
+ return 1;
+ }
+ return 0;
+}
+#endif /* CONFIG_CMD_MTDPARTS_SPREAD */
+
+/**
+ * The mtdparts variable tends to be long. If we need to access it
+ * before the env is relocated, then we need to use our own stack
+ * buffer. gd->env_buf will be too small.
+ *
+ * @param buf temporary buffer pointer MTDPARTS_MAXLEN long
+ * Return: mtdparts variable string, NULL if not found
+ */
+static const char *env_get_mtdparts(char *buf)
+{
+ if (gd->flags & GD_FLG_ENV_READY)
+ return env_get("mtdparts");
+ if (env_get_f("mtdparts", buf, MTDPARTS_MAXLEN) != -1)
+ return buf;
+ return NULL;
+}
+
+/**
+ * Accept character string describing mtd partitions and call device_parse()
+ * for each entry. Add created devices to the global devices list.
+ *
+ * @param mtdparts string specifing mtd partitions
+ * Return: 0 on success, 1 otherwise
+ */
+static int parse_mtdparts(const char *const mtdparts)
+{
+ const char *p;
+ struct mtd_device *dev;
+ int err = 1;
+ char tmp_parts[MTDPARTS_MAXLEN];
+
+ debug("\n---parse_mtdparts---\nmtdparts = %s\n\n", mtdparts);
+
+ /* delete all devices and partitions */
+ if (mtd_devices_init() != 0) {
+ printf("could not initialise device list\n");
+ return err;
+ }
+
+ /* re-read 'mtdparts' variable, mtd_devices_init may be updating env */
+ p = env_get_mtdparts(tmp_parts);
+ if (!p)
+ p = mtdparts;
+
+ /* Skip the useless prefix, if any */
+ if (strncmp(p, "mtdparts=", 9) == 0)
+ p += 9;
+
+ while (*p != '\0') {
+ err = 1;
+ if ((device_parse(p, &p, &dev) != 0) || (!dev))
+ break;
+
+ debug("+ device: %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
+ dev->id->num, dev->id->mtd_id);
+
+ /* check if parsed device is already on the list */
+ if (device_find(dev->id->type, dev->id->num) != NULL) {
+ printf("device %s%d redefined, please correct mtdparts variable\n",
+ MTD_DEV_TYPE(dev->id->type), dev->id->num);
+ break;
+ }
+
+ list_add_tail(&dev->link, &devices);
+ err = 0;
+ }
+ if (err == 1) {
+ free(dev);
+ device_delall(&devices);
+ }
+
+ return err;
+}
+
+/**
+ * Parse provided string describing mtdids mapping (see file header for mtdids
+ * variable format). Allocate memory for each entry and add all found entries
+ * to the global mtdids list.
+ *
+ * @param ids mapping string
+ * Return: 0 on success, 1 otherwise
+ */
+static int parse_mtdids(const char *const ids)
+{
+ const char *p = ids;
+ const char *mtd_id;
+ int mtd_id_len;
+ struct mtdids *id;
+ struct list_head *entry, *n;
+ struct mtdids *id_tmp;
+ u8 type, num;
+ u64 size;
+ int ret = 1;
+
+ debug("\n---parse_mtdids---\nmtdids = %s\n\n", ids);
+
+ /* clean global mtdids list */
+ list_for_each_safe(entry, n, &mtdids) {
+ id_tmp = list_entry(entry, struct mtdids, link);
+ debug("mtdids del: %d %d\n", id_tmp->type, id_tmp->num);
+ list_del(entry);
+ free(id_tmp);
+ }
+ last_ids[0] = '\0';
+ INIT_LIST_HEAD(&mtdids);
+
+ while(p && (*p != '\0')) {
+
+ ret = 1;
+ /* parse 'nor'|'nand'|'onenand'|'spi-nand'<dev-num> */
+ if (mtd_id_parse(p, &p, &type, &num) != 0)
+ break;
+
+ if (*p != '=') {
+ printf("mtdids: incorrect <dev-num>\n");
+ break;
+ }
+ p++;
+
+ /* check if requested device exists */
+ if (mtd_device_validate(type, num, &size) != 0)
+ return 1;
+
+ /* locate <mtd-id> */
+ mtd_id = p;
+ if ((p = strchr(mtd_id, ',')) != NULL) {
+ mtd_id_len = p - mtd_id + 1;
+ p++;
+ } else {
+ mtd_id_len = strlen(mtd_id) + 1;
+ }
+ if (mtd_id_len == 0) {
+ printf("mtdids: no <mtd-id> identifier\n");
+ break;
+ }
+
+ /* check if this id is already on the list */
+ int double_entry = 0;
+ list_for_each(entry, &mtdids) {
+ id_tmp = list_entry(entry, struct mtdids, link);
+ if ((id_tmp->type == type) && (id_tmp->num == num)) {
+ double_entry = 1;
+ break;
+ }
+ }
+ if (double_entry) {
+ printf("device id %s%d redefined, please correct mtdids variable\n",
+ MTD_DEV_TYPE(type), num);
+ break;
+ }
+
+ /* allocate mtdids structure */
+ if (!(id = (struct mtdids *)malloc(sizeof(struct mtdids) + mtd_id_len))) {
+ printf("out of memory\n");
+ break;
+ }
+ memset(id, 0, sizeof(struct mtdids) + mtd_id_len);
+ id->num = num;
+ id->type = type;
+ id->size = size;
+ id->mtd_id = (char *)(id + 1);
+ strncpy(id->mtd_id, mtd_id, mtd_id_len - 1);
+ id->mtd_id[mtd_id_len - 1] = '\0';
+ INIT_LIST_HEAD(&id->link);
+
+ debug("+ id %s%d\t%16lld bytes\t%s\n",
+ MTD_DEV_TYPE(id->type), id->num,
+ id->size, id->mtd_id);
+
+ list_add_tail(&id->link, &mtdids);
+ ret = 0;
+ }
+ if (ret == 1) {
+ /* clean mtdids list and free allocated memory */
+ list_for_each_safe(entry, n, &mtdids) {
+ id_tmp = list_entry(entry, struct mtdids, link);
+ list_del(entry);
+ free(id_tmp);
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Parse and initialize global mtdids mapping and create global
+ * device/partition list.
+ *
+ * Return: 0 on success, 1 otherwise
+ */
+int mtdparts_init(void)
+{
+ static int initialized = 0;
+ const char *ids, *parts;
+ const char *current_partition;
+ int ids_changed;
+ char tmp_ep[PARTITION_MAXLEN + 1];
+ char tmp_parts[MTDPARTS_MAXLEN];
+
+ debug("\n---mtdparts_init---\n");
+ if (!initialized) {
+ INIT_LIST_HEAD(&mtdids);
+ INIT_LIST_HEAD(&devices);
+ memset(last_ids, 0, sizeof(last_ids));
+ memset(last_parts, 0, sizeof(last_parts));
+ memset(last_partition, 0, sizeof(last_partition));
+#if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
+ board_mtdparts_default(&mtdids_default, &mtdparts_default);
+#endif
+ use_defaults = 1;
+ initialized = 1;
+ }
+
+ /* get variables */
+ ids = env_get("mtdids");
+ parts = env_get_mtdparts(tmp_parts);
+ current_partition = env_get("partition");
+
+ /* save it for later parsing, cannot rely on current partition pointer
+ * as 'partition' variable may be updated during init */
+ memset(tmp_parts, 0, sizeof(tmp_parts));
+ memset(tmp_ep, 0, sizeof(tmp_ep));
+ if (current_partition)
+ strncpy(tmp_ep, current_partition, PARTITION_MAXLEN);
+
+ debug("last_ids : %s\n", last_ids);
+ debug("env_ids : %s\n", ids);
+ debug("last_parts: %s\n", last_parts);
+ debug("env_parts : %s\n\n", parts);
+
+ debug("last_partition : %s\n", last_partition);
+ debug("env_partition : %s\n", current_partition);
+
+ /* if mtdids variable is empty try to use defaults */
+ if (!ids) {
+ if (mtdids_default) {
+ debug("mtdids variable not defined, using default\n");
+ ids = mtdids_default;
+ env_set("mtdids", (char *)ids);
+ } else {
+ printf("mtdids not defined, no default present\n");
+ return 1;
+ }
+ }
+ if (strlen(ids) > MTDIDS_MAXLEN - 1) {
+ printf("mtdids too long (> %d)\n", MTDIDS_MAXLEN);
+ return 1;
+ }
+
+ /* use defaults when mtdparts variable is not defined
+ * once mtdparts is saved environment, drop use_defaults flag */
+ if (!parts) {
+ if (mtdparts_default && use_defaults) {
+ parts = mtdparts_default;
+ if (env_set("mtdparts", (char *)parts) == 0)
+ use_defaults = 0;
+ } else
+ printf("mtdparts variable not set, see 'help mtdparts'\n");
+ }
+
+ if (parts && (strlen(parts) > MTDPARTS_MAXLEN - 1)) {
+ printf("mtdparts too long (> %d)\n", MTDPARTS_MAXLEN);
+ return 1;
+ }
+
+ /* check if we have already parsed those mtdids */
+ if ((last_ids[0] != '\0') && (strcmp(last_ids, ids) == 0)) {
+ ids_changed = 0;
+ } else {
+ ids_changed = 1;
+
+ if (parse_mtdids(ids) != 0) {
+ mtd_devices_init();
+ return 1;
+ }
+
+ /* ok it's good, save new ids */
+ strncpy(last_ids, ids, MTDIDS_MAXLEN);
+ }
+
+ /* parse partitions if either mtdparts or mtdids were updated */
+ if (parts && ((last_parts[0] == '\0') || ((strcmp(last_parts, parts) != 0)) || ids_changed)) {
+ if (parse_mtdparts(parts) != 0)
+ return 1;
+
+ if (list_empty(&devices)) {
+ printf("mtdparts_init: no valid partitions\n");
+ return 1;
+ }
+
+ /* ok it's good, save new parts */
+ strncpy(last_parts, parts, MTDPARTS_MAXLEN);
+
+ /* reset first partition from first dev from the list as current */
+ current_mtd_dev = list_entry(devices.next, struct mtd_device, link);
+ current_mtd_partnum = 0;
+ current_save();
+
+ debug("mtdparts_init: current_mtd_dev = %s%d, current_mtd_partnum = %d\n",
+ MTD_DEV_TYPE(current_mtd_dev->id->type),
+ current_mtd_dev->id->num, current_mtd_partnum);
+ }
+
+ /* mtdparts variable was reset to NULL, delete all devices/partitions */
+ if (!parts && (last_parts[0] != '\0'))
+ return mtd_devices_init();
+
+ /* do not process current partition if mtdparts variable is null */
+ if (!parts)
+ return 0;
+
+ /* is current partition set in environment? if so, use it */
+ if ((tmp_ep[0] != '\0') && (strcmp(tmp_ep, last_partition) != 0)) {
+ struct part_info *p;
+ struct mtd_device *cdev;
+ u8 pnum;
+
+ debug("--- getting current partition: %s\n", tmp_ep);
+
+ if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) {
+ current_mtd_dev = cdev;
+ current_mtd_partnum = pnum;
+ current_save();
+ }
+ } else if (env_get("partition") == NULL) {
+ debug("no partition variable set, setting...\n");
+ current_save();
+ }
+
+ return 0;
+}
+
+/**
+ * Return pointer to the partition of a requested number from a requested
+ * device.
+ *
+ * @param dev device that is to be searched for a partition
+ * @param part_num requested partition number
+ * Return: pointer to the part_info, NULL otherwise
+ */
+static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num)
+{
+ struct list_head *entry;
+ struct part_info *part;
+ int num;
+
+ if (!dev)
+ return NULL;
+
+ debug("\n--- mtd_part_info: partition number %d for device %s%d (%s)\n",
+ part_num, MTD_DEV_TYPE(dev->id->type),
+ dev->id->num, dev->id->mtd_id);
+
+ if (part_num >= dev->num_parts) {
+ printf("invalid partition number %d for device %s%d (%s)\n",
+ part_num, MTD_DEV_TYPE(dev->id->type),
+ dev->id->num, dev->id->mtd_id);
+ return NULL;
+ }
+
+ /* locate partition number, return it */
+ num = 0;
+ list_for_each(entry, &dev->parts) {
+ part = list_entry(entry, struct part_info, link);
+
+ if (part_num == num++) {
+ return part;
+ }
+ }
+
+ return NULL;
+}
+
+/***************************************************/
+/* U-Boot commands */
+/***************************************************/
+/* command line only */
+/**
+ * Routine implementing u-boot chpart command. Sets new current partition based
+ * on the user supplied partition id. For partition id format see find_dev_and_part().
+ *
+ * @param cmdtp command internal data
+ * @param flag command flag
+ * @param argc number of arguments supplied to the command
+ * @param argv arguments list
+ * Return: 0 on success, 1 otherwise
+ */
+static int do_chpart(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+/* command line only */
+ struct mtd_device *dev;
+ struct part_info *part;
+ u8 pnum;
+
+ if (mtdparts_init() !=0)
+ return 1;
+
+ if (argc < 2) {
+ printf("no partition id specified\n");
+ return 1;
+ }
+
+ if (find_dev_and_part(argv[1], &dev, &pnum, &part) != 0)
+ return 1;
+
+ current_mtd_dev = dev;
+ current_mtd_partnum = pnum;
+ current_save();
+
+ printf("partition changed to %s%d,%d\n",
+ MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum);
+
+ return 0;
+}
+
+/**
+ * Routine implementing u-boot mtdparts command. Initialize/update default global
+ * partition list and process user partition request (list, add, del).
+ *
+ * @param cmdtp command internal data
+ * @param flag command flag
+ * @param argc number of arguments supplied to the command
+ * @param argv arguments list
+ * Return: 0 on success, 1 otherwise
+ */
+static int do_mtdparts(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (argc == 2) {
+ if (strcmp(argv[1], "default") == 0) {
+ env_set("mtdids", NULL);
+ env_set("mtdparts", NULL);
+ env_set("partition", NULL);
+ use_defaults = 1;
+
+ mtdparts_init();
+ return 0;
+ } else if (strcmp(argv[1], "delall") == 0) {
+ /* this may be the first run, initialize lists if needed */
+ mtdparts_init();
+
+ env_set("mtdparts", NULL);
+
+ /* mtd_devices_init() calls current_save() */
+ return mtd_devices_init();
+ }
+ }
+
+ /* make sure we are in sync with env variables */
+ if (mtdparts_init() != 0)
+ return 1;
+
+ if (argc == 1) {
+ list_partitions();
+ return 0;
+ }
+
+ /* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */
+ if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) {
+#define PART_ADD_DESC_MAXLEN 64
+ char tmpbuf[PART_ADD_DESC_MAXLEN];
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
+ struct mtd_info *mtd;
+ uint64_t next_offset;
+#endif
+ u8 type, num, len;
+ struct mtd_device *dev;
+ struct mtd_device *dev_tmp;
+ struct mtdids *id;
+ struct part_info *p;
+
+ if (mtd_id_parse(argv[2], NULL, &type, &num) != 0)
+ return 1;
+
+ if ((id = id_find(type, num)) == NULL) {
+ printf("no such device %s defined in mtdids variable\n", argv[2]);
+ return 1;
+ }
+
+ len = strlen(id->mtd_id) + 1; /* 'mtd_id:' */
+ len += strlen(argv[3]); /* size@offset */
+ len += strlen(argv[4]) + 2; /* '(' name ')' */
+ if (argv[5] && (strlen(argv[5]) == 2))
+ len += 2; /* 'ro' */
+
+ if (len >= PART_ADD_DESC_MAXLEN) {
+ printf("too long partition description\n");
+ return 1;
+ }
+ sprintf(tmpbuf, "%s:%s(%s)%s",
+ id->mtd_id, argv[3], argv[4], argv[5] ? argv[5] : "");
+ debug("add tmpbuf: %s\n", tmpbuf);
+
+ if ((device_parse(tmpbuf, NULL, &dev) != 0) || (!dev))
+ return 1;
+
+ debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
+ dev->id->num, dev->id->mtd_id);
+
+ p = list_entry(dev->parts.next, struct part_info, link);
+
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
+ if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
+ return 1;
+
+ if (!strcmp(&argv[1][3], ".spread")) {
+ spread_partition(mtd, p, &next_offset);
+ debug("increased %s to %llu bytes\n", p->name, p->size);
+ }
+#endif
+
+ dev_tmp = device_find(dev->id->type, dev->id->num);
+ if (dev_tmp == NULL) {
+ device_add(dev);
+ } else if (part_add(dev_tmp, p) != 0) {
+ /* merge new partition with existing ones*/
+ device_del(dev);
+ return 1;
+ }
+
+ if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
+ printf("generated mtdparts too long, resetting to null\n");
+ return 1;
+ }
+
+ return 0;
+ }
+
+ /* mtdparts del part-id */
+ if ((argc == 3) && (strcmp(argv[1], "del") == 0)) {
+ debug("del: part-id = %s\n", argv[2]);
+
+ return delete_partition(argv[2]);
+ }
+
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
+ if ((argc == 2) && (strcmp(argv[1], "spread") == 0))
+ return spread_partitions();
+#endif /* CONFIG_CMD_MTDPARTS_SPREAD */
+
+ return CMD_RET_USAGE;
+}
+
+/***************************************************/
+U_BOOT_CMD(
+ chpart, 2, 0, do_chpart,
+ "change active partition of a MTD device",
+ "part-id\n"
+ " - change active partition (e.g. part-id = nand0,1) of a MTD device"
+);
+
+U_BOOT_LONGHELP(mtdparts,
+ "\n"
+ " - list partition table\n"
+ "mtdparts delall\n"
+ " - delete all partitions\n"
+ "mtdparts del part-id\n"
+ " - delete partition (e.g. part-id = nand0,1)\n"
+ "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n"
+ " - add partition\n"
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
+ "mtdparts add.spread <mtd-dev> <size>[@<offset>] [<name>] [ro]\n"
+ " - add partition, padding size by skipping bad blocks\n"
+#endif
+ "mtdparts default\n"
+ " - reset partition table to defaults\n"
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
+ "mtdparts spread\n"
+ " - adjust the sizes of the partitions so they are\n"
+ " at least as big as the mtdparts variable specifies\n"
+ " and they each start on a good block\n\n"
+#else
+ "\n"
+#endif /* CONFIG_CMD_MTDPARTS_SPREAD */
+ "-----\n\n"
+ "this command uses three environment variables:\n\n"
+ "'partition' - keeps current partition identifier\n\n"
+ "partition := <part-id>\n"
+ "<part-id> := <dev-id>,part_num\n\n"
+ "'mtdids' - linux kernel mtd device id <-> u-boot device id mapping\n\n"
+ "mtdids=<idmap>[,<idmap>,...]\n\n"
+ "<idmap> := <dev-id>=<mtd-id>\n"
+ "<dev-id> := 'nand'|'nor'|'onenand'|'spi-nand'<dev-num>\n"
+ "<dev-num> := mtd device number, 0...\n"
+ "<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)\n\n"
+ "'mtdparts' - partition list\n\n"
+ "mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]\n\n"
+ "<mtd-def> := <mtd-id>:<part-def>[,<part-def>...]\n"
+ "<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)\n"
+ "<part-def> := <size>[@<offset>][<name>][<ro-flag>]\n"
+ "<size> := standard linux memsize OR '-' to denote all remaining space\n"
+ "<offset> := partition start offset within the device\n"
+ "<name> := '(' NAME ')'\n"
+ "<ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)");
+
+U_BOOT_CMD(
+ mtdparts, 6, 0, do_mtdparts,
+ "define flash/nand partitions", mtdparts_help_text
+);
+/***************************************************/
diff --git a/cmd/mux.c b/cmd/mux.c
new file mode 100644
index 00000000000..2f6c08b8b07
--- /dev/null
+++ b/cmd/mux.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * List, select, and deselect mux controllers on the fly.
+ *
+ * Copyright (c) 2020 Texas Instruments Inc.
+ * Author: Pratyush Yadav <p.yadav@ti.com>
+ */
+
+#include <command.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <mux.h>
+#include <mux-internal.h>
+#include <linux/err.h>
+#include <dt-bindings/mux/mux.h>
+
+#define COLUMN_SIZE 16
+
+/*
+ * Print a member of a column. The total size of the text printed, including
+ * trailing whitespace, will always be COLUMN_SIZE.
+ */
+#define PRINT_COLUMN(fmt, args...) do { \
+ char buf[COLUMN_SIZE + 1]; \
+ snprintf(buf, COLUMN_SIZE + 1, fmt, ##args); \
+ printf("%-*s", COLUMN_SIZE, buf); \
+} while (0)
+
+/*
+ * Find a mux based on its device name in argv[1] and index in the chip in
+ * argv[2].
+ */
+static struct mux_control *cmd_mux_find(char *const argv[])
+{
+ struct udevice *dev;
+ struct mux_chip *chip;
+ int ret;
+ unsigned long id;
+
+ ret = strict_strtoul(argv[2], 10, &id);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = uclass_get_device_by_name(UCLASS_MUX, argv[1], &dev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ chip = dev_get_uclass_priv(dev);
+ if (!chip)
+ return ERR_PTR(-EINVAL);
+
+ if (id >= chip->controllers)
+ return ERR_PTR(-EINVAL);
+
+ return &chip->mux[id];
+}
+
+/*
+ * Print the details of a mux. The columns printed correspond to: "Selected",
+ * "Current State", "Idle State", and "Num States".
+ */
+static void print_mux(struct mux_control *mux)
+{
+ PRINT_COLUMN("%s", mux->in_use ? "yes" : "no");
+
+ if (mux->cached_state == MUX_IDLE_AS_IS)
+ PRINT_COLUMN("%s", "unknown");
+ else
+ PRINT_COLUMN("0x%x", mux->cached_state);
+
+ if (mux->idle_state == MUX_IDLE_AS_IS)
+ PRINT_COLUMN("%s", "as-is");
+ else if (mux->idle_state == MUX_IDLE_DISCONNECT)
+ PRINT_COLUMN("%s", "disconnect");
+ else
+ PRINT_COLUMN("0x%x", mux->idle_state);
+
+ PRINT_COLUMN("0x%x", mux->states);
+
+ printf("\n");
+}
+
+static int do_mux_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ struct mux_chip *chip;
+ int j;
+
+ for (uclass_first_device(UCLASS_MUX, &dev);
+ dev;
+ uclass_next_device(&dev)) {
+ chip = dev_get_uclass_priv(dev);
+ if (!chip) {
+ dev_err(dev, "can't find mux chip\n");
+ continue;
+ }
+
+ printf("%s:\n", dev->name);
+
+ printf(" ");
+ PRINT_COLUMN("ID");
+ PRINT_COLUMN("Selected");
+ PRINT_COLUMN("Current State");
+ PRINT_COLUMN("Idle State");
+ PRINT_COLUMN("Num States");
+ printf("\n");
+ for (j = 0; j < chip->controllers; j++) {
+ printf(" ");
+ PRINT_COLUMN("%d", j);
+ print_mux(&chip->mux[j]);
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static int do_mux_select(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct mux_control *mux;
+ int ret;
+ unsigned long state;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ mux = cmd_mux_find(argv);
+ if (IS_ERR_OR_NULL(mux)) {
+ printf("Failed to find the specified mux\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = strict_strtoul(argv[3], 16, &state);
+ if (ret) {
+ printf("Invalid state\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = mux_control_select(mux, state);
+ if (ret) {
+ printf("Failed to select requested state\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_mux_deselect(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct mux_control *mux;
+ int ret;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ mux = cmd_mux_find(argv);
+ if (IS_ERR_OR_NULL(mux)) {
+ printf("Failed to find the specified mux\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = mux_control_deselect(mux);
+ if (ret) {
+ printf("Failed to deselect mux\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_LONGHELP(mux,
+ "list - List all Muxes and their states\n"
+ "select <chip> <id> <state> - Select the given mux state\n"
+ "deselect <chip> <id> - Deselect the given mux and reset it to its idle state");
+
+U_BOOT_CMD_WITH_SUBCMDS(mux, "List, select, and deselect muxes", mux_help_text,
+ U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mux_list),
+ U_BOOT_SUBCMD_MKENT(select, 4, 0, do_mux_select),
+ U_BOOT_SUBCMD_MKENT(deselect, 3, 0, do_mux_deselect));
diff --git a/cmd/mvebu/Kconfig b/cmd/mvebu/Kconfig
new file mode 100644
index 00000000000..e83a9829491
--- /dev/null
+++ b/cmd/mvebu/Kconfig
@@ -0,0 +1,82 @@
+menu "MVEBU commands"
+depends on ARCH_MVEBU
+
+config CMD_MVEBU_BUBT
+ bool "bubt"
+ default y
+ select SHA256 if ARMADA_3700
+ select SHA512 if ARMADA_3700
+ select DOS_PARTITION if ARMADA_3700
+ select EFI_PARTITION if ARMADA_3700
+ select PARTITION_TYPE_GUID if ARMADA_3700
+ select MVEBU_EFUSE if ARMADA_38X || ARMADA_3700
+ help
+ bubt - Burn a u-boot image to flash
+ For details about bubt command please see the documentation
+ in doc/mvebu/cmd/bubt.txt
+
+if CMD_MVEBU_BUBT
+
+choice
+ prompt "Flash for image"
+ default MVEBU_SPI_BOOT if MVEBU_SPL_BOOT_DEVICE_SPI
+ default MVEBU_NAND_BOOT if MVEBU_SPL_BOOT_DEVICE_NAND
+ default MVEBU_MMC_BOOT if MVEBU_SPL_BOOT_DEVICE_MMC
+ default MVEBU_SATA_BOOT if MVEBU_SPL_BOOT_DEVICE_SATA
+ default MVEBU_SPI_BOOT
+
+config MVEBU_NAND_BOOT
+ bool "NAND flash boot"
+ depends on NAND_PXA3XX
+ help
+ Enable boot from NAND flash.
+ Allow usage of NAND flash as a target for "bubt" command
+ For details about bubt command please see the documentation
+ in doc/mvebu/cmd/bubt.txt
+
+config MVEBU_SPI_BOOT
+ bool "SPI flash boot"
+ depends on SPI_FLASH
+ help
+ Enable boot from SPI flash.
+ Allow usage of SPI flash as a target for "bubt" command
+ For details about bubt command please see the documentation
+ in doc/mvebu/cmd/bubt.txt
+
+config MVEBU_MMC_BOOT
+ bool "eMMC flash boot"
+ depends on MVEBU_MMC || MMC_SDHCI_XENON
+ help
+ Enable boot from eMMC boot partition
+ Allow usage of eMMC/SD device as a target for "bubt" command
+ For details about bubt command please see the documentation
+ in doc/mvebu/cmd/bubt.txt
+
+config MVEBU_SATA_BOOT
+ bool "SATA flash boot"
+ depends on SCSI
+ help
+ Enable boot from SATA disk.
+ Allow usage of SATA disk as a target for "bubt" command
+ For details about bubt command please see the documentation
+ in doc/mvebu/cmd/bubt.txt
+
+endchoice
+
+config MVEBU_UBOOT_DFLT_NAME
+ string "Default image name for bubt command"
+ default BUILD_TARGET if ARMADA_32BIT && BUILD_TARGET != ""
+ default "flash-image.bin"
+ help
+ This option should contain a default file name to be used with
+ MVEBU "bubt" command if the source file name is omitted
+
+endif
+
+config CMD_MVEBU_COMPHY_RX_TRAINING
+ bool "mvebu_comphy_rx_training"
+ depends on ARMADA_8K
+ help
+ Perform COMPHY RX training sequence
+
+endmenu
diff --git a/cmd/mvebu/Makefile b/cmd/mvebu/Makefile
new file mode 100644
index 00000000000..ca96ad01d91
--- /dev/null
+++ b/cmd/mvebu/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2016 Marvell International Ltd.
+#
+# https://spdx.org/licenses
+
+obj-$(CONFIG_CMD_MVEBU_BUBT) += bubt.o
+obj-$(CONFIG_CMD_MVEBU_COMPHY_RX_TRAINING) += comphy_rx_training.o
diff --git a/cmd/mvebu/bubt.c b/cmd/mvebu/bubt.c
new file mode 100644
index 00000000000..2755c26cdf7
--- /dev/null
+++ b/cmd/mvebu/bubt.c
@@ -0,0 +1,1265 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016 Marvell International Ltd.
+ * https://spdx.org/licenses
+ */
+
+#include <config.h>
+#include <command.h>
+#include <env.h>
+#include <image.h>
+#include <net.h>
+#include <vsprintf.h>
+#include <errno.h>
+#include <dm.h>
+#include <fuse.h>
+#include <mach/efuse.h>
+
+#include <spi_flash.h>
+#include <spi.h>
+#include <nand.h>
+#include <scsi.h>
+#include <usb.h>
+#include <fs.h>
+#include <mmc.h>
+#ifdef CONFIG_BLK
+#include <blk.h>
+#endif
+#include <u-boot/sha1.h>
+#include <u-boot/sha256.h>
+#include <u-boot/sha512.h>
+
+#if defined(CONFIG_ARMADA_8K)
+#define MAIN_HDR_MAGIC 0xB105B002
+
+struct mvebu_image_header {
+ u32 magic; /* 0-3 */
+ u32 prolog_size; /* 4-7 */
+ u32 prolog_checksum; /* 8-11 */
+ u32 boot_image_size; /* 12-15 */
+ u32 boot_image_checksum; /* 16-19 */
+ u32 rsrvd0; /* 20-23 */
+ u32 load_addr; /* 24-27 */
+ u32 exec_addr; /* 28-31 */
+ u8 uart_cfg; /* 32 */
+ u8 baudrate; /* 33 */
+ u8 ext_count; /* 34 */
+ u8 aux_flags; /* 35 */
+ u32 io_arg_0; /* 36-39 */
+ u32 io_arg_1; /* 40-43 */
+ u32 io_arg_2; /* 43-47 */
+ u32 io_arg_3; /* 48-51 */
+ u32 rsrvd1; /* 52-55 */
+ u32 rsrvd2; /* 56-59 */
+ u32 rsrvd3; /* 60-63 */
+};
+#elif defined(CONFIG_ARMADA_3700) /* A3700 */
+#define HASH_SUM_LEN 16
+#define IMAGE_VERSION_3_6_0 0x030600
+#define IMAGE_VERSION_3_5_0 0x030500
+
+struct tim_boot_flash_sign {
+ unsigned int id;
+ const char *name;
+};
+
+struct tim_boot_flash_sign tim_boot_flash_signs[] = {
+ { 0x454d4d08, "mmc" },
+ { 0x454d4d0b, "mmc" },
+ { 0x5350490a, "spi" },
+ { 0x5350491a, "nand" },
+ { 0x55415223, "uart" },
+ { 0x53415432, "sata" },
+ {},
+};
+
+struct common_tim_data {
+ u32 version;
+ u32 identifier;
+ u32 trusted;
+ u32 issue_date;
+ u32 oem_unique_id;
+ u32 reserved[5]; /* Reserve 20 bytes */
+ u32 boot_flash_sign;
+ u32 num_images;
+ u32 num_keys;
+ u32 size_of_reserved;
+};
+
+struct mvebu_image_info {
+ u32 image_id;
+ u32 next_image_id;
+ u32 flash_entry_addr;
+ u32 load_addr;
+ u32 image_size;
+ u32 image_size_to_hash;
+ u32 hash_algorithm_id;
+ u32 hash[HASH_SUM_LEN]; /* Reserve 512 bits for the hash */
+ u32 partition_number;
+ u32 enc_algorithm_id;
+ u32 encrypt_start_offset;
+ u32 encrypt_size;
+};
+#elif defined(CONFIG_ARMADA_32BIT)
+
+/* Structure of the main header, version 1 (Armada 370/XP/375/38x/39x) */
+struct a38x_main_hdr_v1 {
+ u8 blockid; /* 0x0 */
+ u8 flags; /* 0x1 */
+ u16 nandpagesize; /* 0x2-0x3 */
+ u32 blocksize; /* 0x4-0x7 */
+ u8 version; /* 0x8 */
+ u8 headersz_msb; /* 0x9 */
+ u16 headersz_lsb; /* 0xA-0xB */
+ u32 srcaddr; /* 0xC-0xF */
+ u32 destaddr; /* 0x10-0x13 */
+ u32 execaddr; /* 0x14-0x17 */
+ u8 options; /* 0x18 */
+ u8 nandblocksize; /* 0x19 */
+ u8 nandbadblklocation; /* 0x1A */
+ u8 reserved4; /* 0x1B */
+ u16 reserved5; /* 0x1C-0x1D */
+ u8 ext; /* 0x1E */
+ u8 checksum; /* 0x1F */
+};
+
+/*
+ * Header for the optional headers, version 1 (Armada 370/XP/375/38x/39x)
+ */
+struct a38x_opt_hdr_v1 {
+ u8 headertype;
+ u8 headersz_msb;
+ u16 headersz_lsb;
+ u8 data[0];
+};
+#define A38X_OPT_HDR_V1_SECURE_TYPE 0x1
+
+struct a38x_boot_mode {
+ unsigned int id;
+ const char *name;
+};
+
+/* The blockid header field values used to indicate boot device of image */
+struct a38x_boot_mode a38x_boot_modes[] = {
+ { 0x4D, "i2c" },
+ { 0x5A, "spi" },
+ { 0x69, "uart" },
+ { 0x78, "sata" },
+ { 0x8B, "nand" },
+ { 0x9C, "pex" },
+ { 0xAE, "mmc" },
+ {},
+};
+
+#endif
+
+struct bubt_dev {
+ char name[8];
+ size_t (*read)(const char *file_name);
+ int (*write)(size_t image_size);
+ int (*active)(void);
+};
+
+static ulong get_load_addr(void)
+{
+ const char *addr_str;
+ unsigned long addr;
+
+ addr_str = env_get("loadaddr");
+ if (addr_str)
+ addr = hextoul(addr_str, NULL);
+ else
+ addr = CONFIG_SYS_LOAD_ADDR;
+
+ return addr;
+}
+
+/********************************************************************
+ * eMMC services
+ ********************************************************************/
+#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(MMC_WRITE)
+static int mmc_burn_image(size_t image_size)
+{
+ struct mmc *mmc;
+ lbaint_t start_lba;
+ lbaint_t blk_count;
+ ulong blk_written;
+ int err;
+ const u8 mmc_dev_num = CONFIG_ENV_MMC_DEVICE_INDEX;
+#ifdef CONFIG_BLK
+ struct blk_desc *blk_desc;
+#endif
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+ u8 part;
+ u8 orig_part;
+#endif
+
+ mmc = find_mmc_device(mmc_dev_num);
+ if (!mmc) {
+ printf("No SD/MMC/eMMC card found\n");
+ return -ENOMEDIUM;
+ }
+
+ err = mmc_init(mmc);
+ if (err) {
+ printf("%s(%d) init failed\n", IS_SD(mmc) ? "SD" : "MMC",
+ mmc_dev_num);
+ return err;
+ }
+
+#ifdef CONFIG_BLK
+ blk_desc = mmc_get_blk_desc(mmc);
+ if (!blk_desc) {
+ printf("Error - failed to obtain block descriptor\n");
+ return -ENODEV;
+ }
+#endif
+
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+#ifdef CONFIG_BLK
+ orig_part = blk_desc->hwpart;
+#else
+ orig_part = mmc->block_dev.hwpart;
+#endif
+
+ part = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config);
+ if (part == EMMC_BOOT_PART_USER)
+ part = EMMC_HWPART_DEFAULT;
+
+#ifdef CONFIG_BLK
+ err = blk_dselect_hwpart(blk_desc, part);
+#else
+ err = mmc_switch_part(mmc, part);
+#endif
+
+ if (err) {
+ printf("Error - MMC partition switch failed\n");
+ return err;
+ }
+#endif
+
+ /* SD reserves LBA-0 for MBR and boots from LBA-1,
+ * MMC/eMMC boots from LBA-0 and LBA-4096
+ */
+ if (IS_SD(mmc))
+ start_lba = 1;
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+ else if (part)
+ start_lba = 0;
+#endif
+ else
+ start_lba = 4096;
+#ifdef CONFIG_BLK
+ blk_count = image_size / mmc->write_bl_len;
+ if (image_size % mmc->write_bl_len)
+ blk_count += 1;
+
+ blk_written = blk_dwrite(blk_desc, start_lba, blk_count,
+ (void *)get_load_addr());
+#else
+ blk_count = image_size / mmc->block_dev.blksz;
+ if (image_size % mmc->block_dev.blksz)
+ blk_count += 1;
+
+ blk_written = mmc->block_dev.block_write(mmc_dev_num,
+ start_lba, blk_count,
+ (void *)get_load_addr());
+#endif /* CONFIG_BLK */
+
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+#ifdef CONFIG_BLK
+ err = blk_dselect_hwpart(blk_desc, orig_part);
+#else
+ err = mmc_switch_part(mmc, orig_part);
+#endif
+ if (err)
+ printf("Error - MMC failed to switch back to original partition\n");
+#endif
+
+ if (blk_written != blk_count) {
+ printf("Error - written %#lx blocks\n", blk_written);
+ return -ENOSPC;
+ }
+ printf("Done!\n");
+
+ return 0;
+}
+
+static size_t mmc_read_file(const char *file_name)
+{
+ loff_t act_read = 0;
+ int rc;
+ struct mmc *mmc;
+ const u8 mmc_dev_num = CONFIG_ENV_MMC_DEVICE_INDEX;
+
+ mmc = find_mmc_device(mmc_dev_num);
+ if (!mmc) {
+ printf("No SD/MMC/eMMC card found\n");
+ return 0;
+ }
+
+ if (mmc_init(mmc)) {
+ printf("%s(%d) init failed\n", IS_SD(mmc) ? "SD" : "MMC",
+ mmc_dev_num);
+ return 0;
+ }
+
+ /* Load from data partition (0) */
+ if (fs_set_blk_dev("mmc", "0", FS_TYPE_ANY)) {
+ printf("Error: MMC 0 not found\n");
+ return 0;
+ }
+
+ /* Perfrom file read */
+ rc = fs_read(file_name, get_load_addr(), 0, 0, &act_read);
+ if (rc)
+ return 0;
+
+ return act_read;
+}
+
+static int is_mmc_active(void)
+{
+ return 1;
+}
+#else /* CONFIG_DM_MMC */
+static int mmc_burn_image(size_t image_size)
+{
+ return -ENODEV;
+}
+
+static size_t mmc_read_file(const char *file_name)
+{
+ return 0;
+}
+
+static int is_mmc_active(void)
+{
+ return 0;
+}
+#endif /* CONFIG_DM_MMC */
+
+/********************************************************************
+ * SATA services
+ ********************************************************************/
+#if defined(CONFIG_SCSI) && defined(CONFIG_BLK)
+static int sata_burn_image(size_t image_size)
+{
+#if defined(CONFIG_ARMADA_3700) || defined(CONFIG_ARMADA_32BIT)
+ lbaint_t start_lba;
+ lbaint_t blk_count;
+ ulong blk_written;
+ struct blk_desc *blk_desc;
+#ifdef CONFIG_ARMADA_3700
+ struct disk_partition info;
+ int part;
+#endif
+
+ scsi_scan(false);
+
+ blk_desc = blk_get_devnum_by_uclass_id(UCLASS_SCSI, 0);
+ if (!blk_desc)
+ return -ENODEV;
+
+#ifdef CONFIG_ARMADA_3700
+ /*
+ * 64-bit Armada 3700 BootROM loads SATA firmware from
+ * GPT 'Marvell Armada 3700 Boot partition' or from
+ * MBR 'M' (0x4d) partition.
+ */
+ switch (blk_desc->part_type) {
+ case PART_TYPE_DOS:
+ for (part = 1; part <= 4; part++) {
+ info.sys_ind = 0;
+ if (part_get_info(blk_desc, part, &info))
+ continue;
+ if (info.sys_ind == 'M')
+ break;
+ }
+ if (part > 4) {
+ printf("Error - cannot find MBR 'M' (0x4d) partition on SATA disk\n");
+ return -ENODEV;
+ }
+ start_lba = info.start;
+ break;
+ case PART_TYPE_EFI:
+ for (part = 1; part <= 64; part++) {
+ info.type_guid[0] = 0;
+ if (part_get_info(blk_desc, part, &info))
+ continue;
+ /* Check for GPT type GUID of 'Marvell Armada 3700 Boot partition' */
+ if (strcmp(info.type_guid, "6828311A-BA55-42A4-BCDE-A89BB5EDECAE") == 0)
+ break;
+ }
+ if (part > 64) {
+ printf("Error - cannot find GPT 'Marvell Armada 3700 Boot partition' on SATA disk\n");
+ return -ENODEV;
+ }
+ start_lba = info.start;
+ break;
+ default:
+ printf("Error - no partitions on SATA disk\n");
+ return -ENODEV;
+ }
+#else
+ /* 32-bit Armada BootROM loads SATA firmware from the sector 1. */
+ start_lba = 1;
+#endif
+
+ blk_count = image_size / blk_desc->blksz;
+ if (image_size % blk_desc->blksz)
+ blk_count += 1;
+
+ blk_written = blk_dwrite(blk_desc, start_lba, blk_count,
+ (void *)get_load_addr());
+
+ if (blk_written != blk_count) {
+ printf("Error - written %#lx blocks\n", blk_written);
+ return -ENOSPC;
+ }
+
+ printf("Done!\n");
+ return 0;
+#else
+ return -ENODEV;
+#endif
+}
+
+static size_t sata_read_file(const char *file_name)
+{
+ loff_t act_read = 0;
+ struct udevice *dev;
+ int rc;
+
+ /* try to recognize storage devices immediately */
+ scsi_scan(false);
+
+ /* Try to recognize storage devices immediately */
+ blk_first_device(UCLASS_SCSI, &dev);
+ if (!dev) {
+ printf("Error: SATA device not found\n");
+ return 0;
+ }
+
+ /* Always load from scsi 0 */
+ if (fs_set_blk_dev("scsi", "0", FS_TYPE_ANY)) {
+ printf("Error: SATA 0 not found\n");
+ return 0;
+ }
+
+ /* Perfrom file read */
+ rc = fs_read(file_name, get_load_addr(), 0, 0, &act_read);
+ if (rc)
+ return 0;
+
+ return act_read;
+}
+
+static int is_sata_active(void)
+{
+ return 1;
+}
+#else /* CONFIG_SCSI */
+static int sata_burn_image(size_t image_size)
+{
+ return -ENODEV;
+}
+
+static size_t sata_read_file(const char *file_name)
+{
+ return 0;
+}
+
+static int is_sata_active(void)
+{
+ return 0;
+}
+#endif /* CONFIG_SCSI */
+
+/********************************************************************
+ * SPI services
+ ********************************************************************/
+#ifdef CONFIG_SPI_FLASH
+static int spi_burn_image(size_t image_size)
+{
+ int ret;
+ struct spi_flash *flash;
+ u32 erase_bytes;
+
+ /* Probe the SPI bus to get the flash device */
+ flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS,
+ CONFIG_SF_DEFAULT_CS,
+ CONFIG_SF_DEFAULT_SPEED,
+ CONFIG_SF_DEFAULT_MODE);
+ if (!flash) {
+ printf("Failed to probe SPI Flash\n");
+ return -ENOMEDIUM;
+ }
+
+ erase_bytes = image_size +
+ (flash->erase_size - image_size % flash->erase_size);
+ printf("Erasing %d bytes (%d blocks) at offset 0 ...",
+ erase_bytes, erase_bytes / flash->erase_size);
+ ret = spi_flash_erase(flash, 0, erase_bytes);
+ if (ret)
+ printf("Error!\n");
+ else
+ printf("Done!\n");
+
+ printf("Writing %d bytes from 0x%lx to offset 0 ...",
+ (int)image_size, get_load_addr());
+ ret = spi_flash_write(flash, 0, image_size, (void *)get_load_addr());
+ if (ret)
+ printf("Error!\n");
+ else
+ printf("Done!\n");
+
+ return ret;
+}
+
+static int is_spi_active(void)
+{
+ return 1;
+}
+
+#else /* CONFIG_SPI_FLASH */
+static int spi_burn_image(size_t image_size)
+{
+ return -ENODEV;
+}
+
+static int is_spi_active(void)
+{
+ return 0;
+}
+#endif /* CONFIG_SPI_FLASH */
+
+/********************************************************************
+ * NAND services
+ ********************************************************************/
+#ifdef CONFIG_CMD_NAND
+static int nand_burn_image(size_t image_size)
+{
+ int ret;
+ uint32_t block_size;
+ struct mtd_info *mtd;
+
+ mtd = get_nand_dev_by_index(nand_curr_device);
+ if (!mtd) {
+ puts("\nno devices available\n");
+ return -ENOMEDIUM;
+ }
+ block_size = mtd->erasesize;
+
+ /* Align U-Boot size to currently used blocksize */
+ image_size = ((image_size + (block_size - 1)) & (~(block_size - 1)));
+
+ /* Erase the U-Boot image space */
+ printf("Erasing 0x%x - 0x%x:...", 0, (int)image_size);
+ ret = nand_erase(mtd, 0, image_size);
+ if (ret) {
+ printf("Error!\n");
+ goto error;
+ }
+ printf("Done!\n");
+
+ /* Write the image to flash */
+ printf("Writing %d bytes from 0x%lx to offset 0 ... ",
+ (int)image_size, get_load_addr());
+ ret = nand_write(mtd, 0, &image_size, (void *)get_load_addr());
+ if (ret)
+ printf("Error!\n");
+ else
+ printf("Done!\n");
+
+error:
+ return ret;
+}
+
+static int is_nand_active(void)
+{
+ return 1;
+}
+
+#else /* CONFIG_CMD_NAND */
+static int nand_burn_image(size_t image_size)
+{
+ return -ENODEV;
+}
+
+static int is_nand_active(void)
+{
+ return 0;
+}
+#endif /* CONFIG_CMD_NAND */
+
+/********************************************************************
+ * USB services
+ ********************************************************************/
+#if defined(CONFIG_USB_STORAGE) && defined(CONFIG_BLK)
+static size_t usb_read_file(const char *file_name)
+{
+ loff_t act_read = 0;
+ struct udevice *dev;
+ int rc;
+
+ usb_stop();
+
+ if (usb_init() < 0) {
+ printf("Error: usb_init failed\n");
+ return 0;
+ }
+
+ /* Try to recognize storage devices immediately */
+ blk_first_device(UCLASS_USB, &dev);
+ if (!dev) {
+ printf("Error: USB storage device not found\n");
+ return 0;
+ }
+
+ /* Always load from usb 0 */
+ if (fs_set_blk_dev("usb", "0", FS_TYPE_ANY)) {
+ printf("Error: USB 0 not found\n");
+ return 0;
+ }
+
+ /* Perfrom file read */
+ rc = fs_read(file_name, get_load_addr(), 0, 0, &act_read);
+ if (rc)
+ return 0;
+
+ return act_read;
+}
+
+static int is_usb_active(void)
+{
+ return 1;
+}
+
+#else /* defined(CONFIG_USB_STORAGE) && defined (CONFIG_BLK) */
+static size_t usb_read_file(const char *file_name)
+{
+ return 0;
+}
+
+static int is_usb_active(void)
+{
+ return 0;
+}
+#endif /* defined(CONFIG_USB_STORAGE) && defined (CONFIG_BLK) */
+
+/********************************************************************
+ * Network services
+ ********************************************************************/
+#ifdef CONFIG_CMD_NET
+static size_t tftp_read_file(const char *file_name)
+{
+ int ret;
+
+ /*
+ * update global variable image_load_addr before tftp file from network
+ */
+ image_load_addr = get_load_addr();
+ ret = net_loop(TFTPGET);
+ return ret > 0 ? ret : 0;
+}
+
+static int is_tftp_active(void)
+{
+ return 1;
+}
+
+#else
+static size_t tftp_read_file(const char *file_name)
+{
+ return 0;
+}
+
+static int is_tftp_active(void)
+{
+ return 0;
+}
+#endif /* CONFIG_CMD_NET */
+
+enum bubt_devices {
+ BUBT_DEV_NET = 0,
+ BUBT_DEV_USB,
+ BUBT_DEV_MMC,
+ BUBT_DEV_SATA,
+ BUBT_DEV_SPI,
+ BUBT_DEV_NAND,
+
+ BUBT_MAX_DEV
+};
+
+static struct bubt_dev bubt_devs[BUBT_MAX_DEV] = {
+ {"tftp", tftp_read_file, NULL, is_tftp_active},
+ {"usb", usb_read_file, NULL, is_usb_active},
+ {"mmc", mmc_read_file, mmc_burn_image, is_mmc_active},
+ {"sata", sata_read_file, sata_burn_image, is_sata_active},
+ {"spi", NULL, spi_burn_image, is_spi_active},
+ {"nand", NULL, nand_burn_image, is_nand_active},
+};
+
+static int bubt_write_file(struct bubt_dev *dst, size_t image_size)
+{
+ if (!dst->write) {
+ printf("Error: Write not supported on device %s\n", dst->name);
+ return -ENOTSUPP;
+ }
+
+ return dst->write(image_size);
+}
+
+#if defined(CONFIG_ARMADA_8K)
+static u32 do_checksum32(u32 *start, int32_t len)
+{
+ u32 sum = 0;
+ u32 *startp = start;
+
+ do {
+ sum += *startp;
+ startp++;
+ len -= 4;
+ } while (len > 0);
+
+ return sum;
+}
+
+static int check_image_header(void)
+{
+ struct mvebu_image_header *hdr =
+ (struct mvebu_image_header *)get_load_addr();
+ u32 checksum;
+ u32 checksum_ref;
+
+ /*
+ * For now compare checksum, and magic. Later we can
+ * verify more stuff on the header like interface type, etc
+ */
+ if (hdr->magic != MAIN_HDR_MAGIC) {
+ printf("ERROR: Bad MAGIC 0x%08x != 0x%08x\n",
+ hdr->magic, MAIN_HDR_MAGIC);
+ return -ENOEXEC;
+ }
+
+ checksum_ref = hdr->prolog_checksum;
+ checksum = do_checksum32((u32 *)hdr, hdr->prolog_size);
+ checksum -= hdr->prolog_checksum;
+ if (checksum != checksum_ref) {
+ printf("Error: Bad Prolog checksum. 0x%x != 0x%x\n",
+ checksum, checksum_ref);
+ return -ENOEXEC;
+ }
+
+ checksum_ref = hdr->boot_image_checksum;
+ checksum = do_checksum32((u32 *)((u8 *)hdr + hdr->prolog_size), hdr->boot_image_size);
+ if (checksum != checksum_ref) {
+ printf("Error: Bad Image checksum. 0x%x != 0x%x\n",
+ checksum, checksum_ref);
+ return -ENOEXEC;
+ }
+
+ printf("Image checksum...OK!\n");
+
+ return 0;
+}
+#elif defined(CONFIG_ARMADA_3700) /* Armada 3700 */
+static int check_image_header(void)
+{
+ struct common_tim_data *hdr = (struct common_tim_data *)get_load_addr();
+ int image_num;
+ u8 hash_160_output[SHA1_SUM_LEN];
+ u8 hash_256_output[SHA256_SUM_LEN];
+ u8 hash_512_output[SHA512_SUM_LEN];
+ sha1_context hash1_text;
+ sha256_context hash256_text;
+ sha512_context hash512_text;
+ u8 *hash_output;
+ u32 hash_algorithm_id;
+ u32 image_size_to_hash;
+ u32 flash_entry_addr;
+ u32 *hash_value;
+ u32 internal_hash[HASH_SUM_LEN];
+ const u8 *buff;
+ u32 num_of_image = hdr->num_images;
+ u32 version = hdr->version;
+ u32 trusted = hdr->trusted;
+
+ /* bubt checksum validation only supports nontrusted images */
+ if (trusted == 1) {
+ printf("bypass image validation, ");
+ printf("only untrusted image is supported now\n");
+ return 0;
+ }
+ /* only supports image version 3.5 and 3.6 */
+ if (version != IMAGE_VERSION_3_5_0 && version != IMAGE_VERSION_3_6_0) {
+ printf("Error: Unsupported Image version = 0x%08x\n", version);
+ return -ENOEXEC;
+ }
+ /* validate images hash value */
+ for (image_num = 0; image_num < num_of_image; image_num++) {
+ struct mvebu_image_info *info =
+ (struct mvebu_image_info *)(get_load_addr() +
+ sizeof(struct common_tim_data) +
+ image_num * sizeof(struct mvebu_image_info));
+ hash_algorithm_id = info->hash_algorithm_id;
+ image_size_to_hash = info->image_size_to_hash;
+ flash_entry_addr = info->flash_entry_addr;
+ hash_value = info->hash;
+ buff = (const u8 *)(get_load_addr() + flash_entry_addr);
+
+ if (image_num == 0) {
+ /*
+ * The first image includes hash values in its content.
+ * For hash calculation, we need to save the original
+ * hash values to a local variable that will be
+ * copied back for comparsion and set all zeros to
+ * the orignal hash values for calculating new value.
+ * First image original format :
+ * x...x (datum1) x...x(orig. hash values) x...x(datum2)
+ * Replaced first image format :
+ * x...x (datum1) 0...0(hash values) x...x(datum2)
+ */
+ memcpy(internal_hash, hash_value,
+ sizeof(internal_hash));
+ memset(hash_value, 0, sizeof(internal_hash));
+ }
+ if (image_size_to_hash == 0) {
+ printf("Warning: Image_%d hash checksum is disabled, ",
+ image_num);
+ printf("skip the image validation.\n");
+ continue;
+ }
+ switch (hash_algorithm_id) {
+ case SHA1_SUM_LEN:
+ sha1_starts(&hash1_text);
+ sha1_update(&hash1_text, buff, image_size_to_hash);
+ sha1_finish(&hash1_text, hash_160_output);
+ hash_output = hash_160_output;
+ break;
+ case SHA256_SUM_LEN:
+ sha256_starts(&hash256_text);
+ sha256_update(&hash256_text, buff, image_size_to_hash);
+ sha256_finish(&hash256_text, hash_256_output);
+ hash_output = hash_256_output;
+ break;
+ case SHA512_SUM_LEN:
+ sha512_starts(&hash512_text);
+ sha512_update(&hash512_text, buff, image_size_to_hash);
+ sha512_finish(&hash512_text, hash_512_output);
+ hash_output = hash_512_output;
+ break;
+ default:
+ printf("Error: Unsupported hash_algorithm_id = %d\n",
+ hash_algorithm_id);
+ return -ENOEXEC;
+ }
+ if (image_num == 0)
+ memcpy(hash_value, internal_hash,
+ sizeof(internal_hash));
+ if (memcmp(hash_value, hash_output, hash_algorithm_id) != 0) {
+ printf("Error: Image_%d checksum is not correct\n",
+ image_num);
+ return -ENOEXEC;
+ }
+ }
+ printf("Image checksum...OK!\n");
+
+ return 0;
+}
+#elif defined(CONFIG_ARMADA_32BIT)
+static size_t a38x_header_size(const struct a38x_main_hdr_v1 *h)
+{
+ if (h->version == 1)
+ return (h->headersz_msb << 16) | le16_to_cpu(h->headersz_lsb);
+
+ printf("Error: Invalid A38x image (header version 0x%x unknown)!\n",
+ h->version);
+ return 0;
+}
+
+static uint8_t image_checksum8(const void *start, size_t len)
+{
+ u8 csum = 0;
+ const u8 *p = start;
+
+ while (len) {
+ csum += *p;
+ ++p;
+ --len;
+ }
+
+ return csum;
+}
+
+static uint32_t image_checksum32(const void *start, size_t len)
+{
+ u32 csum = 0;
+ const u32 *p = start;
+
+ while (len) {
+ csum += *p;
+ ++p;
+ len -= sizeof(u32);
+ }
+
+ return csum;
+}
+
+static int check_image_header(void)
+{
+ u8 checksum;
+ u32 checksum32, exp_checksum32;
+ u32 offset, size;
+ const struct a38x_main_hdr_v1 *hdr =
+ (struct a38x_main_hdr_v1 *)get_load_addr();
+ const size_t hdr_size = a38x_header_size(hdr);
+
+ if (!hdr_size)
+ return -ENOEXEC;
+
+ checksum = image_checksum8(hdr, hdr_size);
+ checksum -= hdr->checksum;
+ if (checksum != hdr->checksum) {
+ printf("Error: Bad A38x image header checksum. 0x%x != 0x%x\n",
+ checksum, hdr->checksum);
+ return -ENOEXEC;
+ }
+
+ offset = le32_to_cpu(hdr->srcaddr);
+ size = le32_to_cpu(hdr->blocksize);
+
+ if (hdr->blockid == 0x78) { /* SATA id */
+ struct blk_desc *blk_dev = IS_ENABLED(CONFIG_BLK) ? blk_get_devnum_by_uclass_id(UCLASS_SCSI, 0) : NULL;
+ unsigned long blksz = blk_dev ? blk_dev->blksz : 512;
+ offset *= blksz;
+ }
+
+ if (offset % 4 != 0 || size < 4 || size % 4 != 0) {
+ printf("Error: Bad A38x image blocksize.\n");
+ return -ENOEXEC;
+ }
+
+ checksum32 = image_checksum32((u8 *)hdr + offset, size - 4);
+ exp_checksum32 = *(u32 *)((u8 *)hdr + offset + size - 4);
+ if (checksum32 != exp_checksum32) {
+ printf("Error: Bad A38x image data checksum. 0x%08x != 0x%08x\n",
+ checksum32, exp_checksum32);
+ return -ENOEXEC;
+ }
+
+ printf("Image checksum...OK!\n");
+ return 0;
+}
+
+#if defined(CONFIG_ARMADA_38X)
+static int a38x_image_is_secure(const struct a38x_main_hdr_v1 *hdr)
+{
+ const size_t hdr_size = a38x_header_size(hdr);
+ struct a38x_opt_hdr_v1 *ohdr;
+ u32 ohdr_size;
+
+ if (hdr->version != 1)
+ return 0;
+
+ if (!hdr->ext)
+ return 0;
+
+ ohdr = (struct a38x_opt_hdr_v1 *)(hdr + 1);
+ do {
+ if (ohdr->headertype == A38X_OPT_HDR_V1_SECURE_TYPE)
+ return 1;
+
+ ohdr_size = (ohdr->headersz_msb << 16) | le16_to_cpu(ohdr->headersz_lsb);
+
+ if (!*((u8 *)ohdr + ohdr_size - 4))
+ break;
+
+ ohdr = (struct a38x_opt_hdr_v1 *)((u8 *)ohdr + ohdr_size);
+ if ((u8 *)ohdr >= (u8 *)hdr + hdr_size)
+ break;
+ } while (1);
+
+ return 0;
+}
+#endif
+#else /* Not ARMADA? */
+static int check_image_header(void)
+{
+ printf("bubt cmd does not support this SoC device or family!\n");
+ return -ENOEXEC;
+}
+#endif
+
+#if defined(CONFIG_ARMADA_3700) || defined(CONFIG_ARMADA_38X)
+static u64 fuse_read_u64(u32 bank)
+{
+ u32 val[2];
+ int ret;
+
+ ret = fuse_read(bank, 0, &val[0]);
+ if (ret < 0)
+ return -1;
+
+ ret = fuse_read(bank, 1, &val[1]);
+ if (ret < 0)
+ return -1;
+
+ return ((u64)val[1] << 32) | val[0];
+}
+#endif
+
+#if defined(CONFIG_ARMADA_3700)
+static inline u8 maj3(u8 val)
+{
+ /* return majority vote of 3 bits */
+ return ((val & 0x7) == 3 || (val & 0x7) > 4) ? 1 : 0;
+}
+#endif
+
+static int bubt_check_boot_mode(const struct bubt_dev *dst)
+{
+#if defined(CONFIG_ARMADA_3700) || defined(CONFIG_ARMADA_32BIT)
+ int mode;
+#if defined(CONFIG_ARMADA_3700) || defined(CONFIG_ARMADA_38X)
+ int secure_mode;
+#endif
+#if defined(CONFIG_ARMADA_3700)
+ const struct tim_boot_flash_sign *boot_modes = tim_boot_flash_signs;
+ const struct common_tim_data *hdr =
+ (struct common_tim_data *)get_load_addr();
+ u32 id = hdr->boot_flash_sign;
+ int is_secure = hdr->trusted != 0;
+ u64 otp_secure_bits = fuse_read_u64(1);
+ int otp_secure_boot = ((maj3(otp_secure_bits >> 0) << 0) |
+ (maj3(otp_secure_bits >> 4) << 1)) == 2;
+ unsigned int otp_boot_device = (maj3(otp_secure_bits >> 48) << 0) |
+ (maj3(otp_secure_bits >> 52) << 1) |
+ (maj3(otp_secure_bits >> 56) << 2) |
+ (maj3(otp_secure_bits >> 60) << 3);
+#elif defined(CONFIG_ARMADA_32BIT)
+ const struct a38x_boot_mode *boot_modes = a38x_boot_modes;
+ const struct a38x_main_hdr_v1 *hdr =
+ (struct a38x_main_hdr_v1 *)get_load_addr();
+ u32 id = hdr->blockid;
+#if defined(CONFIG_ARMADA_38X)
+ int is_secure = a38x_image_is_secure(hdr);
+ u64 otp_secure_bits = fuse_read_u64(EFUSE_LINE_SECURE_BOOT);
+ int otp_secure_boot = otp_secure_bits & 0x1;
+ unsigned int otp_boot_device = (otp_secure_bits >> 8) & 0x7;
+#endif
+#endif
+
+ for (mode = 0; boot_modes[mode].name; mode++) {
+ if (boot_modes[mode].id == id)
+ break;
+ }
+
+ if (!boot_modes[mode].name) {
+ printf("Error: unknown boot device in image header: 0x%x\n", id);
+ return -ENOEXEC;
+ }
+
+ if (strcmp(boot_modes[mode].name, dst->name) != 0) {
+ printf("Error: image meant to be booted from \"%s\", not \"%s\"!\n",
+ boot_modes[mode].name, dst->name);
+ return -ENOEXEC;
+ }
+
+#if defined(CONFIG_ARMADA_38X) || defined(CONFIG_ARMADA_3700)
+ if (otp_secure_bits == (u64)-1) {
+ printf("Error: cannot read OTP secure bits\n");
+ return -ENOEXEC;
+ } else {
+ if (otp_secure_boot && !is_secure) {
+ printf("Error: secure boot is enabled in OTP but image does not have secure boot header!\n");
+ return -ENOEXEC;
+ } else if (!otp_secure_boot && is_secure) {
+#if defined(CONFIG_ARMADA_3700)
+ /*
+ * Armada 3700 BootROM rejects trusted image when secure boot is not enabled.
+ * Armada 385 BootROM accepts image with secure boot header also when secure boot is not enabled.
+ */
+ printf("Error: secure boot is disabled in OTP but image has secure boot header!\n");
+ return -ENOEXEC;
+#endif
+ } else if (otp_boot_device && otp_boot_device != id) {
+ for (secure_mode = 0; boot_modes[secure_mode].name; secure_mode++) {
+ if (boot_modes[secure_mode].id == otp_boot_device)
+ break;
+ }
+ printf("Error: boot source is set to \"%s\" in OTP but image is for \"%s\"!\n",
+ boot_modes[secure_mode].name ?: "unknown", dst->name);
+ return -ENOEXEC;
+ }
+ }
+#endif
+#endif
+ return 0;
+}
+
+static int bubt_verify(const struct bubt_dev *dst)
+{
+ int err;
+
+ /* Check a correct image header exists */
+ err = check_image_header();
+ if (err) {
+ printf("Error: Image header verification failed\n");
+ return err;
+ }
+
+ err = bubt_check_boot_mode(dst);
+ if (err) {
+ printf("Error: Image boot mode verification failed\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int bubt_read_file(struct bubt_dev *src)
+{
+ size_t image_size;
+
+ if (!src->read) {
+ printf("Error: Read not supported on device \"%s\"\n",
+ src->name);
+ return 0;
+ }
+
+ image_size = src->read(net_boot_file_name);
+ if (image_size <= 0) {
+ printf("Error: Failed to read file %s from %s\n",
+ net_boot_file_name, src->name);
+ return 0;
+ }
+
+ return image_size;
+}
+
+static int bubt_is_dev_active(struct bubt_dev *dev)
+{
+ if (!dev->active) {
+ printf("Device \"%s\" not supported by U-Boot image\n",
+ dev->name);
+ return 0;
+ }
+
+ if (!dev->active()) {
+ printf("Device \"%s\" is inactive\n", dev->name);
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct bubt_dev *find_bubt_dev(char *dev_name)
+{
+ int dev;
+
+ for (dev = 0; dev < BUBT_MAX_DEV; dev++) {
+ if (strcmp(bubt_devs[dev].name, dev_name) == 0)
+ return &bubt_devs[dev];
+ }
+
+ return 0;
+}
+
+#define DEFAULT_BUBT_SRC "tftp"
+
+#ifndef DEFAULT_BUBT_DST
+#ifdef CONFIG_MVEBU_SPI_BOOT
+#define DEFAULT_BUBT_DST "spi"
+#elif defined(CONFIG_MVEBU_NAND_BOOT)
+#define DEFAULT_BUBT_DST "nand"
+#elif defined(CONFIG_MVEBU_MMC_BOOT)
+#define DEFAULT_BUBT_DST "mmc"
+#elif defined(CONFIG_MVEBU_SATA_BOOT)
+#define DEFAULT_BUBT_DST "sata"
+#else
+#define DEFAULT_BUBT_DST "error"
+#endif
+#endif /* DEFAULT_BUBT_DST */
+
+static int do_bubt_cmd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct bubt_dev *src, *dst;
+ size_t image_size;
+ char src_dev_name[8];
+ char dst_dev_name[8];
+ char *name;
+ int err;
+
+ if (argc < 2)
+ copy_filename(net_boot_file_name,
+ CONFIG_MVEBU_UBOOT_DFLT_NAME,
+ sizeof(net_boot_file_name));
+ else
+ copy_filename(net_boot_file_name, argv[1],
+ sizeof(net_boot_file_name));
+
+ if (argc >= 3) {
+ strncpy(dst_dev_name, argv[2], 8);
+ } else {
+ name = DEFAULT_BUBT_DST;
+ strncpy(dst_dev_name, name, 8);
+ }
+
+ if (argc >= 4)
+ strncpy(src_dev_name, argv[3], 8);
+ else
+ strncpy(src_dev_name, DEFAULT_BUBT_SRC, 8);
+
+ /* Figure out the destination device */
+ dst = find_bubt_dev(dst_dev_name);
+ if (!dst) {
+ printf("Error: Unknown destination \"%s\"\n", dst_dev_name);
+ return 1;
+ }
+
+ if (!bubt_is_dev_active(dst))
+ return 1;
+
+ /* Figure out the source device */
+ src = find_bubt_dev(src_dev_name);
+ if (!src) {
+ printf("Error: Unknown source \"%s\"\n", src_dev_name);
+ return 1;
+ }
+
+ if (!bubt_is_dev_active(src))
+ return -ENODEV;
+
+ printf("Burning U-Boot image \"%s\" from \"%s\" to \"%s\"\n",
+ net_boot_file_name, src->name, dst->name);
+
+ image_size = bubt_read_file(src);
+ if (!image_size)
+ return 1;
+
+ err = bubt_verify(dst);
+ if (err)
+ return 1;
+
+ err = bubt_write_file(dst, image_size);
+ if (err)
+ return 1;
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ bubt, 4, 0, do_bubt_cmd,
+ "Burn a u-boot image to flash",
+ "[file-name] [destination [source]]\n"
+ "\t-file-name The image file name to burn. Default = " CONFIG_MVEBU_UBOOT_DFLT_NAME "\n"
+ "\t-destination Flash to burn to [spi, nand, mmc, sata]. Default = " DEFAULT_BUBT_DST "\n"
+ "\t-source The source to load image from [tftp, usb, mmc, sata]. Default = " DEFAULT_BUBT_SRC "\n"
+ "Examples:\n"
+ "\tbubt - Burn flash-image.bin from tftp to active boot device\n"
+ "\tbubt flash-image-new.bin nand - Burn flash-image-new.bin from tftp to NAND flash\n"
+ "\tbubt backup-flash-image.bin mmc usb - Burn backup-flash-image.bin from usb to MMC\n"
+
+);
diff --git a/cmd/mvebu/comphy_rx_training.c b/cmd/mvebu/comphy_rx_training.c
new file mode 100644
index 00000000000..5653877cd4a
--- /dev/null
+++ b/cmd/mvebu/comphy_rx_training.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017 Marvell International Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <command.h>
+#include <console.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <dm/device-internal.h>
+#include <mvebu/comphy.h>
+
+int mvebu_comphy_rx_training_cmd(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ struct udevice *dev;
+ struct uclass *uc;
+ int ret, cp_index, comphy_index, i = 0;
+
+ if (argc != 3) {
+ printf("missing arguments\n");
+ return CMD_RET_USAGE;
+ }
+
+ cp_index = hextoul(argv[1], NULL);
+ comphy_index = hextoul(argv[2], NULL);
+
+ ret = uclass_get(UCLASS_MISC, &uc);
+ if (ret) {
+ printf("Couldn't find UCLASS_MISC\n");
+ return ret;
+ }
+
+ uclass_foreach_dev(dev, uc) {
+ if (!(memcmp(dev->name, "comphy", 5))) {
+ if (i == cp_index) {
+ comphy_rx_training(dev, comphy_index);
+ return 0;
+ }
+
+ i++;
+ }
+ }
+
+ printf("Coudn't find comphy %d\n", cp_index);
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ mvebu_comphy_rx_training, 3, 0, mvebu_comphy_rx_training_cmd,
+ "mvebu_comphy_rx_training <cp id> <comphy id>\n",
+ "\n\tRun COMPHY RX training sequence, the user must state CP index (0/1) and comphy ID (0/5)"
+);
diff --git a/cmd/nand.c b/cmd/nand.c
new file mode 100644
index 00000000000..b5678b0a008
--- /dev/null
+++ b/cmd/nand.c
@@ -0,0 +1,1253 @@
+/*
+ * Driver for NAND support, Rick Bronson
+ * borrowed heavily from:
+ * (c) 1999 Machine Vision Holdings, Inc.
+ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Ported 'dynenv' to 'nand env.oob' command
+ * (C) 2010 Nanometrics, Inc.
+ * 'dynenv' -- Dynamic environment offset in NAND OOB
+ * (C) Copyright 2006-2007 OpenMoko, Inc.
+ * Added 16-bit nand support
+ * (C) 2004 Texas Instruments
+ *
+ * Copyright 2010, 2012 Freescale Semiconductor
+ * The portions of this file whose copyright is held by Freescale and which
+ * are not considered a derived work of GPL v2-only code may be distributed
+ * and/or modified under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The function nand_biterror() in this file is inspired from
+ * mtd-utils/nand-utils/nandflipbits.c which was released under GPLv2
+ * only
+ */
+
+#include <bootstage.h>
+#include <image.h>
+#include <asm/cache.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <command.h>
+#include <console.h>
+#include <env.h>
+#include <watchdog.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <asm/byteorder.h>
+#include <jffs2/jffs2.h>
+#include <nand.h>
+#include <display_options.h>
+
+#include "legacy-mtd-utils.h"
+
+#if defined(CONFIG_CMD_MTDPARTS)
+
+/* partition handling routines */
+int mtdparts_init(void);
+int find_dev_and_part(const char *id, struct mtd_device **dev,
+ u8 *part_num, struct part_info **part);
+#endif
+
+#define MAX_NUM_PAGES 64
+
+static int nand_biterror(struct mtd_info *mtd, ulong off, int bit)
+{
+ int ret = 0;
+ int page = 0;
+ ulong block_off;
+ u_char *datbuf[MAX_NUM_PAGES]; /* Data and OOB */
+ u_char data;
+ int pages_per_blk = mtd->erasesize / mtd->writesize;
+ struct erase_info einfo;
+
+ if (pages_per_blk > MAX_NUM_PAGES) {
+ printf("Too many pages in one erase block\n");
+ return 1;
+ }
+
+ if (bit < 0 || bit > 7) {
+ printf("bit position 0 to 7 is allowed\n");
+ return 1;
+ }
+
+ /* Allocate memory */
+ memset(datbuf, 0, sizeof(datbuf));
+ for (page = 0; page < pages_per_blk ; page++) {
+ datbuf[page] = malloc(mtd->writesize + mtd->oobsize);
+ if (!datbuf[page]) {
+ printf("No memory for page buffer\n");
+ ret = -ENOMEM;
+ goto free_memory;
+ }
+ }
+
+ /* Align to erase block boundary */
+ block_off = off & (~(mtd->erasesize - 1));
+
+ /* Read out memory as first step */
+ for (page = 0; page < pages_per_blk ; page++) {
+ struct mtd_oob_ops ops;
+ loff_t addr = (loff_t)block_off;
+
+ memset(&ops, 0, sizeof(ops));
+ ops.datbuf = datbuf[page];
+ ops.oobbuf = datbuf[page] + mtd->writesize;
+ ops.len = mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ops.mode = MTD_OPS_RAW;
+ ret = mtd_read_oob(mtd, addr, &ops);
+ if (ret < 0) {
+ printf("Error (%d) reading page %08lx\n",
+ ret, block_off);
+ ret = 1;
+ goto free_memory;
+ }
+ block_off += mtd->writesize;
+ }
+
+ /* Erase the block */
+ memset(&einfo, 0, sizeof(einfo));
+ einfo.mtd = mtd;
+ /* Align to erase block boundary */
+ einfo.addr = (loff_t)(off & (~(mtd->erasesize - 1)));
+ einfo.len = mtd->erasesize;
+ ret = mtd_erase(mtd, &einfo);
+ if (ret < 0) {
+ printf("Error (%d) nand_erase_nand page %08llx\n",
+ ret, einfo.addr);
+ ret = 1;
+ goto free_memory;
+ }
+
+ /* Twist a bit in data part */
+ block_off = off & (mtd->erasesize - 1);
+ data = datbuf[block_off / mtd->writesize][block_off % mtd->writesize];
+ data ^= (1 << bit);
+ datbuf[block_off / mtd->writesize][block_off % mtd->writesize] = data;
+
+ printf("Flip data at 0x%lx with xor 0x%02x (bit=%d) to value=0x%02x\n",
+ off, (1 << bit), bit, data);
+
+ /* Write back twisted data and unmodified OOB */
+ /* Align to erase block boundary */
+ block_off = off & (~(mtd->erasesize - 1));
+ for (page = 0; page < pages_per_blk; page++) {
+ struct mtd_oob_ops ops;
+ loff_t addr = (loff_t)block_off;
+
+ memset(&ops, 0, sizeof(ops));
+ ops.datbuf = datbuf[page];
+ ops.oobbuf = datbuf[page] + mtd->writesize;
+ ops.len = mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ops.mode = MTD_OPS_RAW;
+ ret = mtd_write_oob(mtd, addr, &ops);
+ if (ret < 0) {
+ printf("Error (%d) write page %08lx\n", ret, block_off);
+ ret = 1;
+ goto free_memory;
+ }
+ block_off += mtd->writesize;
+ }
+
+free_memory:
+ for (page = 0; page < pages_per_blk ; page++) {
+ if (datbuf[page])
+ free(datbuf[page]);
+ }
+ return ret;
+}
+
+static int nand_dump(struct mtd_info *mtd, ulong off, int only_oob,
+ int ecc, int repeat)
+{
+ int i;
+ u_char *datbuf, *oobbuf, *p;
+ static loff_t last;
+ int ret = 0;
+
+ if (repeat)
+ off = last + mtd->writesize;
+
+ last = off;
+
+ datbuf = memalign(ARCH_DMA_MINALIGN, mtd->writesize);
+ if (!datbuf) {
+ puts("No memory for page buffer\n");
+ return 1;
+ }
+
+ oobbuf = memalign(ARCH_DMA_MINALIGN, mtd->oobsize);
+ if (!oobbuf) {
+ puts("No memory for page buffer\n");
+ ret = 1;
+ goto free_dat;
+ }
+ off &= ~(mtd->writesize - 1);
+ loff_t addr = (loff_t) off;
+ struct mtd_oob_ops ops;
+ memset(&ops, 0, sizeof(ops));
+ ops.datbuf = datbuf;
+ ops.oobbuf = oobbuf;
+ ops.len = mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ if (ecc)
+ ops.mode = MTD_OPS_PLACE_OOB;
+ else
+ ops.mode = MTD_OPS_RAW;
+ i = mtd_read_oob(mtd, addr, &ops);
+ if (i < 0) {
+ printf("Error reading page at offset %08lx, %d %s\n",
+ off, i, i == -EUCLEAN ? "correctable" :
+ "uncorrectable, dumping raw data");
+ ret = 1;
+ }
+ printf("\nPage at offset %08lx dump:\n", off);
+
+ if (!only_oob) {
+ i = mtd->writesize;
+ p = datbuf;
+ print_buffer(off, p, 1, i, 16);
+ }
+
+ puts("\nOOB:\n");
+ i = mtd->oobsize;
+ p = oobbuf;
+ print_buffer(0, p, 1, i, 8);
+
+ free(oobbuf);
+free_dat:
+ free(datbuf);
+
+ return ret;
+}
+
+#ifdef CONFIG_CMD_NAND_WATCH
+static int nand_watch_bf(struct mtd_info *mtd, ulong off, ulong size, bool quiet)
+{
+ unsigned int max_bf = 0, pages_wbf = 0;
+ unsigned int first_page, pages, i;
+ struct mtd_oob_ops ops = {};
+ u_char *buf;
+ int ret;
+
+ buf = memalign(ARCH_DMA_MINALIGN, mtd->writesize);
+ if (!buf) {
+ puts("No memory for page buffer\n");
+ return 1;
+ }
+
+ first_page = off / mtd->writesize;
+ pages = size / mtd->writesize;
+
+ ops.datbuf = buf;
+ ops.len = mtd->writesize;
+ for (i = first_page; i < first_page + pages; i++) {
+ ulong addr = mtd->writesize * i;
+ ret = mtd_read_oob_bf(mtd, addr, &ops);
+ if (ret < 0) {
+ if (quiet)
+ continue;
+
+ printf("Page %7d (0x%08lx) -> error %d\n",
+ i, addr, ret);
+ } else if (ret) {
+ max_bf = max(max_bf, (unsigned int)ret);
+ pages_wbf++;
+ if (quiet)
+ continue;
+ printf("Page %7d (0x%08lx) -> up to %2d bf/chunk\n",
+ i, addr, ret);
+ }
+ }
+
+ printf("Maximum number of bitflips: %u\n", max_bf);
+ printf("Pages with bitflips: %u/%u\n", pages_wbf, pages);
+
+ free(buf);
+
+ return 0;
+}
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+static int set_dev(int dev)
+{
+ struct mtd_info *mtd = get_nand_dev_by_index(dev);
+
+ if (!mtd)
+ return -ENODEV;
+
+ if (nand_curr_device == dev)
+ return 0;
+
+ printf("Device %d: %s", dev, mtd->name);
+ puts("... is now current device\n");
+ nand_curr_device = dev;
+
+#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
+ board_nand_select_device(mtd_to_nand(mtd), dev);
+#endif
+
+ return 0;
+}
+
+#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK
+static void print_status(ulong start, ulong end, ulong erasesize, int status)
+{
+ /*
+ * Micron NAND flash (e.g. MT29F4G08ABADAH4) BLOCK LOCK READ STATUS is
+ * not the same as others. Instead of bit 1 being lock, it is
+ * #lock_tight. To make the driver support either format, ignore bit 1
+ * and use only bit 0 and bit 2.
+ */
+ printf("%08lx - %08lx: %08lx blocks %s%s%s\n",
+ start,
+ end - 1,
+ (end - start) / erasesize,
+ ((status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""),
+ (!(status & NAND_LOCK_STATUS_UNLOCK) ? "LOCK " : ""),
+ ((status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : ""));
+}
+
+static void do_nand_status(struct mtd_info *mtd)
+{
+ ulong block_start = 0;
+ ulong off;
+ int last_status = -1;
+
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ /* check the WP bit */
+ nand_chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+ printf("device is %swrite protected\n",
+ (nand_chip->read_byte(mtd) & 0x80 ?
+ "NOT " : ""));
+
+ for (off = 0; off < mtd->size; off += mtd->erasesize) {
+ int s = nand_get_lock_status(mtd, off);
+
+ /* print message only if status has changed */
+ if (s != last_status && off != 0) {
+ print_status(block_start, off, mtd->erasesize,
+ last_status);
+ block_start = off;
+ }
+ last_status = s;
+ }
+ /* Print the last block info */
+ print_status(block_start, off, mtd->erasesize, last_status);
+}
+#endif
+
+#ifdef CONFIG_ENV_OFFSET_OOB
+unsigned long nand_env_oob_offset;
+
+int do_nand_env_oob(struct cmd_tbl *cmdtp, int argc, char *const argv[])
+{
+ int ret;
+ uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)];
+ struct mtd_info *mtd = get_nand_dev_by_index(0);
+ char *cmd = argv[1];
+
+ if (CONFIG_SYS_MAX_NAND_DEVICE == 0 || !mtd) {
+ puts("no devices available\n");
+ return 1;
+ }
+
+ set_dev(0);
+
+ if (!strcmp(cmd, "get")) {
+ ret = get_nand_env_oob(mtd, &nand_env_oob_offset);
+ if (ret)
+ return 1;
+
+ printf("0x%08lx\n", nand_env_oob_offset);
+ } else if (!strcmp(cmd, "set")) {
+ loff_t addr;
+ loff_t maxsize;
+ struct mtd_oob_ops ops;
+ int idx = 0;
+
+ if (argc < 3)
+ goto usage;
+
+ mtd = get_nand_dev_by_index(idx);
+ /* We don't care about size, or maxsize. */
+ if (mtd_arg_off(argv[2], &idx, &addr, &maxsize, &maxsize,
+ MTD_DEV_TYPE_NAND, mtd->size)) {
+ puts("Offset or partition name expected\n");
+ return 1;
+ }
+ if (set_dev(idx)) {
+ puts("Offset or partition name expected\n");
+ return 1;
+ }
+
+ if (idx != 0) {
+ puts("Partition not on first NAND device\n");
+ return 1;
+ }
+
+ if (mtd->oobavail < ENV_OFFSET_SIZE) {
+ printf("Insufficient available OOB bytes:\n"
+ "%d OOB bytes available but %d required for "
+ "env.oob support\n",
+ mtd->oobavail, ENV_OFFSET_SIZE);
+ return 1;
+ }
+
+ if ((addr & (mtd->erasesize - 1)) != 0) {
+ printf("Environment offset must be block-aligned\n");
+ return 1;
+ }
+
+ ops.datbuf = NULL;
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.ooboffs = 0;
+ ops.ooblen = ENV_OFFSET_SIZE;
+ ops.oobbuf = (void *) oob_buf;
+
+ oob_buf[0] = ENV_OOB_MARKER;
+ oob_buf[1] = addr / mtd->erasesize;
+
+ ret = mtd->write_oob(mtd, ENV_OFFSET_SIZE, &ops);
+ if (ret) {
+ printf("Error writing OOB block 0\n");
+ return ret;
+ }
+
+ ret = get_nand_env_oob(mtd, &nand_env_oob_offset);
+ if (ret) {
+ printf("Error reading env offset in OOB\n");
+ return ret;
+ }
+
+ if (addr != nand_env_oob_offset) {
+ printf("Verification of env offset in OOB failed: "
+ "0x%08llx expected but got 0x%08lx\n",
+ (unsigned long long)addr, nand_env_oob_offset);
+ return 1;
+ }
+ } else {
+ goto usage;
+ }
+
+ return ret;
+
+usage:
+ return CMD_RET_USAGE;
+}
+
+#endif
+
+static void nand_print_and_set_info(int idx)
+{
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+
+ mtd = get_nand_dev_by_index(idx);
+ if (!mtd)
+ return;
+
+ chip = mtd_to_nand(mtd);
+ printf("Device %d: ", idx);
+ if (chip->numchips > 1)
+ printf("%dx ", chip->numchips);
+ printf("%s, sector size %u KiB\n",
+ mtd->name, mtd->erasesize >> 10);
+ printf(" Page size %8d b\n", mtd->writesize);
+ printf(" OOB size %8d b\n", mtd->oobsize);
+ printf(" Erase size %8d b\n", mtd->erasesize);
+ printf(" ecc strength %8d bits\n", mtd->ecc_strength);
+ printf(" ecc step size %8d b\n", mtd->ecc_step_size);
+ printf(" subpagesize %8d b\n", chip->subpagesize);
+ printf(" options 0x%08x\n", chip->options);
+ printf(" bbt options 0x%08x\n", chip->bbt_options);
+
+ /* Set geometry info */
+ env_set_hex("nand_writesize", mtd->writesize);
+ env_set_hex("nand_oobsize", mtd->oobsize);
+ env_set_hex("nand_erasesize", mtd->erasesize);
+}
+
+static int raw_access(struct mtd_info *mtd, void *buf, loff_t off,
+ ulong count, int read, int no_verify)
+{
+ int ret = 0;
+
+ while (count--) {
+ /* Raw access */
+ mtd_oob_ops_t ops = {
+ .datbuf = buf,
+ .oobbuf = buf + mtd->writesize,
+ .len = mtd->writesize,
+ .ooblen = mtd->oobsize,
+ .mode = MTD_OPS_RAW
+ };
+
+ if (read) {
+ ret = mtd_read_oob(mtd, off, &ops);
+ } else {
+ ret = mtd_write_oob(mtd, off, &ops);
+ if (!ret && !no_verify)
+ ret = nand_verify_page_oob(mtd, &ops, off);
+ }
+
+ if (ret) {
+ printf("%s: error at offset %llx, ret %d\n",
+ __func__, (long long)off, ret);
+ break;
+ }
+
+ buf += mtd->writesize + mtd->oobsize;
+ off += mtd->writesize;
+ }
+
+ return ret;
+}
+
+/* Adjust a chip/partition size down for bad blocks so we don't
+ * read/write past the end of a chip/partition by accident.
+ */
+static void adjust_size_for_badblocks(loff_t *size, loff_t offset, int dev)
+{
+ /* We grab the nand info object here fresh because this is usually
+ * called after arg_off_size() which can change the value of dev.
+ */
+ struct mtd_info *mtd = get_nand_dev_by_index(dev);
+ loff_t maxoffset = offset + *size;
+ int badblocks = 0;
+
+ /* count badblocks in NAND from offset to offset + size */
+ for (; offset < maxoffset; offset += mtd->erasesize) {
+ if (nand_block_isbad(mtd, offset))
+ badblocks++;
+ }
+ /* adjust size if any bad blocks found */
+ if (badblocks) {
+ *size -= badblocks * mtd->erasesize;
+ printf("size adjusted to 0x%llx (%d bad blocks)\n",
+ (unsigned long long)*size, badblocks);
+ }
+}
+
+static int do_nand(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int i, ret = 0;
+ ulong addr;
+ loff_t off, size, maxsize;
+ char *cmd, *s;
+ struct mtd_info *mtd;
+#ifdef CONFIG_SYS_NAND_QUIET
+ int quiet = CONFIG_SYS_NAND_QUIET;
+#else
+ int quiet = 0;
+#endif
+ const char *quiet_str = env_get("quiet");
+ int dev = nand_curr_device;
+ int repeat = flag & CMD_FLAG_REPEAT;
+
+ /* at least two arguments please */
+ if (argc < 2)
+ goto usage;
+
+ if (quiet_str)
+ quiet = simple_strtoul(quiet_str, NULL, 0) != 0;
+
+ cmd = argv[1];
+
+ /* Only "dump" is repeatable. */
+ if (repeat && strcmp(cmd, "dump"))
+ return 0;
+
+ if (strcmp(cmd, "info") == 0) {
+
+ putc('\n');
+ for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
+ nand_print_and_set_info(i);
+ return 0;
+ }
+
+ if (strcmp(cmd, "device") == 0) {
+ if (argc < 3) {
+ putc('\n');
+ if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE)
+ puts("no devices available\n");
+ else
+ nand_print_and_set_info(dev);
+ return 0;
+ }
+
+ dev = (int)dectoul(argv[2], NULL);
+ set_dev(dev);
+
+ return 0;
+ }
+
+#ifdef CONFIG_ENV_OFFSET_OOB
+ /* this command operates only on the first nand device */
+ if (strcmp(cmd, "env.oob") == 0)
+ return do_nand_env_oob(cmdtp, argc - 1, argv + 1);
+#endif
+
+ /* The following commands operate on the current device, unless
+ * overridden by a partition specifier. Note that if somehow the
+ * current device is invalid, it will have to be changed to a valid
+ * one before these commands can run, even if a partition specifier
+ * for another device is to be used.
+ */
+ mtd = get_nand_dev_by_index(dev);
+ if (!mtd) {
+ puts("\nno devices available\n");
+ return 1;
+ }
+
+ if (strcmp(cmd, "bad") == 0) {
+ printf("\nDevice %d bad blocks:\n", dev);
+ for (off = 0; off < mtd->size; off += mtd->erasesize) {
+ ret = nand_block_isbad(mtd, off);
+ if (ret)
+ printf(" 0x%08llx%s\n", (unsigned long long)off,
+ ret == 2 ? "\t (bbt reserved)" : "");
+ }
+ return 0;
+ }
+
+ /*
+ * Syntax is:
+ * 0 1 2 3 4
+ * nand erase [clean] [off size]
+ */
+ if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) {
+ nand_erase_options_t opts;
+ /* "clean" at index 2 means request to write cleanmarker */
+ int clean = argc > 2 && !strcmp("clean", argv[2]);
+ int scrub_yes = argc > 2 && !strcmp("-y", argv[2]);
+ int o = (clean || scrub_yes) ? 3 : 2;
+ int scrub = !strncmp(cmd, "scrub", 5);
+ int spread = 0;
+ int args = 2;
+ const char *scrub_warn =
+ "Warning: "
+ "scrub option will erase all factory set bad blocks!\n"
+ " "
+ "There is no reliable way to recover them.\n"
+ " "
+ "Use this command only for testing purposes if you\n"
+ " "
+ "are sure of what you are doing!\n"
+ "\nReally scrub this NAND flash? <y/N>\n";
+
+ if (cmd[5] != 0) {
+ if (!strcmp(&cmd[5], ".spread")) {
+ spread = 1;
+ } else if (!strcmp(&cmd[5], ".part")) {
+ args = 1;
+ } else if (!strcmp(&cmd[5], ".chip")) {
+ args = 0;
+ } else {
+ goto usage;
+ }
+ }
+
+ /*
+ * Don't allow missing arguments to cause full chip/partition
+ * erases -- easy to do accidentally, e.g. with a misspelled
+ * variable name.
+ */
+ if (argc != o + args)
+ goto usage;
+
+ printf("\nNAND %s: ", cmd);
+ /* skip first two or three arguments, look for offset and size */
+ if (mtd_arg_off_size(argc - o, argv + o, &dev, &off, &size,
+ &maxsize, MTD_DEV_TYPE_NAND,
+ mtd->size) != 0)
+ return 1;
+
+ if (set_dev(dev))
+ return 1;
+
+ mtd = get_nand_dev_by_index(dev);
+
+ memset(&opts, 0, sizeof(opts));
+ opts.offset = off;
+ opts.length = size;
+ opts.jffs2 = clean;
+ opts.quiet = quiet;
+ opts.spread = spread;
+
+ if (scrub) {
+ if (scrub_yes) {
+ opts.scrub = 1;
+ } else {
+ puts(scrub_warn);
+ if (confirm_yesno()) {
+ opts.scrub = 1;
+ } else {
+ puts("scrub aborted\n");
+ return 1;
+ }
+ }
+ }
+ ret = nand_erase_opts(mtd, &opts);
+ printf("%s\n", ret ? "ERROR" : "OK");
+
+ return ret == 0 ? 0 : 1;
+ }
+
+ if (strncmp(cmd, "dump", 4) == 0) {
+ int only_oob, ecc;
+
+ if (argc < 3)
+ goto usage;
+
+ only_oob = !strcmp(&cmd[4], ".oob") || !strcmp(&cmd[4], ".ecc.oob") ||
+ !strcmp(&cmd[4], ".oob.ecc");
+
+ ecc = !strcmp(&cmd[4], ".ecc") || !strcmp(&cmd[4], ".ecc.oob") ||
+ !strcmp(&cmd[4], ".oob.ecc");
+
+ off = (int)hextoul(argv[2], NULL);
+ ret = nand_dump(mtd, off, only_oob, ecc, repeat);
+
+ return ret == 0 ? 1 : 0;
+ }
+
+ if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
+ size_t rwsize;
+ ulong pagecount = 1;
+ int read;
+ int raw = 0;
+ int no_verify = 0;
+ void *buf;
+
+ if (argc < 4)
+ goto usage;
+
+ addr = (ulong)hextoul(argv[2], NULL);
+
+ read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
+ printf("\nNAND %s: ", read ? "read" : "write");
+
+ s = strchr(cmd, '.');
+
+ if (s && !strncmp(s, ".raw", 4)) {
+ raw = 1;
+
+ if (!strcmp(s, ".raw.noverify"))
+ no_verify = 1;
+
+ if (mtd_arg_off(argv[3], &dev, &off, &size, &maxsize,
+ MTD_DEV_TYPE_NAND,
+ mtd->size))
+ return 1;
+
+ if (set_dev(dev))
+ return 1;
+
+ mtd = get_nand_dev_by_index(dev);
+
+ if (argc > 4 && !str2long(argv[4], &pagecount)) {
+ printf("'%s' is not a number\n", argv[4]);
+ return 1;
+ }
+
+ if (pagecount * mtd->writesize > size) {
+ puts("Size exceeds partition or device limit\n");
+ return -1;
+ }
+
+ rwsize = pagecount * (mtd->writesize + mtd->oobsize);
+ } else {
+ if (mtd_arg_off_size(argc - 3, argv + 3, &dev, &off,
+ &size, &maxsize,
+ MTD_DEV_TYPE_NAND,
+ mtd->size) != 0)
+ return 1;
+
+ if (set_dev(dev))
+ return 1;
+
+ /* size is unspecified */
+ if (argc < 5)
+ adjust_size_for_badblocks(&size, off, dev);
+ rwsize = size;
+ }
+
+ mtd = get_nand_dev_by_index(dev);
+ buf = map_sysmem(addr, maxsize);
+
+ if (!s || !strcmp(s, ".jffs2") ||
+ !strcmp(s, ".e") || !strcmp(s, ".i")) {
+ if (read)
+ ret = nand_read_skip_bad(mtd, off, &rwsize,
+ NULL, maxsize, buf);
+ else
+ ret = nand_write_skip_bad(mtd, off, &rwsize,
+ NULL, maxsize, buf,
+ WITH_WR_VERIFY);
+#ifdef CONFIG_CMD_NAND_TRIMFFS
+ } else if (!strcmp(s, ".trimffs")) {
+ if (read) {
+ printf("Unknown nand command suffix '%s'\n", s);
+ unmap_sysmem(buf);
+ return 1;
+ }
+ ret = nand_write_skip_bad(mtd, off, &rwsize, NULL,
+ maxsize, buf,
+ WITH_DROP_FFS | WITH_WR_VERIFY);
+#endif
+ } else if (!strcmp(s, ".oob")) {
+ /* out-of-band data */
+ mtd_oob_ops_t ops = {
+ .oobbuf = buf,
+ .ooblen = rwsize,
+ .mode = MTD_OPS_RAW
+ };
+
+ if (read)
+ ret = mtd_read_oob(mtd, off, &ops);
+ else
+ ret = mtd_write_oob(mtd, off, &ops);
+ } else if (raw) {
+ ret = raw_access(mtd, buf, off, pagecount, read,
+ no_verify);
+ } else {
+ printf("Unknown nand command suffix '%s'.\n", s);
+ unmap_sysmem(buf);
+ return 1;
+ }
+
+ unmap_sysmem(buf);
+ printf(" %zu bytes %s: %s\n", rwsize,
+ read ? "read" : "written", ret ? "ERROR" : "OK");
+
+ return ret == 0 ? 0 : 1;
+ }
+
+#ifdef CONFIG_CMD_NAND_WATCH
+ if (strncmp(cmd, "watch", 5) == 0) {
+ int args = 2;
+
+ if (cmd[5]) {
+ if (!strncmp(&cmd[5], ".part", 5)) {
+ args = 1;
+ } else if (!strncmp(&cmd[5], ".chip", 5)) {
+ args = 0;
+ } else {
+ goto usage;
+ }
+ }
+
+ if (cmd[10])
+ if (!strncmp(&cmd[10], ".quiet", 6))
+ quiet = true;
+
+ if (argc != 2 + args)
+ goto usage;
+
+ ret = mtd_arg_off_size(argc - 2, argv + 2, &dev, &off, &size,
+ &maxsize, MTD_DEV_TYPE_NAND, mtd->size);
+ if (ret)
+ return ret;
+
+ /* size is unspecified */
+ if (argc < 4)
+ adjust_size_for_badblocks(&size, off, dev);
+
+ if ((off & (mtd->writesize - 1)) ||
+ (size & (mtd->writesize - 1))) {
+ printf("Attempt to read non page-aligned data\n");
+ return -EINVAL;
+ }
+
+ ret = set_dev(dev);
+ if (ret)
+ return ret;
+
+ mtd = get_nand_dev_by_index(dev);
+
+ printf("\nNAND watch for bitflips in area 0x%llx-0x%llx:\n",
+ off, off + size);
+
+ return nand_watch_bf(mtd, off, size, quiet);
+ }
+#endif
+
+#ifdef CONFIG_CMD_NAND_TORTURE
+ if (strcmp(cmd, "torture") == 0) {
+ loff_t endoff;
+ unsigned int failed = 0, passed = 0;
+
+ if (argc < 3)
+ goto usage;
+
+ if (!str2off(argv[2], &off)) {
+ puts("Offset is not a valid number\n");
+ return 1;
+ }
+
+ size = mtd->erasesize;
+ if (argc > 3) {
+ if (!str2off(argv[3], &size)) {
+ puts("Size is not a valid number\n");
+ return 1;
+ }
+ }
+
+ endoff = off + size;
+ if (endoff > mtd->size) {
+ puts("Arguments beyond end of NAND\n");
+ return 1;
+ }
+
+ off = round_down(off, mtd->erasesize);
+ endoff = round_up(endoff, mtd->erasesize);
+ size = endoff - off;
+ printf("\nNAND torture: device %d offset 0x%llx size 0x%llx (block size 0x%x)\n",
+ dev, off, size, mtd->erasesize);
+ while (off < endoff) {
+ ret = nand_torture(mtd, off);
+ if (ret) {
+ failed++;
+ printf(" block at 0x%llx failed\n", off);
+ } else {
+ passed++;
+ }
+ off += mtd->erasesize;
+ }
+ printf(" Passed: %u, failed: %u\n", passed, failed);
+ return failed != 0;
+ }
+#endif
+
+ if (strcmp(cmd, "markbad") == 0) {
+ argc -= 2;
+ argv += 2;
+
+ if (argc <= 0)
+ goto usage;
+
+ while (argc > 0) {
+ addr = hextoul(*argv, NULL);
+
+ if (mtd_block_markbad(mtd, addr)) {
+ printf("block 0x%08lx NOT marked "
+ "as bad! ERROR %d\n",
+ addr, ret);
+ ret = 1;
+ } else {
+ printf("block 0x%08lx successfully "
+ "marked as bad\n",
+ addr);
+ }
+ --argc;
+ ++argv;
+ }
+ return ret;
+ }
+
+ if (strcmp(cmd, "biterr") == 0) {
+ int bit;
+
+ if (argc != 4)
+ goto usage;
+
+ off = (int)simple_strtoul(argv[2], NULL, 16);
+ bit = (int)simple_strtoul(argv[3], NULL, 10);
+ ret = nand_biterror(mtd, off, bit);
+ return ret;
+ }
+
+#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK
+ if (strcmp(cmd, "lock") == 0) {
+ int tight = 0;
+ int status = 0;
+ if (argc == 3) {
+ if (!strcmp("tight", argv[2]))
+ tight = 1;
+ if (!strcmp("status", argv[2]))
+ status = 1;
+ }
+ if (status) {
+ do_nand_status(mtd);
+ } else {
+ if (!nand_lock(mtd, tight)) {
+ puts("NAND flash successfully locked\n");
+ } else {
+ puts("Error locking NAND flash\n");
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ if (strncmp(cmd, "unlock", 5) == 0) {
+ int allexcept = 0;
+
+ s = strchr(cmd, '.');
+
+ if (s && !strcmp(s, ".allexcept"))
+ allexcept = 1;
+
+ if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &off, &size,
+ &maxsize, MTD_DEV_TYPE_NAND,
+ mtd->size) < 0)
+ return 1;
+
+ if (set_dev(dev))
+ return 1;
+
+ mtd = get_nand_dev_by_index(dev);
+
+ if (!nand_unlock(mtd, off, size, allexcept)) {
+ puts("NAND flash successfully unlocked\n");
+ } else {
+ puts("Error unlocking NAND flash, "
+ "write and erase will probably fail\n");
+ return 1;
+ }
+ return 0;
+ }
+#endif
+
+usage:
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_LONGHELP(nand,
+ "info - show available NAND devices\n"
+ "nand device [dev] - show or set current device\n"
+ "nand read - addr off|partition size\n"
+ "nand write - addr off|partition size\n"
+ " read/write 'size' bytes starting at offset 'off'\n"
+ " to/from memory address 'addr', skipping bad blocks.\n"
+ "nand read.raw - addr off|partition [pages]\n"
+ "nand write.raw[.noverify] - addr off|partition [pages]\n"
+ " Use read.raw/write.raw to avoid ECC and access the flash as-is.\n"
+#ifdef CONFIG_CMD_NAND_TRIMFFS
+ "nand write.trimffs - addr off|partition size\n"
+ " write 'size' bytes starting at offset 'off' from memory address\n"
+ " 'addr', skipping bad blocks and dropping any pages at the end\n"
+ " of eraseblocks that contain only 0xFF\n"
+#endif
+ "nand erase[.spread] [clean] off size - erase 'size' bytes "
+ "from offset 'off'\n"
+ " With '.spread', erase enough for given file size, otherwise,\n"
+ " 'size' includes skipped bad blocks.\n"
+ "nand erase.part [clean] partition - erase entire mtd partition'\n"
+ "nand erase.chip [clean] - erase entire chip'\n"
+ "nand bad - show bad blocks\n"
+ "nand dump[.oob][.ecc] off - dump raw (default) or ecc corrected page at offset\n"
+#ifdef CONFIG_CMD_NAND_WATCH
+ "nand watch <off> <size> - check an area for bitflips\n"
+ "nand watch.part <part> - check a partition for bitflips\n"
+ "nand watch.chip - check the whole device for bitflips\n"
+ "\t\t.quiet - Query only the summary, not the details\n"
+#endif
+#ifdef CONFIG_CMD_NAND_TORTURE
+ "nand torture off - torture one block at offset\n"
+ "nand torture off [size] - torture blocks from off to off+size\n"
+#endif
+ "nand scrub [-y] off size | scrub.part partition | scrub.chip\n"
+ " really clean NAND erasing bad blocks (UNSAFE)\n"
+ "nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n"
+ "nand biterr off bit - make a bit error at offset and bit position (UNSAFE)"
+#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK
+ "\n"
+ "nand lock [tight] [status]\n"
+ " bring nand to lock state or display locked pages\n"
+ "nand unlock[.allexcept] [offset] [size] - unlock section"
+#endif
+#ifdef CONFIG_ENV_OFFSET_OOB
+ "\n"
+ "nand env.oob - environment offset in OOB of block 0 of"
+ " first device.\n"
+ "nand env.oob set off|partition - set enviromnent offset\n"
+ "nand env.oob get - get environment offset"
+#endif
+ );
+
+U_BOOT_CMD(
+ nand, CONFIG_SYS_MAXARGS, 1, do_nand,
+ "NAND sub-system", nand_help_text
+);
+
+static int nand_load_image(struct cmd_tbl *cmdtp, struct mtd_info *mtd,
+ ulong offset, ulong addr, char *cmd)
+{
+ int r;
+ char *s;
+ size_t cnt;
+#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
+ struct legacy_img_hdr *hdr;
+#endif
+#if defined(CONFIG_FIT)
+ const void *fit_hdr = NULL;
+#endif
+
+ s = strchr(cmd, '.');
+ if (s != NULL &&
+ (strcmp(s, ".jffs2") && strcmp(s, ".e") && strcmp(s, ".i"))) {
+ printf("Unknown nand load suffix '%s'\n", s);
+ bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX);
+ return 1;
+ }
+
+ printf("\nLoading from %s, offset 0x%lx\n", mtd->name, offset);
+
+ cnt = mtd->writesize;
+ r = nand_read_skip_bad(mtd, offset, &cnt, NULL, mtd->size,
+ (u_char *)addr);
+ if (r) {
+ puts("** Read error\n");
+ bootstage_error(BOOTSTAGE_ID_NAND_HDR_READ);
+ return 1;
+ }
+ bootstage_mark(BOOTSTAGE_ID_NAND_HDR_READ);
+
+ switch (genimg_get_format ((void *)addr)) {
+#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
+ case IMAGE_FORMAT_LEGACY:
+ hdr = (struct legacy_img_hdr *)addr;
+
+ bootstage_mark(BOOTSTAGE_ID_NAND_TYPE);
+ image_print_contents (hdr);
+
+ cnt = image_get_image_size (hdr);
+ break;
+#endif
+#if defined(CONFIG_FIT)
+ case IMAGE_FORMAT_FIT:
+ fit_hdr = (const void *)addr;
+ puts ("Fit image detected...\n");
+
+ cnt = fit_get_size (fit_hdr);
+ break;
+#endif
+ default:
+ bootstage_error(BOOTSTAGE_ID_NAND_TYPE);
+ puts ("** Unknown image type\n");
+ return 1;
+ }
+ bootstage_mark(BOOTSTAGE_ID_NAND_TYPE);
+
+ r = nand_read_skip_bad(mtd, offset, &cnt, NULL, mtd->size,
+ (u_char *)addr);
+ if (r) {
+ puts("** Read error\n");
+ bootstage_error(BOOTSTAGE_ID_NAND_READ);
+ return 1;
+ }
+ bootstage_mark(BOOTSTAGE_ID_NAND_READ);
+
+#if defined(CONFIG_FIT)
+ /* This cannot be done earlier, we need complete FIT image in RAM first */
+ if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) {
+ if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) {
+ bootstage_error(BOOTSTAGE_ID_NAND_FIT_READ);
+ puts ("** Bad FIT image format\n");
+ return 1;
+ }
+ bootstage_mark(BOOTSTAGE_ID_NAND_FIT_READ_OK);
+ fit_print_contents (fit_hdr);
+ }
+#endif
+
+ /* Loading ok, update default load address */
+
+ image_load_addr = addr;
+
+ return bootm_maybe_autostart(cmdtp, cmd);
+}
+
+static int do_nandboot(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *boot_device = NULL;
+ int idx;
+ ulong addr, offset = 0;
+ struct mtd_info *mtd;
+#if defined(CONFIG_CMD_MTDPARTS)
+ struct mtd_device *dev;
+ struct part_info *part;
+ u8 pnum;
+
+ if (argc >= 2) {
+ char *p = (argc == 2) ? argv[1] : argv[2];
+ if (!(str2long(p, &addr)) && (mtdparts_init() == 0) &&
+ (find_dev_and_part(p, &dev, &pnum, &part) == 0)) {
+ if (dev->id->type != MTD_DEV_TYPE_NAND) {
+ puts("Not a NAND device\n");
+ return 1;
+ }
+ if (argc > 3)
+ goto usage;
+ if (argc == 3)
+ addr = hextoul(argv[1], NULL);
+ else
+ addr = CONFIG_SYS_LOAD_ADDR;
+
+ mtd = get_nand_dev_by_index(dev->id->num);
+ return nand_load_image(cmdtp, mtd, part->offset,
+ addr, argv[0]);
+ }
+ }
+#endif
+
+ bootstage_mark(BOOTSTAGE_ID_NAND_PART);
+ switch (argc) {
+ case 1:
+ addr = CONFIG_SYS_LOAD_ADDR;
+ boot_device = env_get("bootdevice");
+ break;
+ case 2:
+ addr = hextoul(argv[1], NULL);
+ boot_device = env_get("bootdevice");
+ break;
+ case 3:
+ addr = hextoul(argv[1], NULL);
+ boot_device = argv[2];
+ break;
+ case 4:
+ addr = hextoul(argv[1], NULL);
+ boot_device = argv[2];
+ offset = hextoul(argv[3], NULL);
+ break;
+ default:
+#if defined(CONFIG_CMD_MTDPARTS)
+usage:
+#endif
+ bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX);
+ return CMD_RET_USAGE;
+ }
+ bootstage_mark(BOOTSTAGE_ID_NAND_SUFFIX);
+
+ if (!boot_device) {
+ puts("\n** No boot device **\n");
+ bootstage_error(BOOTSTAGE_ID_NAND_BOOT_DEVICE);
+ return 1;
+ }
+ bootstage_mark(BOOTSTAGE_ID_NAND_BOOT_DEVICE);
+
+ idx = hextoul(boot_device, NULL);
+
+ mtd = get_nand_dev_by_index(idx);
+ if (!mtd) {
+ printf("\n** Device %d not available\n", idx);
+ bootstage_error(BOOTSTAGE_ID_NAND_AVAILABLE);
+ return 1;
+ }
+ bootstage_mark(BOOTSTAGE_ID_NAND_AVAILABLE);
+
+ return nand_load_image(cmdtp, mtd, offset, addr, argv[0]);
+}
+
+U_BOOT_CMD(nboot, 4, 1, do_nandboot,
+ "boot from NAND device",
+ "[partition] | [[[loadAddr] dev] offset]"
+);
diff --git a/cmd/net-common.c b/cmd/net-common.c
new file mode 100644
index 00000000000..1c6f11cd435
--- /dev/null
+++ b/cmd/net-common.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <dm/device.h>
+#include <dm/uclass.h>
+#include <net.h>
+#include <linux/compat.h>
+#include <linux/ethtool.h>
+
+static int do_net_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ const struct udevice *current = eth_get_dev();
+ unsigned char env_enetaddr[ARP_HLEN];
+ const struct udevice *dev;
+ struct uclass *uc;
+
+ uclass_id_foreach_dev(UCLASS_ETH, dev, uc) {
+ eth_env_get_enetaddr_by_index("eth", dev_seq(dev), env_enetaddr);
+ printf("eth%d : %s %pM %s\n", dev_seq(dev), dev->name, env_enetaddr,
+ current == dev ? "active" : "");
+ }
+ return CMD_RET_SUCCESS;
+}
+
+static int do_net_stats(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int nstats, err, i, off;
+ struct udevice *dev;
+ u64 *values;
+ u8 *strings;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ err = uclass_get_device_by_name(UCLASS_ETH, argv[1], &dev);
+ if (err) {
+ printf("Could not find device %s\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ if (!eth_get_ops(dev)->get_sset_count ||
+ !eth_get_ops(dev)->get_strings ||
+ !eth_get_ops(dev)->get_stats) {
+ printf("Driver does not implement stats dump!\n");
+ return CMD_RET_FAILURE;
+ }
+
+ nstats = eth_get_ops(dev)->get_sset_count(dev);
+ strings = kcalloc(nstats, ETH_GSTRING_LEN, GFP_KERNEL);
+ if (!strings)
+ return CMD_RET_FAILURE;
+
+ values = kcalloc(nstats, sizeof(u64), GFP_KERNEL);
+ if (!values)
+ goto err_free_strings;
+
+ eth_get_ops(dev)->get_strings(dev, strings);
+ eth_get_ops(dev)->get_stats(dev, values);
+
+ off = 0;
+ for (i = 0; i < nstats; i++) {
+ printf(" %s: %llu\n", &strings[off], values[i]);
+ off += ETH_GSTRING_LEN;
+ };
+
+ kfree(strings);
+ kfree(values);
+
+ return CMD_RET_SUCCESS;
+
+err_free_strings:
+ kfree(strings);
+
+ return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl cmd_net[] = {
+ U_BOOT_CMD_MKENT(list, 1, 0, do_net_list, "", ""),
+ U_BOOT_CMD_MKENT(stats, 2, 0, do_net_stats, "", ""),
+};
+
+static int do_net(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ cp = find_cmd_tbl(argv[1], cmd_net, ARRAY_SIZE(cmd_net));
+
+ /* Drop the net command */
+ argc--;
+ argv++;
+
+ if (!cp || argc > cp->maxargs)
+ return CMD_RET_USAGE;
+ if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp))
+ return CMD_RET_SUCCESS;
+
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(net, 3, 1, do_net, "NET sub-system",
+ "list - list available devices\n"
+ "stats <device> - dump statistics for specified device\n");
diff --git a/cmd/net.c b/cmd/net.c
new file mode 100644
index 00000000000..886735ea14f
--- /dev/null
+++ b/cmd/net.c
@@ -0,0 +1,677 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#define LOG_CATEGORY UCLASS_ETH
+
+/*
+ * Boot support
+ */
+#include <bootstage.h>
+#include <command.h>
+#include <dm.h>
+#include <dm/devres.h>
+#include <env.h>
+#include <image.h>
+#include <log.h>
+#include <net.h>
+#include <net6.h>
+#include <net/udp.h>
+#include <net/sntp.h>
+#include <net/ncsi.h>
+
+static int netboot_common(enum proto_t, struct cmd_tbl *, int, char * const []);
+
+#ifdef CONFIG_CMD_BOOTP
+static int do_bootp(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return netboot_common(BOOTP, cmdtp, argc, argv);
+}
+
+U_BOOT_CMD(
+ bootp, 3, 1, do_bootp,
+ "boot image via network using BOOTP/TFTP protocol",
+ "[loadAddress] [[hostIPaddr:]bootfilename]"
+);
+#endif
+
+#ifdef CONFIG_CMD_TFTPBOOT
+int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int ret;
+
+ bootstage_mark_name(BOOTSTAGE_KERNELREAD_START, "tftp_start");
+ ret = netboot_common(TFTPGET, cmdtp, argc, argv);
+ bootstage_mark_name(BOOTSTAGE_KERNELREAD_STOP, "tftp_done");
+ return ret;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+U_BOOT_CMD(
+ tftpboot, 4, 1, do_tftpb,
+ "boot image via network using TFTP protocol\n"
+ "To use IPv6 add -ipv6 parameter or use IPv6 hostIPaddr framed "
+ "with [] brackets",
+ "[loadAddress] [[hostIPaddr:]bootfilename] [" USE_IP6_CMD_PARAM "]"
+);
+#else
+U_BOOT_CMD(
+ tftpboot, 3, 1, do_tftpb,
+ "load file via network using TFTP protocol",
+ "[loadAddress] [[hostIPaddr:]bootfilename]"
+);
+#endif
+#endif
+
+#ifdef CONFIG_CMD_TFTPPUT
+static int do_tftpput(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return netboot_common(TFTPPUT, cmdtp, argc, argv);
+}
+
+U_BOOT_CMD(
+ tftpput, 4, 1, do_tftpput,
+ "TFTP put command, for uploading files to a server",
+ "Address Size [[hostIPaddr:]filename]"
+);
+#endif
+
+#ifdef CONFIG_CMD_TFTPSRV
+static int do_tftpsrv(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return netboot_common(TFTPSRV, cmdtp, argc, argv);
+}
+
+U_BOOT_CMD(
+ tftpsrv, 2, 1, do_tftpsrv,
+ "act as a TFTP server and boot the first received file",
+ "[loadAddress]\n"
+ "Listen for an incoming TFTP transfer, receive a file and boot it.\n"
+ "The transfer is aborted if a transfer has not been started after\n"
+ "about 50 seconds or if Ctrl-C is pressed."
+);
+#endif
+
+#ifdef CONFIG_CMD_RARP
+int do_rarpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ return netboot_common(RARP, cmdtp, argc, argv);
+}
+
+U_BOOT_CMD(
+ rarpboot, 3, 1, do_rarpb,
+ "boot image via network using RARP/TFTP protocol",
+ "[loadAddress] [[hostIPaddr:]bootfilename]"
+);
+#endif
+
+#if defined(CONFIG_CMD_DHCP6)
+static int do_dhcp6(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int i;
+ int dhcp_argc;
+ char *dhcp_argv[] = {NULL, NULL, NULL, NULL};
+
+ /* Add -ipv6 flag for autoload */
+ for (i = 0; i < argc; i++)
+ dhcp_argv[i] = argv[i];
+ dhcp_argc = argc + 1;
+ dhcp_argv[dhcp_argc - 1] = USE_IP6_CMD_PARAM;
+
+ return netboot_common(DHCP6, cmdtp, dhcp_argc, dhcp_argv);
+}
+
+U_BOOT_CMD(dhcp6, 3, 1, do_dhcp6,
+ "boot image via network using DHCPv6/TFTP protocol.\n"
+ "Use IPv6 hostIPaddr framed with [] brackets",
+ "[loadAddress] [[hostIPaddr:]bootfilename]");
+#endif
+
+#if defined(CONFIG_CMD_DHCP)
+static int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return netboot_common(DHCP, cmdtp, argc, argv);
+}
+
+U_BOOT_CMD(
+ dhcp, 3, 1, do_dhcp,
+ "boot image via network using DHCP/TFTP protocol",
+ "[loadAddress] [[hostIPaddr:]bootfilename]"
+);
+
+int dhcp_run(ulong addr, const char *fname, bool autoload)
+{
+ char *dhcp_argv[] = {"dhcp", NULL, (char *)fname, NULL};
+ struct cmd_tbl cmdtp = {}; /* dummy */
+ char file_addr[17];
+ int old_autoload;
+ int ret, result;
+
+ log_debug("addr=%lx, fname=%s, autoload=%d\n", addr, fname, autoload);
+ old_autoload = env_get_yesno("autoload");
+ ret = env_set("autoload", autoload ? "y" : "n");
+ if (ret)
+ return log_msg_ret("en1", -EINVAL);
+
+ if (autoload) {
+ sprintf(file_addr, "%lx", addr);
+ dhcp_argv[1] = file_addr;
+ }
+
+ result = do_dhcp(&cmdtp, 0, !autoload ? 1 : fname ? 3 : 2, dhcp_argv);
+
+ ret = env_set("autoload", old_autoload == -1 ? NULL :
+ old_autoload ? "y" : "n");
+ if (ret)
+ return log_msg_ret("en2", -EINVAL);
+
+ if (result)
+ return log_msg_ret("res", -ENOENT);
+
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_CMD_NFS)
+static int do_nfs(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return netboot_common(NFS, cmdtp, argc, argv);
+}
+
+U_BOOT_CMD(
+ nfs, 3, 1, do_nfs,
+ "boot image via network using NFS protocol",
+ "[loadAddress] [[hostIPaddr:]bootfilename]"
+);
+#endif
+
+#if defined(CONFIG_CMD_WGET)
+static int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ wget_info = &default_wget_info;
+
+ return netboot_common(WGET, cmdtp, argc, argv);
+}
+
+U_BOOT_CMD(
+ wget, 3, 1, do_wget,
+ "boot image via network using HTTP protocol",
+ "[loadAddress] [[hostIPaddr:]path and image name]"
+);
+#endif
+
+static void netboot_update_env(void)
+{
+ char tmp[46];
+
+ if (net_gateway.s_addr) {
+ ip_to_string(net_gateway, tmp);
+ env_set("gatewayip", tmp);
+ }
+
+ if (net_netmask.s_addr) {
+ ip_to_string(net_netmask, tmp);
+ env_set("netmask", tmp);
+ }
+
+#ifdef CONFIG_CMD_BOOTP
+ if (net_hostname[0])
+ env_set("hostname", net_hostname);
+#endif
+
+#ifdef CONFIG_CMD_BOOTP
+ if (net_root_path[0])
+ env_set("rootpath", net_root_path);
+#endif
+
+ if (net_ip.s_addr) {
+ ip_to_string(net_ip, tmp);
+ env_set("ipaddr", tmp);
+ }
+ /*
+ * Only attempt to change serverip if net/bootp.c:store_net_params()
+ * could have set it
+ */
+ if (!IS_ENABLED(CONFIG_BOOTP_SERVERIP) && net_server_ip.s_addr) {
+ ip_to_string(net_server_ip, tmp);
+ env_set("serverip", tmp);
+ }
+ if (net_dns_server.s_addr) {
+ ip_to_string(net_dns_server, tmp);
+ env_set("dnsip", tmp);
+ }
+#if defined(CONFIG_BOOTP_DNS2)
+ if (net_dns_server2.s_addr) {
+ ip_to_string(net_dns_server2, tmp);
+ env_set("dnsip2", tmp);
+ }
+#endif
+#ifdef CONFIG_CMD_BOOTP
+ if (net_nis_domain[0])
+ env_set("domain", net_nis_domain);
+#endif
+
+#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
+ if (net_ntp_time_offset) {
+ sprintf(tmp, "%d", net_ntp_time_offset);
+ env_set("timeoffset", tmp);
+ }
+#endif
+#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
+ if (net_ntp_server.s_addr) {
+ ip_to_string(net_ntp_server, tmp);
+ env_set("ntpserverip", tmp);
+ }
+#endif
+
+ if (IS_ENABLED(CONFIG_IPV6)) {
+ if (!ip6_is_unspecified_addr(&net_ip6) ||
+ net_prefix_length != 0) {
+ if (net_prefix_length != 0)
+ snprintf(tmp, sizeof(tmp), "%pI6c/%d", &net_ip6, net_prefix_length);
+ else
+ snprintf(tmp, sizeof(tmp), "%pI6c", &net_ip6);
+ env_set("ip6addr", tmp);
+ }
+
+ if (!ip6_is_unspecified_addr(&net_server_ip6)) {
+ snprintf(tmp, sizeof(tmp), "%pI6c", &net_server_ip6);
+ env_set("serverip6", tmp);
+ }
+
+ if (!ip6_is_unspecified_addr(&net_gateway6)) {
+ snprintf(tmp, sizeof(tmp), "%pI6c", &net_gateway6);
+ env_set("gatewayip6", tmp);
+ }
+ }
+}
+
+/**
+ * parse_addr_size() - parse address and size arguments for tftpput
+ *
+ * @argv: command line arguments
+ * Return: 0 on success
+ */
+static int parse_addr_size(char * const argv[])
+{
+ if (strict_strtoul(argv[1], 16, &image_save_addr) < 0 ||
+ strict_strtoul(argv[2], 16, &image_save_size) < 0) {
+ printf("Invalid address/size\n");
+ return CMD_RET_USAGE;
+ }
+ return 0;
+}
+
+/**
+ * parse_args() - parse command line arguments
+ *
+ * @proto: command prototype
+ * @argc: number of arguments
+ * @argv: command line arguments
+ * Return: 0 on success
+ */
+static int parse_args(enum proto_t proto, int argc, char *const argv[])
+{
+ ulong addr;
+ char *end;
+
+ switch (argc) {
+ case 1:
+ if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT)
+ return 1;
+
+ /* refresh bootfile name from env */
+ copy_filename(net_boot_file_name, env_get("bootfile"),
+ sizeof(net_boot_file_name));
+ break;
+
+ case 2:
+ if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT)
+ return 1;
+ /*
+ * Only one arg - accept two forms:
+ * Just load address, or just boot file name. The latter
+ * form must be written in a format which can not be
+ * mis-interpreted as a valid number.
+ */
+ addr = hextoul(argv[1], &end);
+ if (end == (argv[1] + strlen(argv[1]))) {
+ image_load_addr = addr;
+ /* refresh bootfile name from env */
+ copy_filename(net_boot_file_name, env_get("bootfile"),
+ sizeof(net_boot_file_name));
+ } else {
+ net_boot_file_name_explicit = true;
+ copy_filename(net_boot_file_name, argv[1],
+ sizeof(net_boot_file_name));
+ }
+ break;
+
+ case 3:
+ if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) {
+ if (parse_addr_size(argv))
+ return 1;
+ } else {
+ image_load_addr = hextoul(argv[1], NULL);
+ net_boot_file_name_explicit = true;
+ copy_filename(net_boot_file_name, argv[2],
+ sizeof(net_boot_file_name));
+ }
+ break;
+
+#ifdef CONFIG_CMD_TFTPPUT
+ case 4:
+ if (parse_addr_size(argv))
+ return 1;
+ net_boot_file_name_explicit = true;
+ copy_filename(net_boot_file_name, argv[3],
+ sizeof(net_boot_file_name));
+ break;
+#endif
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int netboot_common(enum proto_t proto, struct cmd_tbl *cmdtp, int argc,
+ char *const argv[])
+{
+ char *s;
+ int rcode = 0;
+ int size;
+
+ net_boot_file_name_explicit = false;
+ *net_boot_file_name = '\0';
+
+ /* pre-set image_load_addr */
+ s = env_get("loadaddr");
+ if (s != NULL)
+ image_load_addr = hextoul(s, NULL);
+
+ if (IS_ENABLED(CONFIG_IPV6)) {
+ use_ip6 = false;
+
+ /* IPv6 parameter has to be always *last* */
+ if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM)) {
+ use_ip6 = true;
+ /* It is a hack not to break switch/case code */
+ --argc;
+ }
+ }
+
+ if (parse_args(proto, argc, argv)) {
+ bootstage_error(BOOTSTAGE_ID_NET_START);
+ return CMD_RET_USAGE;
+ }
+
+ bootstage_mark(BOOTSTAGE_ID_NET_START);
+
+ if (IS_ENABLED(CONFIG_IPV6) && !use_ip6) {
+ char *s, *e;
+ size_t len;
+
+ s = strchr(net_boot_file_name, '[');
+ e = strchr(net_boot_file_name, ']');
+ if (s && e) {
+ len = e - s;
+ if (!string_to_ip6(s + 1, len - 1, &net_server_ip6))
+ use_ip6 = true;
+ }
+ }
+
+ size = net_loop(proto);
+ if (size < 0) {
+ bootstage_error(BOOTSTAGE_ID_NET_NETLOOP_OK);
+ return CMD_RET_FAILURE;
+ }
+ bootstage_mark(BOOTSTAGE_ID_NET_NETLOOP_OK);
+
+ /* net_loop ok, update environment */
+ netboot_update_env();
+
+ /* done if no file was loaded (no errors though) */
+ if (size == 0) {
+ bootstage_error(BOOTSTAGE_ID_NET_LOADED);
+ return CMD_RET_SUCCESS;
+ }
+
+ bootstage_mark(BOOTSTAGE_ID_NET_LOADED);
+
+ rcode = bootm_maybe_autostart(cmdtp, argv[0]);
+
+ if (rcode == CMD_RET_SUCCESS)
+ bootstage_mark(BOOTSTAGE_ID_NET_DONE);
+ else
+ bootstage_error(BOOTSTAGE_ID_NET_DONE_ERR);
+ return rcode;
+}
+
+#if defined(CONFIG_CMD_PING)
+int do_ping(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ net_ping_ip = string_to_ip(argv[1]);
+ if (net_ping_ip.s_addr == 0)
+ return CMD_RET_USAGE;
+
+ if (net_loop(PING) < 0) {
+ printf("ping failed; host %s is not alive\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("host %s is alive\n", argv[1]);
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+ ping, 2, 1, do_ping,
+ "send ICMP ECHO_REQUEST to network host",
+ "pingAddress"
+);
+#endif
+
+#if IS_ENABLED(CONFIG_CMD_PING6)
+int do_ping6(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ if (string_to_ip6(argv[1], strlen(argv[1]), &net_ping_ip6))
+ return CMD_RET_USAGE;
+
+ use_ip6 = true;
+ if (net_loop(PING6) < 0) {
+ use_ip6 = false;
+ printf("ping6 failed; host %pI6c is not alive\n",
+ &net_ping_ip6);
+ return 1;
+ }
+
+ use_ip6 = false;
+ printf("host %pI6c is alive\n", &net_ping_ip6);
+ return 0;
+}
+
+U_BOOT_CMD(
+ ping6, 2, 1, do_ping6,
+ "send ICMPv6 ECHO_REQUEST to network host",
+ "pingAddress"
+);
+#endif /* CONFIG_CMD_PING6 */
+
+#if defined(CONFIG_CMD_CDP)
+
+static void cdp_update_env(void)
+{
+ char tmp[16];
+
+ if (cdp_appliance_vlan != htons(-1)) {
+ printf("CDP offered appliance VLAN %d\n",
+ ntohs(cdp_appliance_vlan));
+ vlan_to_string(cdp_appliance_vlan, tmp);
+ env_set("vlan", tmp);
+ net_our_vlan = cdp_appliance_vlan;
+ }
+
+ if (cdp_native_vlan != htons(-1)) {
+ printf("CDP offered native VLAN %d\n", ntohs(cdp_native_vlan));
+ vlan_to_string(cdp_native_vlan, tmp);
+ env_set("nvlan", tmp);
+ net_native_vlan = cdp_native_vlan;
+ }
+}
+
+int do_cdp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int r;
+
+ r = net_loop(CDP);
+ if (r < 0) {
+ printf("cdp failed; perhaps not a CISCO switch?\n");
+ return CMD_RET_FAILURE;
+ }
+
+ cdp_update_env();
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+ cdp, 1, 1, do_cdp,
+ "Perform CDP network configuration",
+ "\n"
+);
+#endif
+
+#if defined(CONFIG_CMD_SNTP)
+static struct udp_ops sntp_ops = {
+ .prereq = sntp_prereq,
+ .start = sntp_start,
+ .data = NULL,
+};
+
+int do_sntp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char *toff;
+
+ if (argc < 2) {
+ net_ntp_server = string_to_ip(env_get("ntpserverip"));
+ if (net_ntp_server.s_addr == 0) {
+ printf("ntpserverip not set\n");
+ return CMD_RET_FAILURE;
+ }
+ } else {
+ net_ntp_server = string_to_ip(argv[1]);
+ if (net_ntp_server.s_addr == 0) {
+ printf("Bad NTP server IP address\n");
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ toff = env_get("timeoffset");
+ if (toff == NULL)
+ net_ntp_time_offset = 0;
+ else
+ net_ntp_time_offset = simple_strtol(toff, NULL, 10);
+
+ if (udp_loop(&sntp_ops) < 0) {
+ printf("SNTP failed: host %pI4 not responding\n",
+ &net_ntp_server);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+ sntp, 2, 1, do_sntp,
+ "synchronize RTC via network",
+ "[NTP server IP]\n"
+);
+#endif
+
+#if defined(CONFIG_CMD_DNS)
+int do_dns(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ if (argc == 1)
+ return CMD_RET_USAGE;
+
+ /*
+ * We should check for a valid hostname:
+ * - Each label must be between 1 and 63 characters long
+ * - the entire hostname has a maximum of 255 characters
+ * - only the ASCII letters 'a' through 'z' (case-insensitive),
+ * the digits '0' through '9', and the hyphen
+ * - cannot begin or end with a hyphen
+ * - no other symbols, punctuation characters, or blank spaces are
+ * permitted
+ * but hey - this is a minimalist implmentation, so only check length
+ * and let the name server deal with things.
+ */
+ if (strlen(argv[1]) >= 255) {
+ printf("dns error: hostname too long\n");
+ return CMD_RET_FAILURE;
+ }
+
+ net_dns_resolve = argv[1];
+
+ if (argc == 3)
+ net_dns_env_var = argv[2];
+ else
+ net_dns_env_var = NULL;
+
+ if (net_loop(DNS) < 0) {
+ printf("dns lookup of %s failed, check setup\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+ dns, 3, 1, do_dns,
+ "lookup the IP of a hostname",
+ "hostname [envvar]"
+);
+
+#endif /* CONFIG_CMD_DNS */
+
+#if defined(CONFIG_CMD_LINK_LOCAL)
+static int do_link_local(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char tmp[22];
+
+ if (net_loop(LINKLOCAL) < 0)
+ return CMD_RET_FAILURE;
+
+ net_gateway.s_addr = 0;
+ ip_to_string(net_gateway, tmp);
+ env_set("gatewayip", tmp);
+
+ ip_to_string(net_netmask, tmp);
+ env_set("netmask", tmp);
+
+ ip_to_string(net_ip, tmp);
+ env_set("ipaddr", tmp);
+ env_set("llipaddr", tmp); /* store this for next time */
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+ linklocal, 1, 1, do_link_local,
+ "acquire a network IP address using the link-local protocol",
+ ""
+);
+
+#endif /* CONFIG_CMD_LINK_LOCAL */
diff --git a/cmd/nvedit.c b/cmd/nvedit.c
new file mode 100644
index 00000000000..11c3cea882b
--- /dev/null
+++ b/cmd/nvedit.c
@@ -0,0 +1,1296 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000-2013
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Andreas Heppel <aheppel@sysgo.de>
+ *
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ */
+
+/*
+ * Support for persistent environment data
+ *
+ * The "environment" is stored on external storage as a list of '\0'
+ * terminated "name=value" strings. The end of the list is marked by
+ * a double '\0'. The environment is preceded by a 32 bit CRC over
+ * the data part and, in case of redundant environment, a byte of
+ * flags.
+ *
+ * This linearized representation will also be used before
+ * relocation, i. e. as long as we don't have a full C runtime
+ * environment. After that, we use a hash table.
+ */
+
+#include <config.h>
+#include <cli.h>
+#include <command.h>
+#include <console.h>
+#include <env.h>
+#include <env_internal.h>
+#include <log.h>
+#include <search.h>
+#include <errno.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <asm/global_data.h>
+#include <linux/bitops.h>
+#include <linux/printk.h>
+#include <u-boot/crc.h>
+#include <linux/stddef.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * Maximum expected input data size for import command
+ */
+#define MAX_ENV_SIZE (1 << 20) /* 1 MiB */
+
+#ifndef CONFIG_XPL_BUILD
+/*
+ * Command interface: print one or all environment variables
+ *
+ * Returns 0 in case of error, or length of printed string
+ */
+static int env_print(char *name, int flag)
+{
+ char *res = NULL;
+ ssize_t len;
+
+ if (name) { /* print a single name */
+ struct env_entry e, *ep;
+
+ e.key = name;
+ e.data = NULL;
+ hsearch_r(e, ENV_FIND, &ep, &env_htab, flag);
+ if (ep == NULL)
+ return 0;
+ len = printf("%s=%s\n", ep->key, ep->data);
+ return len;
+ }
+
+ /* print whole list */
+ len = hexport_r(&env_htab, '\n', flag, &res, 0, 0, NULL);
+
+ if (len > 0) {
+ puts(res);
+ free(res);
+ return len;
+ }
+
+ /* should never happen */
+ printf("## Error: cannot export environment\n");
+ return 0;
+}
+
+static int do_env_print(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int i;
+ int rcode = 0;
+ int env_flag = H_HIDE_DOT;
+
+#if defined(CONFIG_CMD_NVEDIT_EFI)
+ if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'e')
+ return do_env_print_efi(cmdtp, flag, --argc, ++argv);
+#endif
+
+ if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'a') {
+ argc--;
+ argv++;
+ env_flag &= ~H_HIDE_DOT;
+ }
+
+ if (argc == 1) {
+ /* print all env vars */
+ rcode = env_print(NULL, env_flag);
+ if (!rcode)
+ return 1;
+ printf("\nEnvironment size: %d/%ld bytes\n",
+ rcode, (ulong)ENV_SIZE);
+ return 0;
+ }
+
+ /* print selected env vars */
+ env_flag &= ~H_HIDE_DOT;
+ for (i = 1; i < argc; ++i) {
+ int rc = env_print(argv[i], env_flag);
+ if (!rc) {
+ printf("## Error: \"%s\" not defined\n", argv[i]);
+ ++rcode;
+ }
+ }
+
+ return rcode;
+}
+
+#ifdef CONFIG_CMD_GREPENV
+static int do_env_grep(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ char *res = NULL;
+ int len, grep_how, grep_what;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ grep_how = H_MATCH_SUBSTR; /* default: substring search */
+ grep_what = H_MATCH_BOTH; /* default: grep names and values */
+
+ while (--argc > 0 && **++argv == '-') {
+ char *arg = *argv;
+ while (*++arg) {
+ switch (*arg) {
+#ifdef CONFIG_REGEX
+ case 'e': /* use regex matching */
+ grep_how = H_MATCH_REGEX;
+ break;
+#endif
+ case 'n': /* grep for name */
+ grep_what = H_MATCH_KEY;
+ break;
+ case 'v': /* grep for value */
+ grep_what = H_MATCH_DATA;
+ break;
+ case 'b': /* grep for both */
+ grep_what = H_MATCH_BOTH;
+ break;
+ case '-':
+ goto DONE;
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+ }
+
+DONE:
+ len = hexport_r(&env_htab, '\n',
+ flag | grep_what | grep_how,
+ &res, 0, argc, argv);
+
+ if (len > 0) {
+ puts(res);
+ free(res);
+ }
+
+ if (len < 2)
+ return 1;
+
+ return 0;
+}
+#endif
+#endif /* CONFIG_XPL_BUILD */
+
+#ifndef CONFIG_XPL_BUILD
+static int do_env_set(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ return env_do_env_set(flag, argc, argv, H_INTERACTIVE);
+}
+
+/*
+ * Prompt for environment variable
+ */
+#if defined(CONFIG_CMD_ASKENV)
+static int do_env_ask(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char message[CONFIG_SYS_CBSIZE];
+ int i, len, pos, size;
+ char *local_args[4];
+ char *endptr;
+
+ local_args[0] = argv[0];
+ local_args[1] = argv[1];
+ local_args[2] = NULL;
+ local_args[3] = NULL;
+
+ /*
+ * Check the syntax:
+ *
+ * env_ask envname [message1 ...] [size]
+ */
+ if (argc == 1)
+ return CMD_RET_USAGE;
+
+ /*
+ * We test the last argument if it can be converted
+ * into a decimal number. If yes, we assume it's
+ * the size. Otherwise we echo it as part of the
+ * message.
+ */
+ i = dectoul(argv[argc - 1], &endptr);
+ if (*endptr != '\0') { /* no size */
+ size = CONFIG_SYS_CBSIZE - 1;
+ } else { /* size given */
+ size = i;
+ --argc;
+ }
+
+ if (argc <= 2) {
+ sprintf(message, "Please enter '%s': ", argv[1]);
+ } else {
+ /* env_ask envname message1 ... messagen [size] */
+ for (i = 2, pos = 0; i < argc && pos+1 < sizeof(message); i++) {
+ if (pos)
+ message[pos++] = ' ';
+
+ strncpy(message + pos, argv[i], sizeof(message) - pos);
+ pos += strlen(argv[i]);
+ }
+ if (pos < sizeof(message) - 1) {
+ message[pos++] = ' ';
+ message[pos] = '\0';
+ } else
+ message[CONFIG_SYS_CBSIZE - 1] = '\0';
+ }
+
+ if (size >= CONFIG_SYS_CBSIZE)
+ size = CONFIG_SYS_CBSIZE - 1;
+
+ if (size <= 0)
+ return 1;
+
+ /* prompt for input */
+ len = cli_readline(message);
+
+ if (size < len)
+ console_buffer[size] = '\0';
+
+ len = 2;
+ if (console_buffer[0] != '\0') {
+ local_args[2] = console_buffer;
+ len = 3;
+ }
+
+ /* Continue calling setenv code */
+ return env_do_env_set(flag, len, local_args, H_INTERACTIVE);
+}
+#endif
+
+#if defined(CONFIG_CMD_ENV_CALLBACK)
+static int print_static_binding(const char *var_name, const char *callback_name,
+ void *priv)
+{
+ printf("\t%-20s %-20s\n", var_name, callback_name);
+
+ return 0;
+}
+
+static int print_active_callback(struct env_entry *entry)
+{
+ struct env_clbk_tbl *clbkp;
+ int i;
+ int num_callbacks;
+
+ if (entry->callback == NULL)
+ return 0;
+
+ /* look up the callback in the linker-list */
+ num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk);
+ for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk);
+ i < num_callbacks;
+ i++, clbkp++) {
+ if (entry->callback == clbkp->callback)
+ break;
+ }
+
+ if (i == num_callbacks)
+ /* this should probably never happen, but just in case... */
+ printf("\t%-20s %p\n", entry->key, entry->callback);
+ else
+ printf("\t%-20s %-20s\n", entry->key, clbkp->name);
+
+ return 0;
+}
+
+/*
+ * Print the callbacks available and what they are bound to
+ */
+static int do_env_callback(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct env_clbk_tbl *clbkp;
+ int i;
+ int num_callbacks;
+
+ /* Print the available callbacks */
+ puts("Available callbacks:\n");
+ puts("\tCallback Name\n");
+ puts("\t-------------\n");
+ num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk);
+ for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk);
+ i < num_callbacks;
+ i++, clbkp++)
+ printf("\t%s\n", clbkp->name);
+ puts("\n");
+
+ /* Print the static bindings that may exist */
+ puts("Static callback bindings:\n");
+ printf("\t%-20s %-20s\n", "Variable Name", "Callback Name");
+ printf("\t%-20s %-20s\n", "-------------", "-------------");
+ env_attr_walk(ENV_CALLBACK_LIST_STATIC, print_static_binding, NULL);
+ puts("\n");
+
+ /* walk through each variable and print the callback if it has one */
+ puts("Active callback bindings:\n");
+ printf("\t%-20s %-20s\n", "Variable Name", "Callback Name");
+ printf("\t%-20s %-20s\n", "-------------", "-------------");
+ hwalk_r(&env_htab, print_active_callback);
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_CMD_ENV_FLAGS)
+static int print_static_flags(const char *var_name, const char *flags,
+ void *priv)
+{
+ enum env_flags_vartype type = env_flags_parse_vartype(flags);
+ enum env_flags_varaccess access = env_flags_parse_varaccess(flags);
+
+ printf("\t%-20s %-20s %-20s\n", var_name,
+ env_flags_get_vartype_name(type),
+ env_flags_get_varaccess_name(access));
+
+ return 0;
+}
+
+static int print_active_flags(struct env_entry *entry)
+{
+ enum env_flags_vartype type;
+ enum env_flags_varaccess access;
+
+ if (entry->flags == 0)
+ return 0;
+
+ type = (enum env_flags_vartype)
+ (entry->flags & ENV_FLAGS_VARTYPE_BIN_MASK);
+ access = env_flags_parse_varaccess_from_binflags(entry->flags);
+ printf("\t%-20s %-20s %-20s\n", entry->key,
+ env_flags_get_vartype_name(type),
+ env_flags_get_varaccess_name(access));
+
+ return 0;
+}
+
+/*
+ * Print the flags available and what variables have flags
+ */
+static int do_env_flags(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ /* Print the available variable types */
+ printf("Available variable type flags (position %d):\n",
+ ENV_FLAGS_VARTYPE_LOC);
+ puts("\tFlag\tVariable Type Name\n");
+ puts("\t----\t------------------\n");
+ env_flags_print_vartypes();
+ puts("\n");
+
+ /* Print the available variable access types */
+ printf("Available variable access flags (position %d):\n",
+ ENV_FLAGS_VARACCESS_LOC);
+ puts("\tFlag\tVariable Access Name\n");
+ puts("\t----\t--------------------\n");
+ env_flags_print_varaccess();
+ puts("\n");
+
+ /* Print the static flags that may exist */
+ puts("Static flags:\n");
+ printf("\t%-20s %-20s %-20s\n", "Variable Name", "Variable Type",
+ "Variable Access");
+ printf("\t%-20s %-20s %-20s\n", "-------------", "-------------",
+ "---------------");
+ env_attr_walk(ENV_FLAGS_LIST_STATIC, print_static_flags, NULL);
+ puts("\n");
+
+ /* walk through each variable and print the flags if non-default */
+ puts("Active flags:\n");
+ printf("\t%-20s %-20s %-20s\n", "Variable Name", "Variable Type",
+ "Variable Access");
+ printf("\t%-20s %-20s %-20s\n", "-------------", "-------------",
+ "---------------");
+ hwalk_r(&env_htab, print_active_flags);
+ return 0;
+}
+#endif
+
+/*
+ * Interactively edit an environment variable
+ */
+#if defined(CONFIG_CMD_EDITENV)
+static int do_env_edit(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char buffer[CONFIG_SYS_CBSIZE];
+ char *init_val;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* before import into hashtable */
+ if (!(gd->flags & GD_FLG_ENV_READY))
+ return 1;
+
+ /* Set read buffer to initial value or empty sting */
+ init_val = env_get(argv[1]);
+ if (init_val)
+ snprintf(buffer, CONFIG_SYS_CBSIZE, "%s", init_val);
+ else
+ buffer[0] = '\0';
+
+ if (cli_readline_into_buffer("edit: ", buffer, 0) < 0)
+ return 1;
+
+ if (buffer[0] == '\0') {
+ const char * const _argv[3] = { "setenv", argv[1], NULL };
+
+ return env_do_env_set(0, 2, (char * const *)_argv, H_INTERACTIVE);
+ } else {
+ const char * const _argv[4] = { "setenv", argv[1], buffer,
+ NULL };
+
+ return env_do_env_set(0, 3, (char * const *)_argv, H_INTERACTIVE);
+ }
+}
+#endif /* CONFIG_CMD_EDITENV */
+
+#if defined(CONFIG_CMD_SAVEENV) && !IS_ENABLED(CONFIG_ENV_IS_DEFAULT)
+static int do_env_save(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return env_save() ? 1 : 0;
+}
+
+U_BOOT_CMD(
+ saveenv, 1, 0, do_env_save,
+ "save environment variables to persistent storage",
+ ""
+);
+
+#if defined(CONFIG_CMD_ERASEENV)
+static int do_env_erase(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return env_erase() ? 1 : 0;
+}
+
+U_BOOT_CMD(
+ eraseenv, 1, 0, do_env_erase,
+ "erase environment variables from persistent storage",
+ ""
+);
+#endif
+#endif
+
+#if defined(CONFIG_CMD_NVEDIT_LOAD)
+static int do_env_load(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return env_reload() ? 1 : 0;
+}
+#endif
+
+#if defined(CONFIG_CMD_NVEDIT_SELECT)
+static int do_env_select(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return env_select(argv[1]) ? 1 : 0;
+}
+#endif
+
+#endif /* CONFIG_XPL_BUILD */
+
+#ifndef CONFIG_XPL_BUILD
+static int do_env_default(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ int all = 0, env_flag = H_INTERACTIVE;
+
+ debug("Initial value for argc=%d\n", argc);
+ while (--argc > 0 && **++argv == '-') {
+ char *arg = *argv;
+
+ while (*++arg) {
+ switch (*arg) {
+ case 'a': /* default all */
+ all = 1;
+ break;
+ case 'f': /* force */
+ env_flag |= H_FORCE;
+ break;
+ case 'k':
+ env_flag |= H_NOCLEAR;
+ break;
+ default:
+ return cmd_usage(cmdtp);
+ }
+ }
+ }
+ debug("Final value for argc=%d\n", argc);
+ if (all && (argc == 0)) {
+ /* Reset the whole environment */
+ env_set_default("## Resetting to default environment\n",
+ env_flag);
+ return 0;
+ }
+ if (!all && (argc > 0)) {
+ /* Reset individual variables */
+ env_set_default_vars(argc, argv, env_flag);
+ return 0;
+ }
+
+ return cmd_usage(cmdtp);
+}
+
+static int do_env_delete(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ int env_flag = H_INTERACTIVE;
+ int ret = 0;
+
+ debug("Initial value for argc=%d\n", argc);
+ while (argc > 1 && **(argv + 1) == '-') {
+ char *arg = *++argv;
+
+ --argc;
+ while (*++arg) {
+ switch (*arg) {
+ case 'f': /* force */
+ env_flag |= H_FORCE;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+ }
+ debug("Final value for argc=%d\n", argc);
+
+ env_inc_id();
+
+ while (--argc > 0) {
+ char *name = *++argv;
+
+ if (hdelete_r(name, &env_htab, env_flag))
+ ret = 1;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_CMD_EXPORTENV
+/*
+ * env export [-t | -b | -c] [-s size] addr [var ...]
+ * -t: export as text format; if size is given, data will be
+ * padded with '\0' bytes; if not, one terminating '\0'
+ * will be added (which is included in the "filesize"
+ * setting so you can for exmple copy this to flash and
+ * keep the termination).
+ * -b: export as binary format (name=value pairs separated by
+ * '\0', list end marked by double "\0\0")
+ * -c: export as checksum protected environment format as
+ * used for example by "saveenv" command
+ * -s size:
+ * size of output buffer
+ * addr: memory address where environment gets stored
+ * var... List of variable names that get included into the
+ * export. Without arguments, the whole environment gets
+ * exported.
+ *
+ * With "-c" and size is NOT given, then the export command will
+ * format the data as currently used for the persistent storage,
+ * i. e. it will use CONFIG_ENV_SECT_SIZE as output block size and
+ * prepend a valid CRC32 checksum and, in case of redundant
+ * environment, a "current" redundancy flag. If size is given, this
+ * value will be used instead of CONFIG_ENV_SECT_SIZE; again, CRC32
+ * checksum and redundancy flag will be inserted.
+ *
+ * With "-b" and "-t", always only the real data (including a
+ * terminating '\0' byte) will be written; here the optional size
+ * argument will be used to make sure not to overflow the user
+ * provided buffer; the command will abort if the size is not
+ * sufficient. Any remaining space will be '\0' padded.
+ *
+ * On successful return, the variable "filesize" will be set.
+ * Note that filesize includes the trailing/terminating '\0' byte(s).
+ *
+ * Usage scenario: create a text snapshot/backup of the current settings:
+ *
+ * => env export -t 100000
+ * => era ${backup_addr} +${filesize}
+ * => cp.b 100000 ${backup_addr} ${filesize}
+ *
+ * Re-import this snapshot, deleting all other settings:
+ *
+ * => env import -d -t ${backup_addr}
+ */
+static int do_env_export(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ char buf[32];
+ ulong addr;
+ char *ptr, *cmd, *res;
+ size_t size = 0;
+ ssize_t len;
+ env_t *envp;
+ char sep = '\n';
+ int chk = 0;
+ int fmt = 0;
+
+ cmd = *argv;
+
+ while (--argc > 0 && **++argv == '-') {
+ char *arg = *argv;
+ while (*++arg) {
+ switch (*arg) {
+ case 'b': /* raw binary format */
+ if (fmt++)
+ goto sep_err;
+ sep = '\0';
+ break;
+ case 'c': /* external checksum format */
+ if (fmt++)
+ goto sep_err;
+ sep = '\0';
+ chk = 1;
+ break;
+ case 's': /* size given */
+ if (--argc <= 0)
+ return cmd_usage(cmdtp);
+ size = hextoul(*++argv, NULL);
+ goto NXTARG;
+ case 't': /* text format */
+ if (fmt++)
+ goto sep_err;
+ sep = '\n';
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+NXTARG: ;
+ }
+
+ if (argc < 1)
+ return CMD_RET_USAGE;
+
+ addr = hextoul(argv[0], NULL);
+ ptr = map_sysmem(addr, size);
+
+ if (size)
+ memset(ptr, '\0', size);
+
+ argc--;
+ argv++;
+
+ if (sep) { /* export as text file */
+ len = hexport_r(&env_htab, sep,
+ H_MATCH_KEY | H_MATCH_IDENT,
+ &ptr, size, argc, argv);
+ if (len < 0) {
+ pr_err("## Error: Cannot export environment: errno = %d\n",
+ errno);
+ return 1;
+ }
+ sprintf(buf, "%zX", (size_t)len);
+ env_set("filesize", buf);
+
+ return 0;
+ }
+
+ envp = (env_t *)ptr;
+
+ if (chk) /* export as checksum protected block */
+ res = (char *)envp->data;
+ else /* export as raw binary data */
+ res = ptr;
+
+ len = hexport_r(&env_htab, '\0',
+ H_MATCH_KEY | H_MATCH_IDENT,
+ &res, ENV_SIZE, argc, argv);
+ if (len < 0) {
+ pr_err("## Error: Cannot export environment: errno = %d\n",
+ errno);
+ return 1;
+ }
+
+ if (chk) {
+ envp->crc = crc32(0, envp->data,
+ size ? size - offsetof(env_t, data) : ENV_SIZE);
+#ifdef CONFIG_ENV_ADDR_REDUND
+ envp->flags = ENV_REDUND_ACTIVE;
+#endif
+ }
+ env_set_hex("filesize", len + offsetof(env_t, data));
+
+ return 0;
+
+sep_err:
+ printf("## Error: %s: only one of \"-b\", \"-c\" or \"-t\" allowed\n",
+ cmd);
+ return 1;
+}
+#endif
+
+#ifdef CONFIG_CMD_IMPORTENV
+/*
+ * env import [-d] [-t [-r] | -b | -c] addr [size] [var ...]
+ * -d: delete existing environment before importing if no var is
+ * passed; if vars are passed, if one var is in the current
+ * environment but not in the environment at addr, delete var from
+ * current environment;
+ * otherwise overwrite / append to existing definitions
+ * -t: assume text format; either "size" must be given or the
+ * text data must be '\0' terminated
+ * -r: handle CRLF like LF, that means exported variables with
+ * a content which ends with \r won't get imported. Used
+ * to import text files created with editors which are using CRLF
+ * for line endings. Only effective in addition to -t.
+ * -b: assume binary format ('\0' separated, "\0\0" terminated)
+ * -c: assume checksum protected environment format
+ * addr: memory address to read from
+ * size: length of input data; if missing, proper '\0'
+ * termination is mandatory
+ * if var is set and size should be missing (i.e. '\0'
+ * termination), set size to '-'
+ * var... List of the names of the only variables that get imported from
+ * the environment at address 'addr'. Without arguments, the whole
+ * environment gets imported.
+ */
+static int do_env_import(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ ulong addr;
+ char *cmd, *ptr;
+ char sep = '\n';
+ int chk = 0;
+ int fmt = 0;
+ int del = 0;
+ int crlf_is_lf = 0;
+ int wl = 0;
+ size_t size;
+
+ cmd = *argv;
+
+ while (--argc > 0 && **++argv == '-') {
+ char *arg = *argv;
+ while (*++arg) {
+ switch (*arg) {
+ case 'b': /* raw binary format */
+ if (fmt++)
+ goto sep_err;
+ sep = '\0';
+ break;
+ case 'c': /* external checksum format */
+ if (fmt++)
+ goto sep_err;
+ sep = '\0';
+ chk = 1;
+ break;
+ case 't': /* text format */
+ if (fmt++)
+ goto sep_err;
+ sep = '\n';
+ break;
+ case 'r': /* handle CRLF like LF */
+ crlf_is_lf = 1;
+ break;
+ case 'd':
+ del = 1;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+ }
+
+ if (argc < 1)
+ return CMD_RET_USAGE;
+
+ if (!fmt)
+ printf("## Warning: defaulting to text format\n");
+
+ if (sep != '\n' && crlf_is_lf )
+ crlf_is_lf = 0;
+
+ addr = hextoul(argv[0], NULL);
+ ptr = map_sysmem(addr, 0);
+
+ if (argc >= 2 && strcmp(argv[1], "-")) {
+ size = hextoul(argv[1], NULL);
+ } else if (chk) {
+ puts("## Error: external checksum format must pass size\n");
+ return CMD_RET_FAILURE;
+ } else {
+ char *s = ptr;
+
+ size = 0;
+
+ while (size < MAX_ENV_SIZE) {
+ if ((*s == sep) && (*(s+1) == '\0'))
+ break;
+ ++s;
+ ++size;
+ }
+ if (size == MAX_ENV_SIZE) {
+ printf("## Warning: Input data exceeds %d bytes"
+ " - truncated\n", MAX_ENV_SIZE);
+ }
+ size += 2;
+ printf("## Info: input data size = %zu = 0x%zX\n", size, size);
+ }
+
+ if (argc > 2)
+ wl = 1;
+
+ if (chk) {
+ uint32_t crc;
+ env_t *ep = (env_t *)ptr;
+
+ if (size <= offsetof(env_t, data)) {
+ printf("## Error: Invalid size 0x%zX\n", size);
+ return 1;
+ }
+
+ size -= offsetof(env_t, data);
+ memcpy(&crc, &ep->crc, sizeof(crc));
+
+ if (crc32(0, ep->data, size) != crc) {
+ puts("## Error: bad CRC, import failed\n");
+ return 1;
+ }
+ ptr = (char *)ep->data;
+ }
+
+ if (!himport_r(&env_htab, ptr, size, sep, del ? 0 : H_NOCLEAR,
+ crlf_is_lf, wl ? argc - 2 : 0, wl ? &argv[2] : NULL)) {
+ pr_err("## Error: Environment import failed: errno = %d\n",
+ errno);
+ return 1;
+ }
+ gd->flags |= GD_FLG_ENV_READY;
+
+ return 0;
+
+sep_err:
+ printf("## %s: only one of \"-b\", \"-c\" or \"-t\" allowed\n",
+ cmd);
+ return 1;
+}
+#endif
+
+#if defined(CONFIG_CMD_NVEDIT_INDIRECT)
+static int do_env_indirect(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ char *to = argv[1];
+ char *from = argv[2];
+ char *default_value = NULL;
+ int ret = 0;
+ char *val;
+
+ if (argc < 3 || argc > 4) {
+ return CMD_RET_USAGE;
+ }
+
+ if (argc == 4) {
+ default_value = argv[3];
+ }
+
+ val = env_get(from) ?: default_value;
+ if (!val) {
+ printf("## env indirect: Environment variable for <from> (%s) does not exist.\n", from);
+
+ return CMD_RET_FAILURE;
+ }
+
+ ret = env_set(to, val);
+
+ if (ret == 0) {
+ return CMD_RET_SUCCESS;
+ }
+ else {
+ return CMD_RET_FAILURE;
+ }
+}
+#endif
+
+#if defined(CONFIG_CMD_NVEDIT_INFO)
+/*
+ * print_env_info - print environment information
+ */
+static int print_env_info(void)
+{
+ const char *value;
+
+ /* print environment validity value */
+ switch (gd->env_valid) {
+ case ENV_INVALID:
+ value = "invalid";
+ break;
+ case ENV_VALID:
+ value = "valid";
+ break;
+ case ENV_REDUND:
+ value = "redundant";
+ break;
+ default:
+ value = "unknown";
+ break;
+ }
+ printf("env_valid = %s\n", value);
+
+ /* print environment ready flag */
+ value = gd->flags & GD_FLG_ENV_READY ? "true" : "false";
+ printf("env_ready = %s\n", value);
+
+ /* print environment using default flag */
+ value = gd->flags & GD_FLG_ENV_DEFAULT ? "true" : "false";
+ printf("env_use_default = %s\n", value);
+
+ return CMD_RET_SUCCESS;
+}
+
+#define ENV_INFO_IS_DEFAULT BIT(0) /* default environment bit mask */
+#define ENV_INFO_IS_PERSISTED BIT(1) /* environment persistence bit mask */
+
+/*
+ * env info - display environment information
+ * env info [-d] - evaluate whether default environment is used
+ * env info [-p] - evaluate whether environment can be persisted
+ * Add [-q] - quiet mode, use only for command result, for test by example:
+ * test env info -p -d -q
+ */
+static int do_env_info(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ int eval_flags = 0;
+ int eval_results = 0;
+ bool quiet = false;
+#if defined(CONFIG_CMD_SAVEENV) && !IS_ENABLED(CONFIG_ENV_IS_DEFAULT)
+ enum env_location loc;
+#endif
+
+ /* display environment information */
+ if (argc <= 1)
+ return print_env_info();
+
+ /* process options */
+ while (--argc > 0 && **++argv == '-') {
+ char *arg = *argv;
+
+ while (*++arg) {
+ switch (*arg) {
+ case 'd':
+ eval_flags |= ENV_INFO_IS_DEFAULT;
+ break;
+ case 'p':
+ eval_flags |= ENV_INFO_IS_PERSISTED;
+ break;
+ case 'q':
+ quiet = true;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+ }
+
+ /* evaluate whether default environment is used */
+ if (eval_flags & ENV_INFO_IS_DEFAULT) {
+ if (gd->flags & GD_FLG_ENV_DEFAULT) {
+ if (!quiet)
+ printf("Default environment is used\n");
+ eval_results |= ENV_INFO_IS_DEFAULT;
+ } else {
+ if (!quiet)
+ printf("Environment was loaded from persistent storage\n");
+ }
+ }
+
+ /* evaluate whether environment can be persisted */
+ if (eval_flags & ENV_INFO_IS_PERSISTED) {
+#if defined(CONFIG_CMD_SAVEENV) && !IS_ENABLED(CONFIG_ENV_IS_DEFAULT)
+ loc = env_get_location(ENVOP_SAVE, gd->env_load_prio);
+ if (ENVL_NOWHERE != loc && ENVL_UNKNOWN != loc) {
+ if (!quiet)
+ printf("Environment can be persisted\n");
+ eval_results |= ENV_INFO_IS_PERSISTED;
+ } else {
+ if (!quiet)
+ printf("Environment cannot be persisted\n");
+ }
+#else
+ if (!quiet)
+ printf("Environment cannot be persisted\n");
+#endif
+ }
+
+ /* The result of evaluations is combined with AND */
+ if (eval_flags != eval_results)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+#endif
+
+#if defined(CONFIG_CMD_ENV_EXISTS)
+static int do_env_exists(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct env_entry e, *ep;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ e.key = argv[1];
+ e.data = NULL;
+ hsearch_r(e, ENV_FIND, &ep, &env_htab, 0);
+
+ return (ep == NULL) ? 1 : 0;
+}
+#endif
+
+/*
+ * New command line interface: "env" command with subcommands
+ */
+static struct cmd_tbl cmd_env_sub[] = {
+#if defined(CONFIG_CMD_ASKENV)
+ U_BOOT_CMD_MKENT(ask, CONFIG_SYS_MAXARGS, 1, do_env_ask, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(default, 1, 0, do_env_default, "", ""),
+ U_BOOT_CMD_MKENT(delete, CONFIG_SYS_MAXARGS, 0, do_env_delete, "", ""),
+#if defined(CONFIG_CMD_EDITENV)
+ U_BOOT_CMD_MKENT(edit, 2, 0, do_env_edit, "", ""),
+#endif
+#if defined(CONFIG_CMD_ENV_CALLBACK)
+ U_BOOT_CMD_MKENT(callbacks, 1, 0, do_env_callback, "", ""),
+#endif
+#if defined(CONFIG_CMD_ENV_FLAGS)
+ U_BOOT_CMD_MKENT(flags, 1, 0, do_env_flags, "", ""),
+#endif
+#if defined(CONFIG_CMD_EXPORTENV)
+ U_BOOT_CMD_MKENT(export, 4, 0, do_env_export, "", ""),
+#endif
+#if defined(CONFIG_CMD_GREPENV)
+ U_BOOT_CMD_MKENT(grep, CONFIG_SYS_MAXARGS, 1, do_env_grep, "", ""),
+#endif
+#if defined(CONFIG_CMD_IMPORTENV)
+ U_BOOT_CMD_MKENT(import, 5, 0, do_env_import, "", ""),
+#endif
+#if defined(CONFIG_CMD_NVEDIT_INDIRECT)
+ U_BOOT_CMD_MKENT(indirect, 3, 0, do_env_indirect, "", ""),
+#endif
+#if defined(CONFIG_CMD_NVEDIT_INFO)
+ U_BOOT_CMD_MKENT(info, 3, 0, do_env_info, "", ""),
+#endif
+#if defined(CONFIG_CMD_NVEDIT_LOAD)
+ U_BOOT_CMD_MKENT(load, 1, 0, do_env_load, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(print, CONFIG_SYS_MAXARGS, 1, do_env_print, "", ""),
+#if defined(CONFIG_CMD_RUN)
+ U_BOOT_CMD_MKENT(run, CONFIG_SYS_MAXARGS, 1, do_run, "", ""),
+#endif
+#if defined(CONFIG_CMD_SAVEENV) && !IS_ENABLED(CONFIG_ENV_IS_DEFAULT)
+ U_BOOT_CMD_MKENT(save, 1, 0, do_env_save, "", ""),
+#if defined(CONFIG_CMD_ERASEENV)
+ U_BOOT_CMD_MKENT(erase, 1, 0, do_env_erase, "", ""),
+#endif
+#endif
+#if defined(CONFIG_CMD_NVEDIT_SELECT)
+ U_BOOT_CMD_MKENT(select, 2, 0, do_env_select, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(set, CONFIG_SYS_MAXARGS, 0, do_env_set, "", ""),
+#if defined(CONFIG_CMD_ENV_EXISTS)
+ U_BOOT_CMD_MKENT(exists, 2, 0, do_env_exists, "", ""),
+#endif
+};
+
+static int do_env(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* drop initial "env" arg */
+ argc--;
+ argv++;
+
+ cp = find_cmd_tbl(argv[0], cmd_env_sub, ARRAY_SIZE(cmd_env_sub));
+
+ if (cp)
+ return cp->cmd(cmdtp, flag, argc, argv);
+
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_LONGHELP(env,
+#if defined(CONFIG_CMD_ASKENV)
+ "ask name [message] [size] - ask for environment variable\nenv "
+#endif
+#if defined(CONFIG_CMD_ENV_CALLBACK)
+ "callbacks - print callbacks and their associated variables\nenv "
+#endif
+ "default [-k] [-f] -a - [forcibly] reset default environment\n"
+ "env default [-k] [-f] var [...] - [forcibly] reset variable(s) to their default values\n"
+ " \"-k\": keep variables not defined in default environment\n"
+ "env delete [-f] var [...] - [forcibly] delete variable(s)\n"
+#if defined(CONFIG_CMD_EDITENV)
+ "env edit name - edit environment variable\n"
+#endif
+#if defined(CONFIG_CMD_ENV_EXISTS)
+ "env exists name - tests for existence of variable\n"
+#endif
+#if defined(CONFIG_CMD_EXPORTENV)
+ "env export [-t | -b | -c] [-s size] addr [var ...] - export environment\n"
+#endif
+#if defined(CONFIG_CMD_ENV_FLAGS)
+ "env flags - print variables that have non-default flags\n"
+#endif
+#if defined(CONFIG_CMD_GREPENV)
+#ifdef CONFIG_REGEX
+ "env grep [-e] [-n | -v | -b] string [...] - search environment\n"
+#else
+ "env grep [-n | -v | -b] string [...] - search environment\n"
+#endif
+#endif
+#if defined(CONFIG_CMD_IMPORTENV)
+ "env import [-d] [-t [-r] | -b | -c] addr [size] [var ...] - import environment\n"
+#endif
+#if defined(CONFIG_CMD_NVEDIT_INDIRECT)
+ "env indirect <to> <from> [default] - sets <to> to the value of <from>, using [default] when unset\n"
+#endif
+#if defined(CONFIG_CMD_NVEDIT_INFO)
+ "env info - display environment information\n"
+ "env info [-d] [-p] [-q] - evaluate environment information\n"
+ " \"-d\": default environment is used\n"
+ " \"-p\": environment can be persisted\n"
+ " \"-q\": quiet output\n"
+#endif
+ "env print [-a | name ...] - print environment\n"
+#if defined(CONFIG_CMD_NVEDIT_EFI)
+ "env print -e [-guid guid] [-n] [name ...] - print UEFI environment\n"
+#endif
+#if defined(CONFIG_CMD_RUN)
+ "env run var [...] - run commands in an environment variable\n"
+#endif
+#if defined(CONFIG_CMD_SAVEENV) && !IS_ENABLED(CONFIG_ENV_IS_DEFAULT)
+ "env save - save environment\n"
+#if defined(CONFIG_CMD_ERASEENV)
+ "env erase - erase environment\n"
+#endif
+#endif
+#if defined(CONFIG_CMD_NVEDIT_LOAD)
+ "env load - load environment\n"
+#endif
+#if defined(CONFIG_CMD_NVEDIT_SELECT)
+ "env select [target] - select environment target\n"
+#endif
+#if defined(CONFIG_CMD_NVEDIT_EFI)
+ "env set -e [-nv][-bs][-rt][-at][-a][-i addr:size][-v] name [arg ...]\n"
+ " - set UEFI variable; unset if '-i' or 'arg' not specified\n"
+#endif
+ "env set [-f] name [arg ...]\n");
+
+U_BOOT_CMD(
+ env, CONFIG_SYS_MAXARGS, 1, do_env,
+ "environment handling commands", env_help_text
+);
+
+/*
+ * Old command line interface, kept for compatibility
+ */
+
+#if defined(CONFIG_CMD_EDITENV)
+U_BOOT_CMD_COMPLETE(
+ editenv, 2, 0, do_env_edit,
+ "edit environment variable",
+ "name\n"
+ " - edit environment variable 'name'",
+ var_complete
+);
+#endif
+
+U_BOOT_CMD_COMPLETE(
+ printenv, CONFIG_SYS_MAXARGS, 1, do_env_print,
+ "print environment variables",
+ "[-a]\n - print [all] values of all environment variables\n"
+#if defined(CONFIG_CMD_NVEDIT_EFI)
+ "printenv -e [-guid guid][-n] [name ...]\n"
+ " - print UEFI variable 'name' or all the variables\n"
+ " \"-guid\": GUID xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n"
+ " \"-n\": suppress dumping variable's value\n"
+#endif
+ "printenv name ...\n"
+ " - print value of environment variable 'name'",
+ var_complete
+);
+
+#ifdef CONFIG_CMD_GREPENV
+U_BOOT_CMD_COMPLETE(
+ grepenv, CONFIG_SYS_MAXARGS, 0, do_env_grep,
+ "search environment variables",
+#ifdef CONFIG_REGEX
+ "[-e] [-n | -v | -b] string ...\n"
+#else
+ "[-n | -v | -b] string ...\n"
+#endif
+ " - list environment name=value pairs matching 'string'\n"
+#ifdef CONFIG_REGEX
+ " \"-e\": enable regular expressions;\n"
+#endif
+ " \"-n\": search variable names; \"-v\": search values;\n"
+ " \"-b\": search both names and values (default)",
+ var_complete
+);
+#endif
+
+U_BOOT_CMD_COMPLETE(
+ setenv, CONFIG_SYS_MAXARGS, 0, do_env_set,
+ "set environment variables",
+#if defined(CONFIG_CMD_NVEDIT_EFI)
+ "-e [-guid guid][-nv][-bs][-rt][-at][-a][-v]\n"
+ " [-i addr:size name], or [name [value ...]]\n"
+ " - set UEFI variable 'name' to 'value' ...'\n"
+ " \"-guid\": GUID xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n"
+ " \"-nv\": set non-volatile attribute\n"
+ " \"-bs\": set boot-service attribute\n"
+ " \"-rt\": set runtime attribute\n"
+ " \"-at\": set time-based authentication attribute\n"
+ " \"-a\": append-write\n"
+ " \"-i addr:size\": use <addr,size> as variable's value\n"
+ " \"-v\": verbose message\n"
+ " - delete UEFI variable 'name' if 'value' not specified\n"
+#endif
+ "setenv [-f] name value ...\n"
+ " - [forcibly] set environment variable 'name' to 'value ...'\n"
+ "setenv [-f] name\n"
+ " - [forcibly] delete environment variable 'name'",
+ var_complete
+);
+
+#if defined(CONFIG_CMD_ASKENV)
+
+U_BOOT_CMD(
+ askenv, CONFIG_SYS_MAXARGS, 1, do_env_ask,
+ "get environment variables from stdin",
+ "name [message] [size]\n"
+ " - get environment variable 'name' from stdin (max 'size' chars)"
+);
+#endif
+
+#if defined(CONFIG_CMD_RUN)
+U_BOOT_CMD_COMPLETE(
+ run, CONFIG_SYS_MAXARGS, 1, do_run,
+ "run commands in an environment variable",
+ "var [...]\n"
+ " - run the commands in the environment variable(s) 'var'",
+ var_complete
+);
+#endif
+#endif /* CONFIG_XPL_BUILD */
diff --git a/cmd/nvedit_efi.c b/cmd/nvedit_efi.c
new file mode 100644
index 00000000000..351ae47e870
--- /dev/null
+++ b/cmd/nvedit_efi.c
@@ -0,0 +1,533 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Integrate UEFI variables to u-boot env interface
+ *
+ * Copyright (c) 2018 AKASHI Takahiro, Linaro Limited
+ */
+
+#include <charset.h>
+#include <command.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <env.h>
+#include <exports.h>
+#include <hexdump.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <rtc.h>
+#include <u-boot/uuid.h>
+#include <linux/kernel.h>
+
+/*
+ * From efi_variable.c,
+ *
+ * Mapping between UEFI variables and u-boot variables:
+ *
+ * efi_$guid_$varname = {attributes}(type)value
+ */
+
+static const struct {
+ u32 mask;
+ char *text;
+} efi_var_attrs[] = {
+ {EFI_VARIABLE_NON_VOLATILE, "NV"},
+ {EFI_VARIABLE_BOOTSERVICE_ACCESS, "BS"},
+ {EFI_VARIABLE_RUNTIME_ACCESS, "RT"},
+ {EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, "AW"},
+ {EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, "AT"},
+ {EFI_VARIABLE_READ_ONLY, "RO"},
+};
+
+/**
+ * efi_dump_single_var() - show information about a UEFI variable
+ *
+ * @name: Name of the variable
+ * @guid: Vendor GUID
+ * @verbose: if true, dump data
+ *
+ * Show information encoded in one UEFI variable
+ */
+static void efi_dump_single_var(u16 *name, const efi_guid_t *guid, bool verbose)
+{
+ u32 attributes;
+ u8 *data;
+ u64 time;
+ struct rtc_time tm;
+ efi_uintn_t size;
+ int count, i;
+ efi_status_t ret;
+
+ data = NULL;
+ size = 0;
+ ret = efi_get_variable_int(name, guid, &attributes, &size, data, &time);
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ data = malloc(size);
+ if (!data)
+ goto out;
+
+ ret = efi_get_variable_int(name, guid, &attributes, &size,
+ data, &time);
+ }
+ if (ret == EFI_NOT_FOUND) {
+ printf("Error: \"%ls\" not defined\n", name);
+ goto out;
+ }
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ rtc_to_tm(time, &tm);
+ printf("%ls:\n %pUl (%pUs)\n", name, guid, guid);
+ if (attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
+ printf(" %04d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year,
+ tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ printf(" ");
+ for (count = 0, i = 0; i < ARRAY_SIZE(efi_var_attrs); i++)
+ if (attributes & efi_var_attrs[i].mask) {
+ if (count)
+ putc('|');
+ count++;
+ puts(efi_var_attrs[i].text);
+ }
+ printf(", DataSize = 0x%zx\n", size);
+ if (verbose)
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
+ data, size, true);
+
+out:
+ free(data);
+}
+
+static bool match_name(int argc, char *const argv[], u16 *var_name16)
+{
+ char *buf, *p;
+ size_t buflen;
+ int i;
+ bool result = false;
+
+ buflen = utf16_utf8_strlen(var_name16) + 1;
+ buf = calloc(1, buflen);
+ if (!buf)
+ return result;
+
+ p = buf;
+ utf16_utf8_strcpy(&p, var_name16);
+
+ for (i = 0; i < argc; argc--, argv++) {
+ if (!strcmp(buf, argv[i])) {
+ result = true;
+ goto out;
+ }
+ }
+
+out:
+ free(buf);
+
+ return result;
+}
+
+/**
+ * efi_dump_var_all() - show information about all the UEFI variables
+ *
+ * @argc: Number of arguments (variables)
+ * @argv: Argument (variable name) array
+ * @verbose: if true, dump data
+ * Return: CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
+ *
+ * Show information encoded in all the UEFI variables
+ */
+static int efi_dump_var_all(int argc, char *const argv[],
+ const efi_guid_t *guid_p, bool verbose)
+{
+ u16 *var_name16, *p;
+ efi_uintn_t buf_size, size;
+ efi_guid_t guid;
+ efi_status_t ret;
+ bool match = false;
+
+ buf_size = 128;
+ var_name16 = malloc(buf_size);
+ if (!var_name16)
+ return CMD_RET_FAILURE;
+
+ var_name16[0] = 0;
+ for (;;) {
+ size = buf_size;
+ ret = efi_get_next_variable_name_int(&size, var_name16,
+ &guid);
+ if (ret == EFI_NOT_FOUND)
+ break;
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ buf_size = size;
+ p = realloc(var_name16, buf_size);
+ if (!p) {
+ free(var_name16);
+ return CMD_RET_FAILURE;
+ }
+ var_name16 = p;
+ ret = efi_get_next_variable_name_int(&size, var_name16,
+ &guid);
+ }
+ if (ret != EFI_SUCCESS) {
+ free(var_name16);
+ return CMD_RET_FAILURE;
+ }
+
+ if (guid_p && guidcmp(guid_p, &guid))
+ continue;
+ if (!argc || match_name(argc, argv, var_name16)) {
+ match = true;
+ efi_dump_single_var(var_name16, &guid, verbose);
+ }
+ }
+ free(var_name16);
+
+ if (!match && argc == 1) {
+ printf("Error: \"%s\" not defined\n", argv[0]);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * do_env_print_efi() - show information about UEFI variables
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
+ *
+ * This function is for "env print -e" or "printenv -e" command:
+ * => env print -e [-n] [-guid <guid> | -all] [var [...]]
+ * If one or more variable names are specified, show information
+ * named UEFI variables, otherwise show all the UEFI variables.
+ */
+int do_env_print_efi(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const efi_guid_t *guid_p = NULL;
+ efi_guid_t guid;
+ bool verbose = true;
+ efi_status_t ret;
+
+ /* Initialize EFI drivers */
+ ret = efi_init_obj_list();
+ if (ret != EFI_SUCCESS) {
+ printf("Error: Cannot initialize UEFI sub-system, r = %lu\n",
+ ret & ~EFI_ERROR_MASK);
+ return CMD_RET_FAILURE;
+ }
+
+ for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
+ if (!strcmp(argv[0], "-guid")) {
+ if (argc == 1)
+ return CMD_RET_USAGE;
+ argc--;
+ argv++;
+ if (uuid_str_to_bin(argv[0], guid.b,
+ UUID_STR_FORMAT_GUID))
+ return CMD_RET_USAGE;
+ guid_p = (const efi_guid_t *)guid.b;
+ } else if (!strcmp(argv[0], "-n")) {
+ verbose = false;
+ } else {
+ return CMD_RET_USAGE;
+ }
+ }
+
+ /* enumerate and show all UEFI variables */
+ return efi_dump_var_all(argc, argv, guid_p, verbose);
+}
+
+/**
+ * append_value() - encode UEFI variable's value
+ * @bufp: Buffer of encoded UEFI variable's value
+ * @sizep: Size of buffer
+ * @data: data to be encoded into the value
+ * Return: 0 on success, -1 otherwise
+ *
+ * Interpret a given data string and append it to buffer.
+ * Buffer will be realloc'ed if necessary.
+ *
+ * Currently supported formats are:
+ * =0x0123...: Hexadecimal number
+ * =H0123...: Hexadecimal-byte array
+ * ="...", =S"..." or <string>:
+ * String
+ */
+static int append_value(char **bufp, size_t *sizep, char *data)
+{
+ char *tmp_buf = NULL, *new_buf = NULL, *value;
+ unsigned long len = 0;
+
+ if (!strncmp(data, "=0x", 3)) { /* hexadecimal number */
+ union {
+ u8 u8;
+ u16 u16;
+ u32 u32;
+ u64 u64;
+ } tmp_data;
+ unsigned long hex_value;
+ void *hex_ptr;
+
+ data += 3;
+ len = strlen(data);
+ if ((len & 0x1)) /* not multiple of two */
+ return -1;
+
+ len /= 2;
+ if (len > 8)
+ return -1;
+ else if (len > 4)
+ len = 8;
+ else if (len > 2)
+ len = 4;
+
+ /* convert hex hexadecimal number */
+ if (strict_strtoul(data, 16, &hex_value) < 0)
+ return -1;
+
+ tmp_buf = malloc(len);
+ if (!tmp_buf)
+ return -1;
+
+ if (len == 1) {
+ tmp_data.u8 = hex_value;
+ hex_ptr = &tmp_data.u8;
+ } else if (len == 2) {
+ tmp_data.u16 = hex_value;
+ hex_ptr = &tmp_data.u16;
+ } else if (len == 4) {
+ tmp_data.u32 = hex_value;
+ hex_ptr = &tmp_data.u32;
+ } else {
+ tmp_data.u64 = hex_value;
+ hex_ptr = &tmp_data.u64;
+ }
+ memcpy(tmp_buf, hex_ptr, len);
+ value = tmp_buf;
+
+ } else if (!strncmp(data, "=H", 2)) { /* hexadecimal-byte array */
+ data += 2;
+ len = strlen(data);
+ if (len & 0x1) /* not multiple of two */
+ return -1;
+
+ len /= 2;
+ tmp_buf = malloc(len);
+ if (!tmp_buf)
+ return -1;
+
+ if (hex2bin((u8 *)tmp_buf, data, len) < 0) {
+ printf("Error: illegal hexadecimal string\n");
+ free(tmp_buf);
+ return -1;
+ }
+
+ value = tmp_buf;
+ } else { /* string */
+ if (!strncmp(data, "=\"", 2) || !strncmp(data, "=S\"", 3)) {
+ if (data[1] == '"')
+ data += 2;
+ else
+ data += 3;
+ value = data;
+ len = strlen(data) - 1;
+ if (data[len] != '"')
+ return -1;
+ } else {
+ value = data;
+ len = strlen(data);
+ }
+ }
+
+ new_buf = realloc(*bufp, *sizep + len);
+ if (!new_buf)
+ goto out;
+
+ memcpy(new_buf + *sizep, value, len);
+ *bufp = new_buf;
+ *sizep += len;
+
+out:
+ free(tmp_buf);
+
+ return 0;
+}
+
+/**
+ * do_env_set_efi() - set UEFI variable
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
+ *
+ * This function is for "env set -e" or "setenv -e" command:
+ * => env set -e [-guid guid][-nv][-bs][-rt][-at][-a][-v]
+ * [-i address:size] var, or
+ * var [value ...]
+ * Encode values specified and set given UEFI variable.
+ * If no value is specified, delete the variable.
+ */
+int do_env_set_efi(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *var_name, *value, *ep;
+ ulong addr;
+ efi_uintn_t size;
+ efi_guid_t guid;
+ u32 attributes;
+ bool default_guid, verbose, value_on_memory;
+ u16 *var_name16;
+ efi_status_t ret;
+
+ if (argc == 1)
+ return CMD_RET_USAGE;
+
+ /* Initialize EFI drivers */
+ ret = efi_init_obj_list();
+ if (ret != EFI_SUCCESS) {
+ printf("Error: Cannot initialize UEFI sub-system, r = %lu\n",
+ ret & ~EFI_ERROR_MASK);
+ return CMD_RET_FAILURE;
+ }
+
+ /*
+ * attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ * EFI_VARIABLE_RUNTIME_ACCESS;
+ */
+ value = NULL;
+ size = 0;
+ attributes = 0;
+ guid = efi_global_variable_guid;
+ default_guid = true;
+ verbose = false;
+ value_on_memory = false;
+ for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
+ if (!strcmp(argv[0], "-guid")) {
+ if (argc == 1)
+ return CMD_RET_USAGE;
+
+ argc--;
+ argv++;
+ if (uuid_str_to_bin(argv[0], guid.b,
+ UUID_STR_FORMAT_GUID)) {
+ return CMD_RET_USAGE;
+ }
+ default_guid = false;
+ } else if (!strcmp(argv[0], "-bs")) {
+ attributes |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
+ } else if (!strcmp(argv[0], "-rt")) {
+ attributes |= EFI_VARIABLE_RUNTIME_ACCESS;
+ } else if (!strcmp(argv[0], "-nv")) {
+ attributes |= EFI_VARIABLE_NON_VOLATILE;
+ } else if (!strcmp(argv[0], "-at")) {
+ attributes |=
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
+ } else if (!strcmp(argv[0], "-a")) {
+ attributes |= EFI_VARIABLE_APPEND_WRITE;
+ } else if (!strcmp(argv[0], "-i")) {
+ /* data comes from memory */
+ if (argc == 1)
+ return CMD_RET_USAGE;
+
+ argc--;
+ argv++;
+ addr = hextoul(argv[0], &ep);
+ if (*ep != ':')
+ return CMD_RET_USAGE;
+
+ /* 0 should be allowed for delete */
+ size = hextoul(++ep, NULL);
+
+ value_on_memory = true;
+ } else if (!strcmp(argv[0], "-v")) {
+ verbose = true;
+ } else {
+ return CMD_RET_USAGE;
+ }
+ }
+ if (!argc)
+ return CMD_RET_USAGE;
+
+ var_name = argv[0];
+ if (default_guid) {
+ if (!strcmp(var_name, "db") || !strcmp(var_name, "dbx") ||
+ !strcmp(var_name, "dbt"))
+ guid = efi_guid_image_security_database;
+ else
+ guid = efi_global_variable_guid;
+ }
+
+ if (verbose) {
+ printf("GUID: %pUl (%pUs)\n", &guid, &guid);
+ printf("Attributes: 0x%x\n", attributes);
+ }
+
+ /* for value */
+ if (value_on_memory)
+ value = map_sysmem(addr, 0);
+ else if (argc > 1)
+ for (argc--, argv++; argc > 0; argc--, argv++)
+ if (append_value(&value, &size, argv[0]) < 0) {
+ printf("## Failed to process an argument, %s\n",
+ argv[0]);
+ ret = CMD_RET_FAILURE;
+ goto out;
+ }
+
+ if (size && verbose) {
+ printf("Value:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET,
+ 16, 1, value, size, true);
+ }
+
+ var_name16 = efi_convert_string(var_name);
+ if (!var_name16) {
+ printf("## Out of memory\n");
+ ret = CMD_RET_FAILURE;
+ goto out;
+ }
+ ret = efi_set_variable_int(var_name16, &guid, attributes, size, value,
+ true);
+ free(var_name16);
+ unmap_sysmem(value);
+ if (ret == EFI_SUCCESS) {
+ ret = CMD_RET_SUCCESS;
+ } else {
+ const char *msg;
+
+ switch (ret) {
+ case EFI_NOT_FOUND:
+ msg = " (not found)";
+ break;
+ case EFI_WRITE_PROTECTED:
+ msg = " (read only)";
+ break;
+ case EFI_INVALID_PARAMETER:
+ msg = " (invalid parameter)";
+ break;
+ case EFI_SECURITY_VIOLATION:
+ msg = " (validation failed)";
+ break;
+ case EFI_OUT_OF_RESOURCES:
+ msg = " (out of memory)";
+ break;
+ default:
+ msg = "";
+ break;
+ }
+ printf("## Failed to set EFI variable%s\n", msg);
+ ret = CMD_RET_FAILURE;
+ }
+out:
+ if (value_on_memory)
+ unmap_sysmem(value);
+ else
+ free(value);
+
+ return ret;
+}
diff --git a/cmd/nvme.c b/cmd/nvme.c
new file mode 100644
index 00000000000..f2c9acba5c3
--- /dev/null
+++ b/cmd/nvme.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 NXP Semiconductors
+ * Copyright (C) 2017 Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <blk.h>
+#include <command.h>
+#include <dm.h>
+#include <nvme.h>
+
+static int nvme_curr_dev;
+
+static int do_nvme(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+
+ if (argc == 2) {
+ if (strncmp(argv[1], "scan", 4) == 0) {
+ ret = nvme_scan_namespace();
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ return ret;
+ }
+ if (strncmp(argv[1], "deta", 4) == 0) {
+ struct udevice *udev;
+
+ ret = blk_get_device(UCLASS_NVME, nvme_curr_dev,
+ &udev);
+ if (ret < 0)
+ return CMD_RET_FAILURE;
+
+ nvme_print_info(udev);
+
+ return ret;
+ }
+ }
+
+ return blk_common_cmd(argc, argv, UCLASS_NVME, &nvme_curr_dev);
+}
+
+U_BOOT_CMD(
+ nvme, 8, 1, do_nvme,
+ "NVM Express sub-system",
+ "scan - scan NVMe devices\n"
+ "nvme detail - show details of current NVMe device\n"
+ "nvme info - show all available NVMe devices\n"
+ "nvme device [dev] - show or set current NVMe device\n"
+ "nvme part [dev] - print partition table of one or all NVMe devices\n"
+ "nvme read addr blk# cnt - read `cnt' blocks starting at block\n"
+ " `blk#' to memory address `addr'\n"
+ "nvme write addr blk# cnt - write `cnt' blocks starting at block\n"
+ " `blk#' from memory address `addr'"
+);
diff --git a/cmd/onenand.c b/cmd/onenand.c
new file mode 100644
index 00000000000..6e808ce3fce
--- /dev/null
+++ b/cmd/onenand.c
@@ -0,0 +1,599 @@
+/*
+ * U-Boot command for OneNAND support
+ *
+ * Copyright (C) 2005-2008 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <command.h>
+#include <malloc.h>
+#include <linux/printk.h>
+
+#include <linux/compat.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+
+#include <asm/io.h>
+
+static struct mtd_info *mtd;
+
+static loff_t next_ofs;
+static loff_t skip_ofs;
+
+static int arg_off_size_onenand(int argc, char *const argv[], ulong *off,
+ size_t *size)
+{
+ if (argc >= 1) {
+ if (!(str2long(argv[0], off))) {
+ printf("'%s' is not a number\n", argv[0]);
+ return -1;
+ }
+ } else {
+ *off = 0;
+ }
+
+ if (argc >= 2) {
+ if (!(str2long(argv[1], (ulong *)size))) {
+ printf("'%s' is not a number\n", argv[1]);
+ return -1;
+ }
+ } else {
+ *size = mtd->size - *off;
+ }
+
+ if ((*off + *size) > mtd->size) {
+ printf("total chip size (0x%llx) exceeded!\n", mtd->size);
+ return -1;
+ }
+
+ if (*size == mtd->size)
+ puts("whole chip\n");
+ else
+ printf("offset 0x%lx, size 0x%zx\n", *off, *size);
+
+ return 0;
+}
+
+static int onenand_block_read(loff_t from, size_t len,
+ size_t *retlen, u_char *buf, int oob)
+{
+ struct onenand_chip *this = mtd->priv;
+ int blocks = (int) len >> this->erase_shift;
+ int blocksize = (1 << this->erase_shift);
+ loff_t ofs = from;
+ struct mtd_oob_ops ops = {
+ .retlen = 0,
+ };
+ int ret;
+
+ if (oob)
+ ops.ooblen = blocksize;
+ else
+ ops.len = blocksize;
+
+ while (blocks) {
+ ret = mtd_block_isbad(mtd, ofs);
+ if (ret) {
+ printk("Bad blocks %d at 0x%x\n",
+ (u32)(ofs >> this->erase_shift), (u32)ofs);
+ ofs += blocksize;
+ continue;
+ }
+
+ if (oob)
+ ops.oobbuf = buf;
+ else
+ ops.datbuf = buf;
+
+ ops.retlen = 0;
+ ret = mtd_read_oob(mtd, ofs, &ops);
+ if (ret) {
+ printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
+ ofs += blocksize;
+ continue;
+ }
+ ofs += blocksize;
+ buf += blocksize;
+ blocks--;
+ *retlen += ops.retlen;
+ }
+
+ return 0;
+}
+
+static int onenand_write_oneblock_withoob(loff_t to, const u_char * buf,
+ size_t *retlen)
+{
+ struct mtd_oob_ops ops = {
+ .len = mtd->writesize,
+ .ooblen = mtd->oobsize,
+ .mode = MTD_OPS_AUTO_OOB,
+ };
+ int page, ret = 0;
+ for (page = 0; page < (mtd->erasesize / mtd->writesize); page ++) {
+ ops.datbuf = (u_char *)buf;
+ buf += mtd->writesize;
+ ops.oobbuf = (u_char *)buf;
+ buf += mtd->oobsize;
+ ret = mtd_write_oob(mtd, to, &ops);
+ if (ret)
+ break;
+ to += mtd->writesize;
+ }
+
+ *retlen = (ret) ? 0 : mtd->erasesize;
+ return ret;
+}
+
+static int onenand_block_write(loff_t to, size_t len,
+ size_t *retlen, const u_char * buf, int withoob)
+{
+ struct onenand_chip *this = mtd->priv;
+ int blocks = len >> this->erase_shift;
+ int blocksize = (1 << this->erase_shift);
+ loff_t ofs;
+ size_t _retlen = 0;
+ int ret;
+
+ if ((to & (mtd->writesize - 1)) != 0) {
+ printf("Attempt to write non block-aligned data\n");
+ *retlen = 0;
+ return 1;
+ }
+
+ if (to == next_ofs) {
+ next_ofs = to + len;
+ to += skip_ofs;
+ } else {
+ next_ofs = to + len;
+ skip_ofs = 0;
+ }
+ ofs = to;
+
+ while (blocks) {
+ ret = mtd_block_isbad(mtd, ofs);
+ if (ret) {
+ printk("Bad blocks %d at 0x%x\n",
+ (u32)(ofs >> this->erase_shift), (u32)ofs);
+ skip_ofs += blocksize;
+ goto next;
+ }
+
+ if (!withoob)
+ ret = mtd_write(mtd, ofs, blocksize, &_retlen, buf);
+ else
+ ret = onenand_write_oneblock_withoob(ofs, buf, &_retlen);
+ if (ret) {
+ printk("Write failed 0x%x, %d", (u32)ofs, ret);
+ skip_ofs += blocksize;
+ goto next;
+ }
+
+ buf += blocksize;
+ blocks--;
+ *retlen += _retlen;
+next:
+ ofs += blocksize;
+ }
+
+ return 0;
+}
+
+static int onenand_block_erase(u32 start, u32 size, int force)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct erase_info instr = {};
+ loff_t ofs;
+ int ret;
+ int blocksize = 1 << this->erase_shift;
+
+ for (ofs = start; ofs < (start + size); ofs += blocksize) {
+ ret = mtd_block_isbad(mtd, ofs);
+ if (ret && !force) {
+ printf("Skip erase bad block %d at 0x%x\n",
+ (u32)(ofs >> this->erase_shift), (u32)ofs);
+ continue;
+ }
+
+ instr.addr = ofs;
+ instr.len = blocksize;
+ instr.priv = force;
+ instr.mtd = mtd;
+ ret = mtd_erase(mtd, &instr);
+ if (ret) {
+ printf("erase failed block %d at 0x%x\n",
+ (u32)(ofs >> this->erase_shift), (u32)ofs);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static int onenand_block_test(u32 start, u32 size)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct erase_info instr = {};
+
+ int blocks;
+ loff_t ofs;
+ int blocksize = 1 << this->erase_shift;
+ int start_block, end_block;
+ size_t retlen;
+ u_char *buf;
+ u_char *verify_buf;
+ int ret;
+
+ buf = malloc(blocksize);
+ if (!buf) {
+ printf("Not enough malloc space available!\n");
+ return -1;
+ }
+
+ verify_buf = malloc(blocksize);
+ if (!verify_buf) {
+ printf("Not enough malloc space available!\n");
+ return -1;
+ }
+
+ start_block = start >> this->erase_shift;
+ end_block = (start + size) >> this->erase_shift;
+
+ /* Protect boot-loader from badblock testing */
+ if (start_block < 2)
+ start_block = 2;
+
+ if (end_block > (mtd->size >> this->erase_shift))
+ end_block = mtd->size >> this->erase_shift;
+
+ blocks = start_block;
+ ofs = start;
+ while (blocks < end_block) {
+ printf("\rTesting block %d at 0x%x", (u32)(ofs >> this->erase_shift), (u32)ofs);
+
+ ret = mtd_block_isbad(mtd, ofs);
+ if (ret) {
+ printf("Skip erase bad block %d at 0x%x\n",
+ (u32)(ofs >> this->erase_shift), (u32)ofs);
+ goto next;
+ }
+
+ instr.addr = ofs;
+ instr.len = blocksize;
+ ret = mtd_erase(mtd, &instr);
+ if (ret) {
+ printk("Erase failed 0x%x, %d\n", (u32)ofs, ret);
+ goto next;
+ }
+
+ ret = mtd_write(mtd, ofs, blocksize, &retlen, buf);
+ if (ret) {
+ printk("Write failed 0x%x, %d\n", (u32)ofs, ret);
+ goto next;
+ }
+
+ ret = mtd_read(mtd, ofs, blocksize, &retlen, verify_buf);
+ if (ret) {
+ printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
+ goto next;
+ }
+
+ if (memcmp(buf, verify_buf, blocksize))
+ printk("\nRead/Write test failed at 0x%x\n", (u32)ofs);
+
+next:
+ ofs += blocksize;
+ blocks++;
+ }
+ printf("...Done\n");
+
+ free(buf);
+ free(verify_buf);
+
+ return 0;
+}
+
+static int onenand_dump(struct mtd_info *mtd, ulong off, int only_oob)
+{
+ int i;
+ u_char *datbuf, *oobbuf, *p;
+ struct mtd_oob_ops ops;
+ loff_t addr;
+
+ datbuf = malloc(mtd->writesize + mtd->oobsize);
+ oobbuf = malloc(mtd->oobsize);
+ if (!datbuf || !oobbuf) {
+ puts("No memory for page buffer\n");
+ return 1;
+ }
+ off &= ~(mtd->writesize - 1);
+ addr = (loff_t) off;
+ memset(&ops, 0, sizeof(ops));
+ ops.datbuf = datbuf;
+ ops.oobbuf = oobbuf;
+ ops.len = mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ops.retlen = 0;
+ i = mtd_read_oob(mtd, addr, &ops);
+ if (i < 0) {
+ printf("Error (%d) reading page %08lx\n", i, off);
+ free(datbuf);
+ free(oobbuf);
+ return 1;
+ }
+ printf("Page %08lx dump:\n", off);
+ i = mtd->writesize >> 4;
+ p = datbuf;
+
+ while (i--) {
+ if (!only_oob)
+ printf("\t%02x %02x %02x %02x %02x %02x %02x %02x"
+ " %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
+ p[8], p[9], p[10], p[11], p[12], p[13], p[14],
+ p[15]);
+ p += 16;
+ }
+ puts("OOB:\n");
+ i = mtd->oobsize >> 3;
+ p = oobbuf;
+
+ while (i--) {
+ printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+ p += 8;
+ }
+ free(datbuf);
+ free(oobbuf);
+
+ return 0;
+}
+
+static int do_onenand_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ printf("%s\n", mtd->name);
+ return 0;
+}
+
+static int do_onenand_bad(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong ofs;
+
+ mtd = &onenand_mtd;
+ /* Currently only one OneNAND device is supported */
+ printf("\nDevice %d bad blocks:\n", 0);
+ for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) {
+ if (mtd_block_isbad(mtd, ofs))
+ printf(" %08x\n", (u32)ofs);
+ }
+
+ return 0;
+}
+
+static int do_onenand_read(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *s;
+ int oob = 0;
+ ulong addr, ofs;
+ size_t len;
+ int ret = 0;
+ size_t retlen = 0;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ s = strchr(argv[0], '.');
+ if ((s != NULL) && (!strcmp(s, ".oob")))
+ oob = 1;
+
+ addr = (ulong)hextoul(argv[1], NULL);
+
+ printf("\nOneNAND read: ");
+ if (arg_off_size_onenand(argc - 2, argv + 2, &ofs, &len) != 0)
+ return 1;
+
+ ret = onenand_block_read(ofs, len, &retlen, (u8 *)addr, oob);
+
+ printf(" %zu bytes read: %s\n", retlen, ret ? "ERROR" : "OK");
+
+ return ret == 0 ? 0 : 1;
+}
+
+static int do_onenand_write(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr, ofs;
+ size_t len;
+ int ret = 0, withoob = 0;
+ size_t retlen = 0;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ if (strncmp(argv[0] + 6, "yaffs", 5) == 0)
+ withoob = 1;
+
+ addr = (ulong)hextoul(argv[1], NULL);
+
+ printf("\nOneNAND write: ");
+ if (arg_off_size_onenand(argc - 2, argv + 2, &ofs, &len) != 0)
+ return 1;
+
+ ret = onenand_block_write(ofs, len, &retlen, (u8 *)addr, withoob);
+
+ printf(" %zu bytes written: %s\n", retlen, ret ? "ERROR" : "OK");
+
+ return ret == 0 ? 0 : 1;
+}
+
+static int do_onenand_erase(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong ofs;
+ int ret = 0;
+ size_t len;
+ int force;
+
+ /*
+ * Syntax is:
+ * 0 1 2 3 4
+ * onenand erase [force] [off size]
+ */
+ argc--;
+ argv++;
+ if (argc)
+ {
+ if (!strcmp("force", argv[0]))
+ {
+ force = 1;
+ argc--;
+ argv++;
+ }
+ }
+ printf("\nOneNAND erase: ");
+
+ /* skip first two or three arguments, look for offset and size */
+ if (arg_off_size_onenand(argc, argv, &ofs, &len) != 0)
+ return 1;
+
+ ret = onenand_block_erase(ofs, len, force);
+
+ printf("%s\n", ret ? "ERROR" : "OK");
+
+ return ret == 0 ? 0 : 1;
+}
+
+static int do_onenand_test(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong ofs;
+ int ret = 0;
+ size_t len;
+
+ /*
+ * Syntax is:
+ * 0 1 2 3 4
+ * onenand test [force] [off size]
+ */
+
+ printf("\nOneNAND test: ");
+
+ /* skip first two or three arguments, look for offset and size */
+ if (arg_off_size_onenand(argc - 1, argv + 1, &ofs, &len) != 0)
+ return 1;
+
+ ret = onenand_block_test(ofs, len);
+
+ printf("%s\n", ret ? "ERROR" : "OK");
+
+ return ret == 0 ? 0 : 1;
+}
+
+static int do_onenand_dump(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong ofs;
+ int ret = 0;
+ char *s;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ s = strchr(argv[0], '.');
+ ofs = (int)hextoul(argv[1], NULL);
+
+ if (s != NULL && strcmp(s, ".oob") == 0)
+ ret = onenand_dump(mtd, ofs, 1);
+ else
+ ret = onenand_dump(mtd, ofs, 0);
+
+ return ret == 0 ? 1 : 0;
+}
+
+static int do_onenand_markbad(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret = 0;
+ ulong addr;
+
+ argc -= 2;
+ argv += 2;
+
+ if (argc <= 0)
+ return CMD_RET_USAGE;
+
+ while (argc > 0) {
+ addr = hextoul(*argv, NULL);
+
+ if (mtd_block_markbad(mtd, addr)) {
+ printf("block 0x%08lx NOT marked "
+ "as bad! ERROR %d\n",
+ addr, ret);
+ ret = 1;
+ } else {
+ printf("block 0x%08lx successfully "
+ "marked as bad\n",
+ addr);
+ }
+ --argc;
+ ++argv;
+ }
+ return ret;
+}
+
+static struct cmd_tbl cmd_onenand_sub[] = {
+ U_BOOT_CMD_MKENT(info, 1, 0, do_onenand_info, "", ""),
+ U_BOOT_CMD_MKENT(bad, 1, 0, do_onenand_bad, "", ""),
+ U_BOOT_CMD_MKENT(read, 4, 0, do_onenand_read, "", ""),
+ U_BOOT_CMD_MKENT(write, 4, 0, do_onenand_write, "", ""),
+ U_BOOT_CMD_MKENT(write.yaffs, 4, 0, do_onenand_write, "", ""),
+ U_BOOT_CMD_MKENT(erase, 3, 0, do_onenand_erase, "", ""),
+ U_BOOT_CMD_MKENT(test, 3, 0, do_onenand_test, "", ""),
+ U_BOOT_CMD_MKENT(dump, 2, 0, do_onenand_dump, "", ""),
+ U_BOOT_CMD_MKENT(markbad, CONFIG_SYS_MAXARGS, 0, do_onenand_markbad, "", ""),
+};
+
+static int do_onenand(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *c;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ mtd = &onenand_mtd;
+
+ /* Strip off leading 'onenand' command argument */
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], &cmd_onenand_sub[0], ARRAY_SIZE(cmd_onenand_sub));
+
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+ else
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+ onenand, CONFIG_SYS_MAXARGS, 1, do_onenand,
+ "OneNAND sub-system",
+ "info - show available OneNAND devices\n"
+ "onenand bad - show bad blocks\n"
+ "onenand read[.oob] addr off size\n"
+ "onenand write[.yaffs] addr off size\n"
+ " read/write 'size' bytes starting at offset 'off'\n"
+ " to/from memory address 'addr', skipping bad blocks.\n"
+ "onenand erase [force] [off size] - erase 'size' bytes from\n"
+ "onenand test [off size] - test 'size' bytes from\n"
+ " offset 'off' (entire device if not specified)\n"
+ "onenand dump[.oob] off - dump page\n"
+ "onenand markbad off [...] - mark bad block(s) at offset (UNSAFE)"
+);
diff --git a/cmd/optee.c b/cmd/optee.c
new file mode 100644
index 00000000000..155c9f1bb73
--- /dev/null
+++ b/cmd/optee.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2024, Advanced Micro Devices, Inc.
+ */
+#include <command.h>
+#include <errno.h>
+#include <tee.h>
+#include <vsprintf.h>
+#include <linux/string.h>
+
+#define TA_HELLO_WORLD_CMD_INC_VALUE 0
+/* This needs to match the UUID of the Hello World TA. */
+#define TA_HELLO_WORLD_UUID \
+ { 0x8aaaf200, 0x2450, 0x11e4, \
+ { 0xab, 0xe2, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b} }
+
+static int hello_world_ta(unsigned int value)
+{
+ const struct tee_optee_ta_uuid uuid = TA_HELLO_WORLD_UUID;
+ struct tee_open_session_arg session_arg;
+ struct udevice *tee = NULL;
+ struct tee_invoke_arg arg;
+ struct tee_param param[2];
+ int rc;
+
+ tee = tee_find_device(tee, NULL, NULL, NULL);
+ if (!tee)
+ return -ENODEV;
+
+ memset(&session_arg, 0, sizeof(session_arg));
+ tee_optee_ta_uuid_to_octets(session_arg.uuid, &uuid);
+ rc = tee_open_session(tee, &session_arg, 0, NULL);
+ if (rc) {
+ printf("tee_open_session(): failed(%d)\n", rc);
+ return rc;
+ }
+
+ arg.func = TA_HELLO_WORLD_CMD_INC_VALUE;
+ arg.session = session_arg.session;
+
+ param[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INOUT;
+ param[0].u.value.a = value;
+
+ printf("Value before: 0x%x\n", (int)param[0].u.value.a);
+ printf("Calling TA\n");
+ tee_invoke_func(tee, &arg, 1, param);
+
+ printf("Value after: 0x%x\n", (int)param[0].u.value.a);
+ return tee_close_session(tee, session_arg.session);
+}
+
+static int do_optee_hello_world_ta(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ int ret, value = 0;
+
+ if (argc > 1)
+ value = hextoul(argv[1], NULL);
+
+ ret = hello_world_ta(value);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_LONGHELP(optee,
+ "hello [<value>] Invoke the OP-TEE 'Hello World' TA\n");
+
+U_BOOT_CMD_WITH_SUBCMDS(optee, "OP-TEE commands", optee_help_text,
+ U_BOOT_SUBCMD_MKENT(hello, 2, 1, do_optee_hello_world_ta));
diff --git a/cmd/optee_rpmb.c b/cmd/optee_rpmb.c
new file mode 100644
index 00000000000..b155278ee2a
--- /dev/null
+++ b/cmd/optee_rpmb.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 NXP
+ */
+
+#include <command.h>
+#include <env.h>
+#include <errno.h>
+#include <image.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <tee.h>
+#include <tee/optee_ta_avb.h>
+
+static struct udevice *tee;
+static u32 session;
+
+static int avb_ta_open_session(void)
+{
+ const struct tee_optee_ta_uuid uuid = TA_AVB_UUID;
+ struct tee_open_session_arg arg;
+ int rc;
+
+ tee = tee_find_device(tee, NULL, NULL, NULL);
+ if (!tee)
+ return -ENODEV;
+
+ memset(&arg, 0, sizeof(arg));
+ tee_optee_ta_uuid_to_octets(arg.uuid, &uuid);
+ rc = tee_open_session(tee, &arg, 0, NULL);
+ if (!rc)
+ session = arg.session;
+
+ return 0;
+}
+
+static int invoke_func(u32 func, ulong num_param, struct tee_param *param)
+{
+ struct tee_invoke_arg arg;
+
+ if (!tee)
+ if (avb_ta_open_session())
+ return -ENODEV;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.func = func;
+ arg.session = session;
+
+ if (tee_invoke_func(tee, &arg, num_param, param))
+ return -EFAULT;
+ switch (arg.ret) {
+ case TEE_SUCCESS:
+ return 0;
+ case TEE_ERROR_OUT_OF_MEMORY:
+ case TEE_ERROR_STORAGE_NO_SPACE:
+ return -ENOSPC;
+ case TEE_ERROR_ITEM_NOT_FOUND:
+ return -EIO;
+ case TEE_ERROR_TARGET_DEAD:
+ /*
+ * The TA has paniced, close the session to reload the TA
+ * for the next request.
+ */
+ tee_close_session(tee, session);
+ tee = NULL;
+ return -EIO;
+ default:
+ return -EIO;
+ }
+}
+
+static int read_persistent_value(const char *name,
+ size_t buffer_size,
+ u8 *out_buffer,
+ size_t *out_num_bytes_read)
+{
+ int rc = 0;
+ struct tee_shm *shm_name;
+ struct tee_shm *shm_buf;
+ struct tee_param param[2];
+ size_t name_size = strlen(name) + 1;
+
+ if (!tee)
+ if (avb_ta_open_session())
+ return -ENODEV;
+
+ rc = tee_shm_alloc(tee, name_size,
+ TEE_SHM_ALLOC, &shm_name);
+ if (rc) {
+ rc = -ENOMEM;
+ goto close_session;
+ }
+
+ rc = tee_shm_alloc(tee, buffer_size,
+ TEE_SHM_ALLOC, &shm_buf);
+ if (rc) {
+ rc = -ENOMEM;
+ goto free_name;
+ }
+
+ memcpy(shm_name->addr, name, name_size);
+
+ memset(param, 0, sizeof(param));
+ param[0].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INPUT;
+ param[0].u.memref.shm = shm_name;
+ param[0].u.memref.size = name_size;
+ param[1].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT;
+ param[1].u.memref.shm = shm_buf;
+ param[1].u.memref.size = buffer_size;
+
+ rc = invoke_func(TA_AVB_CMD_READ_PERSIST_VALUE,
+ 2, param);
+ if (rc)
+ goto out;
+
+ if (param[1].u.memref.size > buffer_size) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ *out_num_bytes_read = param[1].u.memref.size;
+
+ memcpy(out_buffer, shm_buf->addr, *out_num_bytes_read);
+
+out:
+ tee_shm_free(shm_buf);
+free_name:
+ tee_shm_free(shm_name);
+close_session:
+ tee_close_session(tee, session);
+ tee = NULL;
+
+ return rc;
+}
+
+static int write_persistent_value(const char *name,
+ size_t value_size,
+ const u8 *value)
+{
+ int rc = 0;
+ struct tee_shm *shm_name;
+ struct tee_shm *shm_buf;
+ struct tee_param param[2];
+ size_t name_size = strlen(name) + 1;
+
+ if (!value_size)
+ return -EINVAL;
+
+ if (!tee) {
+ if (avb_ta_open_session())
+ return -ENODEV;
+ }
+
+ rc = tee_shm_alloc(tee, name_size,
+ TEE_SHM_ALLOC, &shm_name);
+ if (rc) {
+ rc = -ENOMEM;
+ goto close_session;
+ }
+
+ rc = tee_shm_alloc(tee, value_size,
+ TEE_SHM_ALLOC, &shm_buf);
+ if (rc) {
+ rc = -ENOMEM;
+ goto free_name;
+ }
+
+ memcpy(shm_name->addr, name, name_size);
+ memcpy(shm_buf->addr, value, value_size);
+
+ memset(param, 0, sizeof(param));
+ param[0].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INPUT;
+ param[0].u.memref.shm = shm_name;
+ param[0].u.memref.size = name_size;
+ param[1].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INPUT;
+ param[1].u.memref.shm = shm_buf;
+ param[1].u.memref.size = value_size;
+
+ rc = invoke_func(TA_AVB_CMD_WRITE_PERSIST_VALUE,
+ 2, param);
+ if (rc)
+ goto out;
+
+out:
+ tee_shm_free(shm_buf);
+free_name:
+ tee_shm_free(shm_name);
+close_session:
+ tee_close_session(tee, session);
+ tee = NULL;
+
+ return rc;
+}
+
+int do_optee_rpmb_read(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ const char *name;
+ size_t bytes;
+ size_t bytes_read;
+ void *buffer;
+ char *endp;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ name = argv[1];
+ bytes = dectoul(argv[2], &endp);
+ if (*endp && *endp != '\n')
+ return CMD_RET_USAGE;
+
+ buffer = malloc(bytes);
+ if (!buffer)
+ return CMD_RET_FAILURE;
+
+ if (read_persistent_value(name, bytes, buffer, &bytes_read) == 0) {
+ printf("Read %zu bytes, value = %s\n", bytes_read,
+ (char *)buffer);
+ free(buffer);
+ return CMD_RET_SUCCESS;
+ }
+
+ printf("Failed to read persistent value\n");
+
+ free(buffer);
+
+ return CMD_RET_FAILURE;
+}
+
+int do_optee_rpmb_write(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ const char *name;
+ const char *value;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ name = argv[1];
+ value = argv[2];
+
+ if (write_persistent_value(name, strlen(value) + 1,
+ (const uint8_t *)value) == 0) {
+ printf("Wrote %zu bytes\n", strlen(value) + 1);
+ return CMD_RET_SUCCESS;
+ }
+
+ printf("Failed to write persistent value\n");
+
+ return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl cmd_optee_rpmb[] = {
+ U_BOOT_CMD_MKENT(read_pvalue, 3, 0, do_optee_rpmb_read, "", ""),
+ U_BOOT_CMD_MKENT(write_pvalue, 3, 0, do_optee_rpmb_write, "", ""),
+};
+
+static int do_optee_rpmb(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ struct cmd_tbl *cp;
+
+ cp = find_cmd_tbl(argv[1], cmd_optee_rpmb, ARRAY_SIZE(cmd_optee_rpmb));
+
+ argc--;
+ argv++;
+
+ if (!cp || argc > cp->maxargs)
+ return CMD_RET_USAGE;
+
+ if (flag == CMD_FLAG_REPEAT)
+ return CMD_RET_FAILURE;
+
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD (
+ optee_rpmb, 29, 0, do_optee_rpmb,
+ "Provides commands for testing secure storage on RPMB on OPTEE",
+ "read_pvalue <name> <bytes> - read a persistent value <name>\n"
+ "optee_rpmb write_pvalue <name> <value> - write a persistent value <name>\n"
+ );
diff --git a/cmd/osd.c b/cmd/osd.c
new file mode 100644
index 00000000000..5671338d9e7
--- /dev/null
+++ b/cmd/osd.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2017
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ *
+ * based on the gdsys osd driver, which is
+ *
+ * (C) Copyright 2010
+ * Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <hexdump.h>
+#include <video_osd.h>
+#include <malloc.h>
+
+/* Container for selected OSD device */
+static struct udevice *osd_cur;
+
+/**
+ * cmd_osd_set_osd_num() - Set the OSD selected for operation
+ *
+ * Set the OSD device, which will be used by all subsequent OSD commands.
+ *
+ * Devices are identified by their uclass sequence number (as listed by 'osd
+ * show').
+ *
+ * @osdnum: The OSD device to be selected, identified by its sequence number.
+ * Return: 0 if OK, -ve on error
+ */
+static int cmd_osd_set_osd_num(unsigned int osdnum)
+{
+ struct udevice *osd;
+ int res;
+
+ res = uclass_get_device_by_seq(UCLASS_VIDEO_OSD, osdnum, &osd);
+ if (res) {
+ printf("%s: No OSD %u (err = %d)\n", __func__, osdnum, res);
+ return res;
+ }
+ osd_cur = osd;
+
+ return 0;
+}
+
+/**
+ * osd_get_osd_cur() - Get the selected OSD device
+ *
+ * Get the OSD device that is used by all OSD commands.
+ *
+ * @osdp: Pointer to structure that will receive the currently selected OSD
+ * device.
+ * Return: 0 if OK, -ve on error
+ */
+static int osd_get_osd_cur(struct udevice **osdp)
+{
+ if (!osd_cur) {
+ puts("No osd selected\n");
+ return -ENODEV;
+ }
+ *osdp = osd_cur;
+
+ return 0;
+}
+
+/**
+ * show_osd() - Display information about a OSD device
+ *
+ * Display a device's ID (sequence number), and whether it is active (i.e.
+ * probed) or not.
+ *
+ * @osd: OSD device to print information for
+ */
+static void show_osd(struct udevice *osd)
+{
+ printf("OSD %d:\t%s", dev_seq(osd), osd->name);
+ if (device_active(osd))
+ printf(" (active)");
+ printf("\n");
+}
+
+static int do_osd_write(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ uint x, y;
+ uint count;
+ char *hexstr;
+ u8 *buffer;
+ size_t buflen;
+ int res;
+
+ if (argc < 4 || (strlen(argv[3]) % 2))
+ return CMD_RET_USAGE;
+
+ if (!osd_cur) {
+ puts("No osd selected\n");
+ return CMD_RET_FAILURE;
+ }
+
+ x = hextoul(argv[1], NULL);
+ y = hextoul(argv[2], NULL);
+ hexstr = argv[3];
+ count = (argc > 4) ? hextoul(argv[4], NULL) : 1;
+
+ buflen = strlen(hexstr) / 2;
+
+ buffer = malloc(buflen);
+ if (!buffer) {
+ puts("Memory allocation failure\n");
+ return CMD_RET_FAILURE;
+ }
+
+ res = hex2bin(buffer, hexstr, buflen);
+ if (res) {
+ free(buffer);
+ puts("Hexadecimal input contained invalid characters\n");
+ return CMD_RET_FAILURE;
+ }
+
+ res = video_osd_set_mem(osd_cur, x, y, buffer, buflen, count);
+ if (res) {
+ free(buffer);
+ printf("%s: Could not write to video mem\n",
+ osd_cur->name);
+ return CMD_RET_FAILURE;
+ }
+
+ free(buffer);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_osd_print(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ uint x, y;
+ u8 color;
+ char *text;
+ int res;
+
+ if (argc < 5)
+ return CMD_RET_USAGE;
+
+ if (!osd_cur) {
+ puts("No osd selected\n");
+ return CMD_RET_FAILURE;
+ }
+
+ x = hextoul(argv[1], NULL);
+ y = hextoul(argv[2], NULL);
+ color = hextoul(argv[3], NULL);
+ text = argv[4];
+
+ res = video_osd_print(osd_cur, x, y, color, text);
+ if (res) {
+ printf("Could not print string to osd %s\n", osd_cur->name);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_osd_size(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ uint x, y;
+ int res;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ if (!osd_cur) {
+ puts("No osd selected\n");
+ return CMD_RET_FAILURE;
+ }
+
+ x = hextoul(argv[1], NULL);
+ y = hextoul(argv[2], NULL);
+
+ res = video_osd_set_size(osd_cur, x, y);
+ if (res) {
+ printf("Could not set size on osd %s\n", osd_cur->name);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_show_osd(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *osd;
+
+ if (argc == 1) {
+ /* show all OSDs */
+ struct uclass *uc;
+ int res;
+
+ res = uclass_get(UCLASS_VIDEO_OSD, &uc);
+ if (res) {
+ printf("Error while getting OSD uclass (err=%d)\n",
+ res);
+ return CMD_RET_FAILURE;
+ }
+
+ uclass_foreach_dev(osd, uc)
+ show_osd(osd);
+ } else {
+ int i, res;
+
+ /* show specific OSD */
+ i = dectoul(argv[1], NULL);
+
+ res = uclass_get_device_by_seq(UCLASS_VIDEO_OSD, i, &osd);
+ if (res) {
+ printf("Invalid osd %d: err=%d\n", i, res);
+ return CMD_RET_FAILURE;
+ }
+ show_osd(osd);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_osd_num(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int osd_no;
+ int res = 0;
+
+ if (argc == 1) {
+ /* querying current setting */
+ struct udevice *osd;
+
+ if (!osd_get_osd_cur(&osd))
+ osd_no = dev_seq(osd);
+ else
+ osd_no = -1;
+ printf("Current osd is %d\n", osd_no);
+ } else {
+ osd_no = dectoul(argv[1], NULL);
+ printf("Setting osd to %d\n", osd_no);
+
+ res = cmd_osd_set_osd_num(osd_no);
+ if (res)
+ printf("Failure changing osd number (err = %d)\n", res);
+ }
+
+ return res ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+static struct cmd_tbl cmd_osd_sub[] = {
+ U_BOOT_CMD_MKENT(show, 1, 1, do_show_osd, "", ""),
+ U_BOOT_CMD_MKENT(dev, 1, 1, do_osd_num, "", ""),
+ U_BOOT_CMD_MKENT(write, 4, 1, do_osd_write, "", ""),
+ U_BOOT_CMD_MKENT(print, 4, 1, do_osd_print, "", ""),
+ U_BOOT_CMD_MKENT(size, 2, 1, do_osd_size, "", ""),
+};
+
+static int do_osd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct cmd_tbl *c;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* Strip off leading 'osd' command argument */
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], &cmd_osd_sub[0], ARRAY_SIZE(cmd_osd_sub));
+
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+ else
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_LONGHELP(osd,
+ "show - show OSD info\n"
+ "osd dev [dev] - show or set current OSD\n"
+ "write [pos_x] [pos_y] [buffer] [count] - write 8-bit hex encoded buffer to osd memory at a given position\n"
+ "print [pos_x] [pos_y] [color] [text] - write ASCII buffer (given by text data and driver-specific color information) to osd memory\n"
+ "size [size_x] [size_y] - set OSD XY size in characters\n");
+
+U_BOOT_CMD(
+ osd, 6, 1, do_osd,
+ "OSD sub-system",
+ osd_help_text
+);
diff --git a/cmd/panic.c b/cmd/panic.c
new file mode 100644
index 00000000000..7c0affa5eb5
--- /dev/null
+++ b/cmd/panic.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020 Theobroma Systems Design und Consulting GmbH
+ */
+
+#include <vsprintf.h>
+#include <command.h>
+
+static int do_panic(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ char *text = (argc < 2) ? "" : argv[1];
+
+ panic(text);
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+ panic, 2, 1, do_panic,
+ "Panic with optional message",
+ "[message]"
+);
diff --git a/cmd/part.c b/cmd/part.c
new file mode 100644
index 00000000000..db7bc5819c0
--- /dev/null
+++ b/cmd/part.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * made from cmd_ext2, which was:
+ *
+ * (C) Copyright 2004
+ * esd gmbh <www.esd-electronics.com>
+ * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
+ *
+ * made from cmd_reiserfs by
+ *
+ * (C) Copyright 2003 - 2004
+ * Sysgo Real-Time Solutions, AG <www.elinos.com>
+ * Pavel Bartusek <pba@sysgo.com>
+ */
+
+#include <config.h>
+#include <command.h>
+#include <env.h>
+#include <part.h>
+#include <stdio.h>
+#include <vsprintf.h>
+
+enum cmd_part_info {
+ CMD_PART_INFO_START = 0,
+ CMD_PART_INFO_SIZE,
+ CMD_PART_INFO_NUMBER
+};
+
+static int do_part_uuid(int argc, char *const argv[])
+{
+ int part;
+ struct blk_desc *dev_desc;
+ struct disk_partition info;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ if (argc > 3)
+ return CMD_RET_USAGE;
+
+ part = blk_get_device_part_str(argv[0], argv[1], &dev_desc, &info, 0);
+ if (part < 0)
+ return 1;
+
+ if (argc > 2)
+ env_set(argv[2], info.uuid);
+ else
+ printf("%s\n", info.uuid);
+
+ return 0;
+}
+
+static int do_part_list(int argc, char *const argv[])
+{
+ int ret;
+ struct blk_desc *desc;
+ char *var = NULL;
+ bool bootable = false;
+ int i;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (argc > 2) {
+ for (i = 2; i < argc ; i++) {
+ if (argv[i][0] == '-') {
+ if (!strcmp(argv[i], "-bootable")) {
+ bootable = true;
+ } else {
+ printf("Unknown option %s\n", argv[i]);
+ return CMD_RET_USAGE;
+ }
+ } else {
+ var = argv[i];
+ break;
+ }
+ }
+
+ /* Loops should have been exited at the last argument, which
+ * as it contained the variable */
+ if (argc != i + 1)
+ return CMD_RET_USAGE;
+ }
+
+ ret = blk_get_device_by_str(argv[0], argv[1], &desc);
+ if (ret < 0)
+ return 1;
+
+ if (var != NULL) {
+ int p;
+ char str[3 * MAX_SEARCH_PARTITIONS] = { '\0', };
+ struct disk_partition info;
+
+ for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
+ char t[5];
+ int r = part_get_info(desc, p, &info);
+
+ if (r != 0)
+ continue;
+
+ if (bootable && !info.bootable)
+ continue;
+
+ sprintf(t, "%s%x", str[0] ? " " : "", p);
+ strcat(str, t);
+ }
+ env_set(var, str);
+ return 0;
+ }
+
+ part_print(desc);
+
+ return 0;
+}
+
+static int do_part_info(int argc, char *const argv[], enum cmd_part_info param)
+{
+ struct blk_desc *desc;
+ struct disk_partition info;
+ char buf[512] = { 0 };
+ char *endp;
+ int part;
+ int err;
+ int ret;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+ if (argc > 4)
+ return CMD_RET_USAGE;
+
+ ret = blk_get_device_by_str(argv[0], argv[1], &desc);
+ if (ret < 0)
+ return 1;
+
+ part = simple_strtoul(argv[2], &endp, 0);
+ if (*endp == '\0') {
+ err = part_get_info(desc, part, &info);
+ if (err)
+ return 1;
+ } else {
+ part = part_get_info_by_name(desc, argv[2], &info);
+ if (part < 0)
+ return 1;
+ }
+
+ switch (param) {
+ case CMD_PART_INFO_START:
+ snprintf(buf, sizeof(buf), LBAF, info.start);
+ break;
+ case CMD_PART_INFO_SIZE:
+ snprintf(buf, sizeof(buf), LBAF, info.size);
+ break;
+ case CMD_PART_INFO_NUMBER:
+ snprintf(buf, sizeof(buf), "0x%x", part);
+ break;
+ default:
+ printf("** Unknown cmd_part_info value: %d\n", param);
+ return 1;
+ }
+
+ if (argc > 3)
+ env_set(argv[3], buf);
+ else
+ printf("%s\n", buf);
+
+ return 0;
+}
+
+static int do_part_start(int argc, char *const argv[])
+{
+ return do_part_info(argc, argv, CMD_PART_INFO_START);
+}
+
+static int do_part_size(int argc, char *const argv[])
+{
+ return do_part_info(argc, argv, CMD_PART_INFO_SIZE);
+}
+
+static int do_part_number(int argc, char *const argv[])
+{
+ return do_part_info(argc, argv, CMD_PART_INFO_NUMBER);
+}
+
+static int do_part_set(int argc, char *const argv[])
+{
+ const char *devname, *partstr, *typestr;
+ struct blk_desc *desc;
+ int dev;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ /* Look up the device */
+ devname = argv[0];
+ partstr = argv[1];
+ typestr = argv[2];
+ dev = blk_get_device_by_str(devname, partstr, &desc);
+ if (dev < 0) {
+ printf("** Bad device specification %s %s **\n", devname,
+ partstr);
+ return CMD_RET_FAILURE;
+ }
+
+ desc->part_type = part_get_type_by_name(typestr);
+ if (!desc->part_type) {
+ printf("Unknown partition type '%s'\n", typestr);
+ return CMD_RET_FAILURE;
+ }
+ part_print(desc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PARTITION_TYPE_GUID
+static int do_part_type(int argc, char *const argv[])
+{
+ int part;
+ struct blk_desc *dev_desc;
+ struct disk_partition info;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ if (argc > 3)
+ return CMD_RET_USAGE;
+
+ part = blk_get_device_part_str(argv[0], argv[1], &dev_desc, &info, 0);
+ if (part < 0)
+ return 1;
+
+ if (argc > 2)
+ env_set(argv[2], info.type_guid);
+ else
+ printf("%s\n", info.type_guid);
+
+ return 0;
+}
+#endif
+
+static int do_part_types(int argc, char * const argv[])
+{
+ struct part_driver *drv = ll_entry_start(struct part_driver,
+ part_driver);
+ const int n_ents = ll_entry_count(struct part_driver, part_driver);
+ struct part_driver *entry;
+ int i = 0;
+
+ puts("Supported partition tables");
+
+ for (entry = drv; entry != drv + n_ents; entry++) {
+ printf("%c %s", i ? ',' : ':', entry->name);
+ i++;
+ }
+ if (!i)
+ puts(": <none>");
+ puts("\n");
+ return CMD_RET_SUCCESS;
+}
+
+static int do_part(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (!strcmp(argv[1], "uuid"))
+ return do_part_uuid(argc - 2, argv + 2);
+ else if (!strcmp(argv[1], "list"))
+ return do_part_list(argc - 2, argv + 2);
+ else if (!strcmp(argv[1], "start"))
+ return do_part_start(argc - 2, argv + 2);
+ else if (!strcmp(argv[1], "size"))
+ return do_part_size(argc - 2, argv + 2);
+ else if (!strcmp(argv[1], "number"))
+ return do_part_number(argc - 2, argv + 2);
+ else if (!strcmp(argv[1], "types"))
+ return do_part_types(argc - 2, argv + 2);
+ else if (!strcmp(argv[1], "set"))
+ return do_part_set(argc - 2, argv + 2);
+#ifdef CONFIG_PARTITION_TYPE_GUID
+ else if (!strcmp(argv[1], "type"))
+ return do_part_type(argc - 2, argv + 2);
+#endif
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+ part, CONFIG_SYS_MAXARGS, 1, do_part,
+ "disk partition related commands",
+ "uuid <interface> <dev>:<part>\n"
+ " - print partition UUID\n"
+ "part uuid <interface> <dev>:<part> <varname>\n"
+ " - set environment variable to partition UUID\n"
+ "part list <interface> <dev>\n"
+ " - print a device's partition table\n"
+ "part list <interface> <dev> [flags] <varname>\n"
+ " - set environment variable to the list of partitions\n"
+ " flags can be -bootable (list only bootable partitions)\n"
+ "part start <interface> <dev> <part> <varname>\n"
+ " - set environment variable to the start of the partition (in blocks)\n"
+ " part can be either partition number or partition name\n"
+ "part size <interface> <dev> <part> <varname>\n"
+ " - set environment variable to the size of the partition (in blocks)\n"
+ " part can be either partition number or partition name\n"
+ "part number <interface> <dev> <part> <varname>\n"
+ " - set environment variable to the partition number using the partition name\n"
+ " part must be specified as partition name\n"
+#ifdef CONFIG_PARTITION_TYPE_GUID
+ "part type <interface> <dev>:<part>\n"
+ " - print partition type\n"
+ "part type <interface> <dev>:<part> <varname>\n"
+ " - set environment variable to partition type\n"
+#endif
+ "part set <interface> <dev> type\n"
+ " - set partition type for a device\n"
+ "part types\n"
+ " - list supported partition table types"
+);
diff --git a/cmd/pause.c b/cmd/pause.c
new file mode 100644
index 00000000000..c97833c0d70
--- /dev/null
+++ b/cmd/pause.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2021
+ * Samuel Dionne-Riel <samuel@dionne-riel.com>
+ */
+
+#include <command.h>
+#include <stdio.h>
+
+static int do_pause(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char *message = "Press any key to continue...";
+
+ if (argc == 2)
+ message = argv[1];
+
+ /* No newline, so it sticks to the bottom of the screen */
+ printf("%s", message);
+
+ /* Wait on "any" key... */
+ (void) getchar();
+
+ /* Since there was no newline, we need it now */
+ printf("\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(pause, 2, 1, do_pause,
+ "delay until user input",
+ "[prompt] - Wait until users presses any key. [prompt] can be used to customize the message.\n"
+);
diff --git a/cmd/pcap.c b/cmd/pcap.c
new file mode 100644
index 00000000000..8d610966c13
--- /dev/null
+++ b/cmd/pcap.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2019
+ * Ramon Fried <rfried.dev@gmail.com>
+ */
+
+#include <command.h>
+#include <vsprintf.h>
+#include <net.h>
+#include <net/pcap.h>
+
+static int do_pcap_init(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ phys_addr_t addr;
+ unsigned int size;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ addr = hextoul(argv[1], NULL);
+ size = dectoul(argv[2], NULL);
+
+ return pcap_init(addr, size) ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+static int do_pcap_start(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return pcap_start_stop(true) ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+static int do_pcap_stop(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return pcap_start_stop(false) ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+static int do_pcap_status(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return pcap_print_status() ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+static int do_pcap_clear(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return pcap_clear() ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+U_BOOT_LONGHELP(pcap,
+ "- network packet capture\n\n"
+ "pcap\n"
+ "pcap init\t\t\t<addr> <max_size>\n"
+ "pcap start\t\t\tstart capture\n"
+ "pcap stop\t\t\tstop capture\n"
+ "pcap status\t\t\tprint status\n"
+ "pcap clear\t\t\tclear capture buffer\n"
+ "\n"
+ "With:\n"
+ "\t<addr>: user address to which pcap will be stored (hexedcimal)\n"
+ "\t<max_size>: Maximum size of pcap file (decimal)\n"
+ "\n");
+
+U_BOOT_CMD_WITH_SUBCMDS(pcap, "pcap", pcap_help_text,
+ U_BOOT_SUBCMD_MKENT(init, 3, 0, do_pcap_init),
+ U_BOOT_SUBCMD_MKENT(start, 1, 0, do_pcap_start),
+ U_BOOT_SUBCMD_MKENT(stop, 1, 0, do_pcap_stop),
+ U_BOOT_SUBCMD_MKENT(status, 1, 0, do_pcap_status),
+ U_BOOT_SUBCMD_MKENT(clear, 1, 0, do_pcap_clear),
+);
diff --git a/cmd/pci.c b/cmd/pci.c
new file mode 100644
index 00000000000..3c0aed50cae
--- /dev/null
+++ b/cmd/pci.c
@@ -0,0 +1,645 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Andreas Heppel <aheppel@sysgo.de>
+ *
+ * (C) Copyright 2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ * Wolfgang Grandegger, DENX Software Engineering, wg@denx.de.
+ */
+
+/*
+ * PCI routines
+ */
+
+#include <bootretry.h>
+#include <cli.h>
+#include <command.h>
+#include <console.h>
+#include <dm.h>
+#include <init.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+#include <pci.h>
+
+struct pci_reg_info {
+ const char *name;
+ enum pci_size_t size;
+ u8 offset;
+};
+
+static int pci_byte_size(enum pci_size_t size)
+{
+ switch (size) {
+ case PCI_SIZE_8:
+ return 1;
+ case PCI_SIZE_16:
+ return 2;
+ case PCI_SIZE_32:
+ default:
+ return 4;
+ }
+}
+
+static int pci_field_width(enum pci_size_t size)
+{
+ return pci_byte_size(size) * 2;
+}
+
+static void pci_show_regs(struct udevice *dev, struct pci_reg_info *regs)
+{
+ for (; regs->name; regs++) {
+ unsigned long val;
+
+ dm_pci_read_config(dev, regs->offset, &val, regs->size);
+ printf(" %s =%*s%#.*lx\n", regs->name,
+ (int)(28 - strlen(regs->name)), "",
+ pci_field_width(regs->size), val);
+ }
+}
+
+static int pci_bar_show(struct udevice *dev)
+{
+ u8 header_type;
+ int bar_cnt, bar_id, mem_type;
+ bool is_64, is_io;
+ u32 base_low, base_high;
+ u32 size_low, size_high;
+ u64 base, size;
+ u32 reg_addr;
+ int prefetchable;
+
+ dm_pci_read_config8(dev, PCI_HEADER_TYPE, &header_type);
+ header_type &= 0x7f;
+
+ if (header_type == PCI_HEADER_TYPE_CARDBUS) {
+ printf("CardBus doesn't support BARs\n");
+ return -ENOSYS;
+ } else if (header_type != PCI_HEADER_TYPE_NORMAL &&
+ header_type != PCI_HEADER_TYPE_BRIDGE) {
+ printf("unknown header type\n");
+ return -ENOSYS;
+ }
+
+ bar_cnt = (header_type == PCI_HEADER_TYPE_NORMAL) ? 6 : 2;
+
+ printf("ID Base Size Width Type\n");
+ printf("----------------------------------------------------------\n");
+
+ bar_id = 0;
+ reg_addr = PCI_BASE_ADDRESS_0;
+ while (bar_cnt) {
+ dm_pci_read_config32(dev, reg_addr, &base_low);
+ dm_pci_write_config32(dev, reg_addr, 0xffffffff);
+ dm_pci_read_config32(dev, reg_addr, &size_low);
+ dm_pci_write_config32(dev, reg_addr, base_low);
+ reg_addr += 4;
+
+ base = base_low & ~0xf;
+ size = size_low & ~0xf;
+ base_high = 0x0;
+ size_high = 0xffffffff;
+ is_64 = 0;
+ prefetchable = base_low & PCI_BASE_ADDRESS_MEM_PREFETCH;
+ is_io = base_low & PCI_BASE_ADDRESS_SPACE_IO;
+ mem_type = base_low & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
+
+ if (mem_type == PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ dm_pci_read_config32(dev, reg_addr, &base_high);
+ dm_pci_write_config32(dev, reg_addr, 0xffffffff);
+ dm_pci_read_config32(dev, reg_addr, &size_high);
+ dm_pci_write_config32(dev, reg_addr, base_high);
+ bar_cnt--;
+ reg_addr += 4;
+ is_64 = 1;
+ }
+
+ base = base | ((u64)base_high << 32);
+ size = size | ((u64)size_high << 32);
+
+ if ((!is_64 && size_low) || (is_64 && size)) {
+ size = ~size + 1;
+ printf(" %d %#018llx %#018llx %d %s %s\n",
+ bar_id, (unsigned long long)base,
+ (unsigned long long)size, is_64 ? 64 : 32,
+ is_io ? "I/O" : "MEM",
+ prefetchable ? "Prefetchable" : "");
+ }
+
+ bar_id++;
+ bar_cnt--;
+ }
+
+ return 0;
+}
+
+static struct pci_reg_info regs_start[] = {
+ { "vendor ID", PCI_SIZE_16, PCI_VENDOR_ID },
+ { "device ID", PCI_SIZE_16, PCI_DEVICE_ID },
+ { "command register ID", PCI_SIZE_16, PCI_COMMAND },
+ { "status register", PCI_SIZE_16, PCI_STATUS },
+ { "revision ID", PCI_SIZE_8, PCI_REVISION_ID },
+ {},
+};
+
+static struct pci_reg_info regs_rest[] = {
+ { "sub class code", PCI_SIZE_8, PCI_CLASS_SUB_CODE },
+ { "programming interface", PCI_SIZE_8, PCI_CLASS_PROG },
+ { "cache line", PCI_SIZE_8, PCI_CACHE_LINE_SIZE },
+ { "latency time", PCI_SIZE_8, PCI_LATENCY_TIMER },
+ { "header type", PCI_SIZE_8, PCI_HEADER_TYPE },
+ { "BIST", PCI_SIZE_8, PCI_BIST },
+ { "base address 0", PCI_SIZE_32, PCI_BASE_ADDRESS_0 },
+ {},
+};
+
+static struct pci_reg_info regs_normal[] = {
+ { "base address 1", PCI_SIZE_32, PCI_BASE_ADDRESS_1 },
+ { "base address 2", PCI_SIZE_32, PCI_BASE_ADDRESS_2 },
+ { "base address 3", PCI_SIZE_32, PCI_BASE_ADDRESS_3 },
+ { "base address 4", PCI_SIZE_32, PCI_BASE_ADDRESS_4 },
+ { "base address 5", PCI_SIZE_32, PCI_BASE_ADDRESS_5 },
+ { "cardBus CIS pointer", PCI_SIZE_32, PCI_CARDBUS_CIS },
+ { "sub system vendor ID", PCI_SIZE_16, PCI_SUBSYSTEM_VENDOR_ID },
+ { "sub system ID", PCI_SIZE_16, PCI_SUBSYSTEM_ID },
+ { "expansion ROM base address", PCI_SIZE_32, PCI_ROM_ADDRESS },
+ { "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE },
+ { "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN },
+ { "min Grant", PCI_SIZE_8, PCI_MIN_GNT },
+ { "max Latency", PCI_SIZE_8, PCI_MAX_LAT },
+ {},
+};
+
+static struct pci_reg_info regs_bridge[] = {
+ { "base address 1", PCI_SIZE_32, PCI_BASE_ADDRESS_1 },
+ { "primary bus number", PCI_SIZE_8, PCI_PRIMARY_BUS },
+ { "secondary bus number", PCI_SIZE_8, PCI_SECONDARY_BUS },
+ { "subordinate bus number", PCI_SIZE_8, PCI_SUBORDINATE_BUS },
+ { "secondary latency timer", PCI_SIZE_8, PCI_SEC_LATENCY_TIMER },
+ { "IO base", PCI_SIZE_8, PCI_IO_BASE },
+ { "IO limit", PCI_SIZE_8, PCI_IO_LIMIT },
+ { "secondary status", PCI_SIZE_16, PCI_SEC_STATUS },
+ { "memory base", PCI_SIZE_16, PCI_MEMORY_BASE },
+ { "memory limit", PCI_SIZE_16, PCI_MEMORY_LIMIT },
+ { "prefetch memory base", PCI_SIZE_16, PCI_PREF_MEMORY_BASE },
+ { "prefetch memory limit", PCI_SIZE_16, PCI_PREF_MEMORY_LIMIT },
+ { "prefetch memory base upper", PCI_SIZE_32, PCI_PREF_BASE_UPPER32 },
+ { "prefetch memory limit upper", PCI_SIZE_32, PCI_PREF_LIMIT_UPPER32 },
+ { "IO base upper 16 bits", PCI_SIZE_16, PCI_IO_BASE_UPPER16 },
+ { "IO limit upper 16 bits", PCI_SIZE_16, PCI_IO_LIMIT_UPPER16 },
+ { "expansion ROM base address", PCI_SIZE_32, PCI_ROM_ADDRESS1 },
+ { "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE },
+ { "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN },
+ { "bridge control", PCI_SIZE_16, PCI_BRIDGE_CONTROL },
+ {},
+};
+
+static struct pci_reg_info regs_cardbus[] = {
+ { "capabilities", PCI_SIZE_8, PCI_CB_CAPABILITY_LIST },
+ { "secondary status", PCI_SIZE_16, PCI_CB_SEC_STATUS },
+ { "primary bus number", PCI_SIZE_8, PCI_CB_PRIMARY_BUS },
+ { "CardBus number", PCI_SIZE_8, PCI_CB_CARD_BUS },
+ { "subordinate bus number", PCI_SIZE_8, PCI_CB_SUBORDINATE_BUS },
+ { "CardBus latency timer", PCI_SIZE_8, PCI_CB_LATENCY_TIMER },
+ { "CardBus memory base 0", PCI_SIZE_32, PCI_CB_MEMORY_BASE_0 },
+ { "CardBus memory limit 0", PCI_SIZE_32, PCI_CB_MEMORY_LIMIT_0 },
+ { "CardBus memory base 1", PCI_SIZE_32, PCI_CB_MEMORY_BASE_1 },
+ { "CardBus memory limit 1", PCI_SIZE_32, PCI_CB_MEMORY_LIMIT_1 },
+ { "CardBus IO base 0", PCI_SIZE_16, PCI_CB_IO_BASE_0 },
+ { "CardBus IO base high 0", PCI_SIZE_16, PCI_CB_IO_BASE_0_HI },
+ { "CardBus IO limit 0", PCI_SIZE_16, PCI_CB_IO_LIMIT_0 },
+ { "CardBus IO limit high 0", PCI_SIZE_16, PCI_CB_IO_LIMIT_0_HI },
+ { "CardBus IO base 1", PCI_SIZE_16, PCI_CB_IO_BASE_1 },
+ { "CardBus IO base high 1", PCI_SIZE_16, PCI_CB_IO_BASE_1_HI },
+ { "CardBus IO limit 1", PCI_SIZE_16, PCI_CB_IO_LIMIT_1 },
+ { "CardBus IO limit high 1", PCI_SIZE_16, PCI_CB_IO_LIMIT_1_HI },
+ { "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE },
+ { "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN },
+ { "bridge control", PCI_SIZE_16, PCI_CB_BRIDGE_CONTROL },
+ { "subvendor ID", PCI_SIZE_16, PCI_CB_SUBSYSTEM_VENDOR_ID },
+ { "subdevice ID", PCI_SIZE_16, PCI_CB_SUBSYSTEM_ID },
+ { "PC Card 16bit base address", PCI_SIZE_32, PCI_CB_LEGACY_MODE_BASE },
+ {},
+};
+
+/**
+ * pci_header_show() - Show the header of the specified PCI device.
+ *
+ * @dev: Bus+Device+Function number
+ */
+static void pci_header_show(struct udevice *dev)
+{
+ unsigned long class, header_type;
+
+ dm_pci_read_config(dev, PCI_CLASS_CODE, &class, PCI_SIZE_8);
+ dm_pci_read_config(dev, PCI_HEADER_TYPE, &header_type, PCI_SIZE_8);
+ pci_show_regs(dev, regs_start);
+ printf(" class code = 0x%.2x (%s)\n", (int)class,
+ pci_class_str(class));
+ pci_show_regs(dev, regs_rest);
+
+ switch (header_type & 0x7f) {
+ case PCI_HEADER_TYPE_NORMAL: /* "normal" PCI device */
+ pci_show_regs(dev, regs_normal);
+ break;
+ case PCI_HEADER_TYPE_BRIDGE: /* PCI-to-PCI bridge */
+ pci_show_regs(dev, regs_bridge);
+ break;
+ case PCI_HEADER_TYPE_CARDBUS: /* PCI-to-CardBus bridge */
+ pci_show_regs(dev, regs_cardbus);
+ break;
+
+ default:
+ printf("unknown header\n");
+ break;
+ }
+}
+
+static void pciinfo_header(bool short_listing)
+{
+ if (short_listing) {
+ printf("BusDevFun VendorId DeviceId Device Class Sub-Class\n");
+ printf("_____________________________________________________________\n");
+ }
+}
+
+/**
+ * pci_header_show_brief() - Show the short-form PCI device header
+ *
+ * Reads and prints the header of the specified PCI device in short form.
+ *
+ * @dev: PCI device to show
+ */
+static void pci_header_show_brief(struct udevice *dev)
+{
+ ulong vendor, device;
+ ulong class, subclass;
+
+ dm_pci_read_config(dev, PCI_VENDOR_ID, &vendor, PCI_SIZE_16);
+ dm_pci_read_config(dev, PCI_DEVICE_ID, &device, PCI_SIZE_16);
+ dm_pci_read_config(dev, PCI_CLASS_CODE, &class, PCI_SIZE_8);
+ dm_pci_read_config(dev, PCI_CLASS_SUB_CODE, &subclass, PCI_SIZE_8);
+
+ printf("0x%.4lx 0x%.4lx %-23s 0x%.2lx\n",
+ vendor, device,
+ pci_class_str(class), subclass);
+}
+
+static void pciinfo(struct udevice *bus, bool short_listing, bool multi)
+{
+ struct udevice *dev;
+
+ if (!multi)
+ printf("Scanning PCI devices on bus %d\n", dev_seq(bus));
+
+ if (!multi || dev_seq(bus) == 0)
+ pciinfo_header(short_listing);
+
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ struct pci_child_plat *pplat;
+
+ pplat = dev_get_parent_plat(dev);
+ if (short_listing) {
+ printf("%02x.%02x.%02x ", dev_seq(bus),
+ PCI_DEV(pplat->devfn), PCI_FUNC(pplat->devfn));
+ pci_header_show_brief(dev);
+ } else {
+ printf("\nFound PCI device %02x.%02x.%02x:\n",
+ dev_seq(bus),
+ PCI_DEV(pplat->devfn), PCI_FUNC(pplat->devfn));
+ pci_header_show(dev);
+ }
+ }
+}
+
+/**
+ * get_pci_dev() - Convert the "bus.device.function" identifier into a number
+ *
+ * @name: Device string in the form "bus.device.function" where each is in hex
+ * Return: encoded pci_dev_t or -1 if the string was invalid
+ */
+static pci_dev_t get_pci_dev(char *name)
+{
+ char cnum[12];
+ int len, i, iold, n;
+ int bdfs[3] = {0,0,0};
+
+ len = strlen(name);
+ if (len > 8)
+ return -1;
+ for (i = 0, iold = 0, n = 0; i < len; i++) {
+ if (name[i] == '.') {
+ memcpy(cnum, &name[iold], i - iold);
+ cnum[i - iold] = '\0';
+ bdfs[n++] = hextoul(cnum, NULL);
+ iold = i + 1;
+ }
+ }
+ strcpy(cnum, &name[iold]);
+ if (n == 0)
+ n = 1;
+ bdfs[n] = hextoul(cnum, NULL);
+
+ return PCI_BDF(bdfs[0], bdfs[1], bdfs[2]);
+}
+
+static int pci_cfg_display(struct udevice *dev, ulong addr,
+ enum pci_size_t size, ulong length)
+{
+#define DISP_LINE_LEN 16
+ ulong i, nbytes, linebytes;
+ int byte_size;
+ int rc = 0;
+
+ byte_size = pci_byte_size(size);
+ if (length == 0)
+ length = 0x40 / byte_size; /* Standard PCI config space */
+
+ if (addr >= 4096)
+ return 1;
+
+ /* Print the lines.
+ * once, and all accesses are with the specified bus width.
+ */
+ nbytes = length * byte_size;
+ do {
+ printf("%08lx:", addr);
+ linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;
+ for (i = 0; i < linebytes; i += byte_size) {
+ unsigned long val;
+
+ dm_pci_read_config(dev, addr, &val, size);
+ printf(" %0*lx", pci_field_width(size), val);
+ addr += byte_size;
+ }
+ printf("\n");
+ nbytes -= linebytes;
+ if (ctrlc()) {
+ rc = 1;
+ break;
+ }
+ } while (nbytes > 0 && addr < 4096);
+
+ if (rc == 0 && nbytes > 0)
+ return 1;
+
+ return (rc);
+}
+
+static int pci_cfg_modify(struct udevice *dev, ulong addr, ulong size,
+ ulong value, int incrflag)
+{
+ ulong i;
+ int nbytes;
+ ulong val;
+
+ if (addr >= 4096)
+ return 1;
+
+ /* Print the address, followed by value. Then accept input for
+ * the next value. A non-converted value exits.
+ */
+ do {
+ printf("%08lx:", addr);
+ dm_pci_read_config(dev, addr, &val, size);
+ printf(" %0*lx", pci_field_width(size), val);
+
+ nbytes = cli_readline(" ? ");
+ if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) {
+ /* <CR> pressed as only input, don't modify current
+ * location and move to next. "-" pressed will go back.
+ */
+ if (incrflag)
+ addr += nbytes ? -size : size;
+ nbytes = 1;
+ /* good enough to not time out */
+ bootretry_reset_cmd_timeout();
+ }
+#ifdef CONFIG_BOOT_RETRY_TIME
+ else if (nbytes == -2) {
+ break; /* timed out, exit the command */
+ }
+#endif
+ else {
+ char *endp;
+ i = hextoul(console_buffer, &endp);
+ nbytes = endp - console_buffer;
+ if (nbytes) {
+ /* good enough to not time out
+ */
+ bootretry_reset_cmd_timeout();
+ dm_pci_write_config(dev, addr, i, size);
+ if (incrflag)
+ addr += size;
+ }
+ }
+ } while (nbytes && addr < 4096);
+
+ if (nbytes)
+ return 1;
+
+ return 0;
+}
+
+static const struct pci_flag_info {
+ uint flag;
+ const char *name;
+} pci_flag_info[] = {
+ { PCI_REGION_IO, "io" },
+ { PCI_REGION_PREFETCH, "prefetch" },
+ { PCI_REGION_SYS_MEMORY, "sysmem" },
+ { PCI_REGION_RO, "readonly" },
+};
+
+static void pci_show_regions(struct udevice *bus)
+{
+ struct pci_controller *hose = dev_get_uclass_priv(pci_get_controller(bus));
+ const struct pci_region *reg;
+ int i, j;
+
+ if (!hose) {
+ printf("Bus '%s' is not a PCI controller\n", bus->name);
+ return;
+ }
+
+ printf("Buses %02x-%02x\n", hose->first_busno, hose->last_busno);
+ printf("# %-18s %-18s %-18s %s\n", "Bus start", "Phys start", "Size",
+ "Flags");
+ for (i = 0, reg = hose->regions; i < hose->region_count; i++, reg++) {
+ printf("%d %#018llx %#018llx %#018llx ", i,
+ (unsigned long long)reg->bus_start,
+ (unsigned long long)reg->phys_start,
+ (unsigned long long)reg->size);
+ if (!(reg->flags & PCI_REGION_TYPE))
+ printf("mem ");
+ for (j = 0; j < ARRAY_SIZE(pci_flag_info); j++) {
+ if (reg->flags & pci_flag_info[j].flag)
+ printf("%s ", pci_flag_info[j].name);
+ }
+ printf("\n");
+ }
+}
+
+/* PCI Configuration Space access commands
+ *
+ * Syntax:
+ * pci display[.b, .w, .l] bus.device.function} [addr] [len]
+ * pci next[.b, .w, .l] bus.device.function [addr]
+ * pci modify[.b, .w, .l] bus.device.function [addr]
+ * pci write[.b, .w, .l] bus.device.function addr value
+ */
+static int do_pci(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ ulong addr = 0, value = 0, cmd_size = 0;
+ enum pci_size_t size = PCI_SIZE_32;
+ struct udevice *dev, *bus;
+ int busnum = -1;
+ pci_dev_t bdf = 0;
+ char cmd = 's';
+ int ret = 0;
+ char *endp;
+
+ if (argc > 1)
+ cmd = argv[1][0];
+
+ switch (cmd) {
+ case 'd': /* display */
+ case 'n': /* next */
+ case 'm': /* modify */
+ case 'w': /* write */
+ /* Check for a size specification. */
+ cmd_size = cmd_get_data_size(argv[1], 4);
+ size = (cmd_size == 4) ? PCI_SIZE_32 : cmd_size - 1;
+ if (argc > 3)
+ addr = hextoul(argv[3], NULL);
+ if (argc > 4)
+ value = hextoul(argv[4], NULL);
+ fallthrough;
+ case 'h': /* header */
+ case 'b': /* bars */
+ if (argc < 3)
+ goto usage;
+ if ((bdf = get_pci_dev(argv[2])) == -1)
+ return 1;
+ break;
+ case 'e':
+ pci_init();
+ return 0;
+ case 'r': /* no break */
+ default: /* scan bus */
+ value = 1; /* short listing */
+ if (argc > 1) {
+ if (cmd != 'r' && argv[argc-1][0] == 'l') {
+ value = 0;
+ argc--;
+ }
+ if (argc > 2 || (argc > 1 && cmd != 'r' && argv[1][0] != 's')) {
+ if (argv[argc - 1][0] != '*') {
+ busnum = hextoul(argv[argc - 1], &endp);
+ if (*endp)
+ goto usage;
+ }
+ argc--;
+ }
+ if (cmd == 'r' && argc > 2)
+ goto usage;
+ else if (cmd != 'r' && (argc > 2 || (argc == 2 && argv[1][0] != 's')))
+ goto usage;
+ }
+ if (busnum == -1) {
+ if (cmd != 'r') {
+ for (busnum = 0;
+ uclass_get_device_by_seq(UCLASS_PCI, busnum, &bus) == 0;
+ busnum++)
+ pciinfo(bus, value, true);
+ } else {
+ for (busnum = 0;
+ uclass_get_device_by_seq(UCLASS_PCI, busnum, &bus) == 0;
+ busnum++) {
+ /* Regions are controller specific so skip non-root buses */
+ if (device_is_on_pci_bus(bus))
+ continue;
+ pci_show_regions(bus);
+ }
+ }
+ return 0;
+ }
+ ret = uclass_get_device_by_seq(UCLASS_PCI, busnum, &bus);
+ if (ret) {
+ printf("No such bus\n");
+ return CMD_RET_FAILURE;
+ }
+ if (cmd == 'r')
+ pci_show_regions(bus);
+ else
+ pciinfo(bus, value, false);
+ return 0;
+ }
+
+ ret = dm_pci_bus_find_bdf(bdf, &dev);
+ if (ret) {
+ printf("No such device\n");
+ return CMD_RET_FAILURE;
+ }
+
+ switch (argv[1][0]) {
+ case 'h': /* header */
+ pci_header_show(dev);
+ break;
+ case 'd': /* display */
+ return pci_cfg_display(dev, addr, size, value);
+ case 'n': /* next */
+ if (argc < 4)
+ goto usage;
+ ret = pci_cfg_modify(dev, addr, size, value, 0);
+ break;
+ case 'm': /* modify */
+ if (argc < 4)
+ goto usage;
+ ret = pci_cfg_modify(dev, addr, size, value, 1);
+ break;
+ case 'w': /* write */
+ if (argc < 5)
+ goto usage;
+ ret = dm_pci_write_config(dev, addr, value, size);
+ break;
+ case 'b': /* bars */
+ return pci_bar_show(dev);
+ default:
+ ret = CMD_RET_USAGE;
+ break;
+ }
+
+ return ret;
+ usage:
+ return CMD_RET_USAGE;
+}
+
+/***************************************************/
+
+U_BOOT_LONGHELP(pci,
+ "[bus|*] [long]\n"
+ " - short or long list of PCI devices on bus 'bus'\n"
+ "pci enum\n"
+ " - Enumerate PCI buses\n"
+ "pci header b.d.f\n"
+ " - show header of PCI device 'bus.device.function'\n"
+ "pci bar b.d.f\n"
+ " - show BARs base and size for device b.d.f'\n"
+ "pci regions [bus|*]\n"
+ " - show PCI regions\n"
+ "pci display[.b, .w, .l] b.d.f [address] [# of objects]\n"
+ " - display PCI configuration space (CFG)\n"
+ "pci next[.b, .w, .l] b.d.f address\n"
+ " - modify, read and keep CFG address\n"
+ "pci modify[.b, .w, .l] b.d.f address\n"
+ " - modify, auto increment CFG address\n"
+ "pci write[.b, .w, .l] b.d.f address value\n"
+ " - write to CFG address");
+
+U_BOOT_CMD(
+ pci, 5, 1, do_pci,
+ "list and access PCI Configuration Space", pci_help_text
+);
diff --git a/cmd/pci_mps.c b/cmd/pci_mps.c
new file mode 100644
index 00000000000..19e71db8cbd
--- /dev/null
+++ b/cmd/pci_mps.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2022 Microsoft Corporation <www.microsoft.com>
+ * Stephen Carlson <stcarlso@linux.microsoft.com>
+ *
+ * PCI Express Maximum Packet Size (MPS) configuration
+ */
+
+#include <bootretry.h>
+#include <cli.h>
+#include <command.h>
+#include <console.h>
+#include <dm.h>
+#include <init.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+#include <pci.h>
+
+#define PCI_MPS_SAFE 0
+#define PCI_MPS_PEER2PEER 1
+
+static int pci_mps_find_safe(struct udevice *bus, unsigned int *min_mps,
+ unsigned int *n)
+{
+ struct udevice *dev;
+ int res = 0, addr;
+ unsigned int mpss;
+ u32 regval;
+
+ if (!min_mps || !n)
+ return -EINVAL;
+
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ addr = dm_pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (addr <= 0)
+ continue;
+
+ res = dm_pci_read_config32(dev, addr + PCI_EXP_DEVCAP,
+ &regval);
+ if (res != 0)
+ return res;
+ mpss = (unsigned int)(regval & PCI_EXP_DEVCAP_PAYLOAD);
+ *n += 1;
+ if (mpss < *min_mps)
+ *min_mps = mpss;
+ }
+
+ return res;
+}
+
+static int pci_mps_set_bus(struct udevice *bus, unsigned int target)
+{
+ struct udevice *dev;
+ u32 mpss, target_mps = (u32)(target << 5);
+ u16 mps;
+ int res = 0, addr;
+
+ for (device_find_first_child(bus, &dev);
+ dev && res == 0;
+ device_find_next_child(&dev)) {
+ addr = dm_pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (addr <= 0)
+ continue;
+
+ res = dm_pci_read_config32(dev, addr + PCI_EXP_DEVCAP,
+ &mpss);
+ if (res != 0)
+ return res;
+
+ /* Do not set device above its maximum MPSS */
+ mpss = (mpss & PCI_EXP_DEVCAP_PAYLOAD) << 5;
+ if (target_mps < mpss)
+ mps = (u16)target_mps;
+ else
+ mps = (u16)mpss;
+ res = dm_pci_clrset_config16(dev, addr + PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_PAYLOAD, mps);
+ }
+
+ return res;
+}
+
+/*
+ * Sets the MPS of each PCI Express device to the specified policy.
+ */
+static int pci_mps_set(int policy)
+{
+ struct udevice *bus;
+ int i, res = 0;
+ /* 0 = 128B, min value for hotplug */
+ unsigned int mps = 0;
+
+ if (policy == PCI_MPS_SAFE) {
+ unsigned int min_mps = PCI_EXP_DEVCAP_PAYLOAD_4096B, n = 0;
+
+ /* Find maximum MPS supported by all devices */
+ for (i = 0;
+ uclass_get_device_by_seq(UCLASS_PCI, i, &bus) == 0 &&
+ res == 0;
+ i++)
+ res = pci_mps_find_safe(bus, &min_mps, &n);
+
+ /* If no devices were found, do not reconfigure */
+ if (n == 0)
+ return res;
+ mps = min_mps;
+ }
+
+ /* This message is checked by the sandbox test */
+ printf("Setting MPS of all devices to %uB\n", 128U << mps);
+ for (i = 0;
+ uclass_get_device_by_seq(UCLASS_PCI, i, &bus) == 0 && res == 0;
+ i++)
+ res = pci_mps_set_bus(bus, mps);
+
+ return res;
+}
+
+/*
+ * PCI MPS tuning commands
+ *
+ * Syntax:
+ * pci_mps safe
+ * pci_mps peer2peer
+ */
+static int do_pci_mps(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char cmd = 'u';
+ int ret = 0;
+
+ if (argc > 1)
+ cmd = argv[1][0];
+
+ switch (cmd) {
+ case 's': /* safe */
+ ret = pci_mps_set(PCI_MPS_SAFE);
+ break;
+ case 'p': /* peer2peer/hotplug */
+ ret = pci_mps_set(PCI_MPS_PEER2PEER);
+ break;
+ default: /* usage, help */
+ goto usage;
+ }
+
+ return ret;
+usage:
+ return CMD_RET_USAGE;
+}
+
+/***************************************************/
+
+U_BOOT_LONGHELP(pci_mps,
+ "safe\n"
+ " - Set PCI Express MPS of all devices to safe values\n"
+ "pci_mps peer2peer\n"
+ " - Set PCI Express MPS of all devices to support hotplug and peer-to-peer DMA\n");
+
+U_BOOT_CMD(pci_mps, 2, 0, do_pci_mps,
+ "configure PCI Express MPS", pci_mps_help_text);
diff --git a/cmd/pinmux.c b/cmd/pinmux.c
new file mode 100644
index 00000000000..01f3e4af6ce
--- /dev/null
+++ b/cmd/pinmux.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <errno.h>
+#include <dm/pinctrl.h>
+#include <dm/uclass-internal.h>
+
+#define LIMIT_DEVNAME 30
+
+static struct udevice *currdev;
+
+static int do_dev(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *name;
+ int ret;
+
+ switch (argc) {
+ case 2:
+ name = argv[1];
+ ret = uclass_get_device_by_name(UCLASS_PINCTRL, name, &currdev);
+ if (ret) {
+ printf("Can't get the pin-controller: %s!\n", name);
+ return CMD_RET_FAILURE;
+ }
+ /* fall through */
+ case 1:
+ if (!currdev) {
+ printf("Pin-controller device is not set!\n");
+ return CMD_RET_USAGE;
+ }
+
+ printf("dev: %s\n", currdev->name);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * Print the muxing information for one or all pins of one pinctrl device
+ *
+ * @param dev pinctrl device
+ * @param name NULL to display all the pins
+ * or name of the pin to display
+ * Return: 0 on success, non-0 on error
+ */
+static int show_pinmux(struct udevice *dev, char *name)
+{
+ char pin_name[PINNAME_SIZE];
+ char pin_mux[PINMUX_SIZE];
+ int pins_count;
+ int i;
+ int ret;
+ bool found = false;
+
+ pins_count = pinctrl_get_pins_count(dev);
+
+ if (pins_count == -ENOSYS) {
+ printf("Ops get_pins_count not supported by %s\n", dev->name);
+ return pins_count;
+ }
+
+ for (i = 0; i < pins_count; i++) {
+ ret = pinctrl_get_pin_name(dev, i, pin_name, PINNAME_SIZE);
+ if (ret) {
+ printf("Ops get_pin_name error (%d) by %s\n", ret, dev->name);
+ return ret;
+ }
+ if (name && strcmp(name, pin_name))
+ continue;
+ found = true;
+ ret = pinctrl_get_pin_muxing(dev, i, pin_mux, PINMUX_SIZE);
+ if (ret) {
+ printf("Ops get_pin_muxing error (%d) by %s in %s\n",
+ ret, pin_name, dev->name);
+ return ret;
+ }
+
+ printf("%-*s: %-*s\n", PINNAME_SIZE, pin_name,
+ PINMUX_SIZE, pin_mux);
+ }
+
+ if (!found)
+ return -ENOENT;
+
+ return 0;
+}
+
+static int do_status(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ char *name;
+ int ret;
+
+ if (argc < 2) {
+ if (!currdev) {
+ printf("pin-controller device not selected\n");
+ return CMD_RET_FAILURE;
+ }
+ show_pinmux(currdev, NULL);
+ return CMD_RET_SUCCESS;
+ }
+
+ if (strcmp(argv[1], "-a"))
+ name = argv[1];
+ else
+ name = NULL;
+
+ uclass_foreach_dev_probe(UCLASS_PINCTRL, dev) {
+ if (!name) {
+ /* insert a separator between each pin-controller display */
+ printf("--------------------------\n");
+ printf("%s:\n", dev->name);
+ }
+ ret = show_pinmux(dev, name);
+ /* stop when the status of requested pin is displayed */
+ if (name && !ret)
+ return CMD_RET_SUCCESS;
+ }
+
+ if (name) {
+ printf("%s not found\n", name);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+
+ printf("| %-*.*s| %-*.*s| %s\n",
+ LIMIT_DEVNAME, LIMIT_DEVNAME, "Device",
+ LIMIT_DEVNAME, LIMIT_DEVNAME, "Driver",
+ "Parent");
+
+ uclass_foreach_dev_probe(UCLASS_PINCTRL, dev) {
+ printf("| %-*.*s| %-*.*s| %s\n",
+ LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
+ LIMIT_DEVNAME, LIMIT_DEVNAME, dev->driver->name,
+ dev->parent->name);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static struct cmd_tbl pinmux_subcmd[] = {
+ U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
+ U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
+ U_BOOT_CMD_MKENT(status, 2, 1, do_status, "", ""),
+};
+
+static int do_pinmux(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *cmd;
+
+ argc--;
+ argv++;
+
+ cmd = find_cmd_tbl(argv[0], pinmux_subcmd, ARRAY_SIZE(pinmux_subcmd));
+ if (!cmd || argc > cmd->maxargs)
+ return CMD_RET_USAGE;
+
+ return cmd->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(pinmux, CONFIG_SYS_MAXARGS, 1, do_pinmux,
+ "show pin-controller muxing",
+ "list - list UCLASS_PINCTRL devices\n"
+ "pinmux dev [pincontroller-name] - select pin-controller device\n"
+ "pinmux status [-a | pin-name] - print pin-controller muxing [for all | for pin-name]\n"
+);
diff --git a/cmd/pmc.c b/cmd/pmc.c
new file mode 100644
index 00000000000..1a3416fb2a9
--- /dev/null
+++ b/cmd/pmc.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel PMC command
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <power/acpi_pmc.h>
+
+static int get_pmc_dev(struct udevice **devp)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_first_device_err(UCLASS_ACPI_PMC, &dev);
+ if (ret) {
+ printf("Could not find device (err=%d)\n", ret);
+ return ret;
+ }
+ ret = pmc_init(dev);
+ if (ret) {
+ printf("Could not init device (err=%d)\n", ret);
+ return ret;
+ }
+ *devp = dev;
+
+ return 0;
+}
+
+static int do_pmc_init(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = get_pmc_dev(&dev);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+static int do_pmc_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = get_pmc_dev(&dev);
+ if (ret)
+ return CMD_RET_FAILURE;
+ pmc_dump_info(dev);
+
+ return 0;
+}
+
+static struct cmd_tbl cmd_pmc_sub[] = {
+ U_BOOT_CMD_MKENT(init, 0, 1, do_pmc_init, "", ""),
+ U_BOOT_CMD_MKENT(info, 0, 1, do_pmc_info, "", ""),
+};
+
+static int do_pmc(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ const struct cmd_tbl *cp;
+
+ if (argc < 2) /* no subcommand */
+ return cmd_usage(cmdtp);
+
+ cp = find_cmd_tbl(argv[1], &cmd_pmc_sub[0], ARRAY_SIZE(cmd_pmc_sub));
+ if (!cp)
+ return CMD_RET_USAGE;
+
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(
+ pmc, 2, 1, do_pmc, "Power-management controller info",
+ "info - read state and show info about the PMC\n"
+ "pmc init - read state from the PMC\n"
+ );
diff --git a/cmd/pmic.c b/cmd/pmic.c
new file mode 100644
index 00000000000..e5fc8f97b75
--- /dev/null
+++ b/cmd/pmic.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ */
+#include <command.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/uclass-internal.h>
+#include <power/pmic.h>
+
+#define LIMIT_DEV 32
+#define LIMIT_PARENT 20
+
+static struct udevice *currdev;
+
+static int failure(int ret)
+{
+ printf("Error: %d (%s)\n", ret, errno_str(ret));
+
+ return CMD_RET_FAILURE;
+}
+
+static int do_dev(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char *name;
+ int ret = -ENODEV;
+
+ switch (argc) {
+ case 2:
+ name = argv[1];
+ ret = pmic_get(name, &currdev);
+ if (ret) {
+ printf("Can't get PMIC: %s!\n", name);
+ return failure(ret);
+ }
+ fallthrough;
+ case 1:
+ if (!currdev) {
+ printf("PMIC device is not set!\n\n");
+ return CMD_RET_USAGE;
+ }
+
+ printf("dev: %d @ %s\n", dev_seq(currdev), currdev->name);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int ret, err = 0;
+
+ printf("| %-*.*s| %-*.*s| %s @ %s\n",
+ LIMIT_DEV, LIMIT_DEV, "Name",
+ LIMIT_PARENT, LIMIT_PARENT, "Parent name",
+ "Parent uclass", "seq");
+
+ for (ret = uclass_first_device_check(UCLASS_PMIC, &dev); dev;
+ ret = uclass_next_device_check(&dev)) {
+ if (ret)
+ err = ret;
+
+ printf("| %-*.*s| %-*.*s| %s @ %d | status: %i\n",
+ LIMIT_DEV, LIMIT_DEV, dev->name,
+ LIMIT_PARENT, LIMIT_PARENT, dev->parent->name,
+ dev_get_uclass_name(dev->parent), dev_seq(dev->parent),
+ ret);
+ }
+
+ if (err)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_dump(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct uc_pmic_priv *priv;
+ struct udevice *dev;
+ char fmt[16];
+ uint reg;
+ int ret;
+
+ if (!currdev) {
+ printf("First, set the PMIC device!\n");
+ return CMD_RET_USAGE;
+ }
+
+ dev = currdev;
+ priv = dev_get_uclass_priv(dev);
+ printf("Dump pmic: %s registers\n", dev->name);
+
+ sprintf(fmt, "%%%d.%dx ", priv->trans_len * 2,
+ priv->trans_len * 2);
+
+ for (reg = 0; reg < pmic_reg_count(dev); reg++) {
+ ret = pmic_reg_read(dev, reg);
+ if (ret < 0 && ret != -ENODATA) {
+ printf("Can't read register: %d\n", reg);
+ return failure(ret);
+ }
+
+ if (!(reg % 16))
+ printf("\n0x%02x: ", reg);
+
+ if (ret == -ENODATA) {
+ int i;
+
+ for (i = 0; i < priv->trans_len; i++)
+ puts("--");
+ puts(" ");
+ } else {
+ printf(fmt, ret);
+ }
+ }
+ printf("\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_read(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct uc_pmic_priv *priv;
+ struct udevice *dev;
+ int regs, ret;
+ char fmt[24];
+ uint reg;
+
+ if (!currdev) {
+ printf("First, set the PMIC device!\n");
+ return CMD_RET_USAGE;
+ }
+
+ dev = currdev;
+ priv = dev_get_uclass_priv(dev);
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ reg = simple_strtoul(argv[1], NULL, 0);
+ regs = pmic_reg_count(dev);
+ if (reg > regs) {
+ printf("PMIC max reg: %d\n", regs);
+ return failure(-EFAULT);
+ }
+
+ ret = pmic_reg_read(dev, reg);
+ if (ret < 0) {
+ printf("Can't read PMIC register: %d!\n", reg);
+ return failure(ret);
+ }
+
+ sprintf(fmt, "0x%%02x: 0x%%%d.%dx\n", priv->trans_len * 2,
+ priv->trans_len * 2);
+ printf(fmt, reg, ret);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_write(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ uint reg, value;
+ int regs, ret;
+
+ if (!currdev) {
+ printf("First, set the PMIC device!\n");
+ return CMD_RET_USAGE;
+ }
+
+ dev = currdev;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ reg = simple_strtoul(argv[1], NULL, 0);
+ regs = pmic_reg_count(dev);
+ if (reg > regs) {
+ printf("PMIC max reg: %d\n", regs);
+ return failure(-EFAULT);
+ }
+
+ value = simple_strtoul(argv[2], NULL, 0);
+
+ ret = pmic_reg_write(dev, reg, value);
+ if (ret) {
+ printf("Can't write PMIC register: %d!\n", reg);
+ return failure(ret);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static struct cmd_tbl subcmd[] = {
+ U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
+ U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
+ U_BOOT_CMD_MKENT(dump, 1, 1, do_dump, "", ""),
+ U_BOOT_CMD_MKENT(read, 2, 1, do_read, "", ""),
+ U_BOOT_CMD_MKENT(write, 3, 1, do_write, "", ""),
+};
+
+static int do_pmic(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *cmd;
+
+ argc--;
+ argv++;
+
+ cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
+ if (cmd == NULL || argc > cmd->maxargs)
+ return CMD_RET_USAGE;
+
+ return cmd->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(pmic, CONFIG_SYS_MAXARGS, 1, do_pmic,
+ "PMIC sub-system",
+ "list - list pmic devices\n"
+ "pmic dev [name] - show or [set] operating PMIC device\n"
+ "pmic dump - dump registers\n"
+ "pmic read <reg> - read byte of 'reg' register\n"
+ "pmic write <reg> <byte> - write 'byte' byte to 'reg' register\n"
+);
diff --git a/cmd/printf.c b/cmd/printf.c
new file mode 100644
index 00000000000..a1727ac15a2
--- /dev/null
+++ b/cmd/printf.c
@@ -0,0 +1,647 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Weidmüller Interface GmbH & Co. KG
+ * Roland Gaudig <roland.gaudig@weidmueller.com>
+ *
+ * Copyright 1999 Dave Cinege
+ * Portions copyright (C) 1990-1996 Free Software Foundation, Inc.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+/*
+ * This file provides a shell printf like format string expansion as required
+ * for the setexpr <name> fmt <format> <value> command.
+ * This source file was mostly taken from the BusyBox project (www.busybox.net)
+ * In contrast to the original sources the output is not written to stdout
+ * anymore but into a char array, which can be used as input for the env_set()
+ * function.
+ */
+/* Usage: printf format [argument...]
+ *
+ * A front end to the printf function that lets it be used from the shell.
+ *
+ * Backslash escapes:
+ *
+ * \" = double quote
+ * \\ = backslash
+ * \a = alert (bell)
+ * \b = backspace
+ * \c = produce no further output
+ * \f = form feed
+ * \n = new line
+ * \r = carriage return
+ * \t = horizontal tab
+ * \v = vertical tab
+ * \0ooo = octal number (ooo is 0 to 3 digits)
+ * \xhhh = hexadecimal number (hhh is 1 to 3 digits)
+ *
+ * Additional directive:
+ *
+ * %b = print an argument string, interpreting backslash escapes
+ *
+ * The 'format' argument is re-used as many times as necessary
+ * to convert all of the given arguments.
+ *
+ * David MacKenzie <djm@gnu.ai.mit.edu>
+ */
+/* 19990508 Busy Boxed! Dave Cinege */
+
+//config:config PRINTF
+//config: bool "printf (3.8 kb)"
+//config: default y
+//config: help
+//config: printf is used to format and print specified strings.
+//config: It's similar to 'echo' except it has more options.
+
+//applet:IF_PRINTF(APPLET_NOFORK(printf, printf, BB_DIR_USR_BIN, BB_SUID_DROP, printf))
+
+//kbuild:lib-$(CONFIG_PRINTF) += printf.o
+//kbuild:lib-$(CONFIG_ASH_PRINTF) += printf.o
+//kbuild:lib-$(CONFIG_HUSH_PRINTF) += printf.o
+
+//usage:#define printf_trivial_usage
+//usage: "FORMAT [ARG]..."
+//usage:#define printf_full_usage "\n\n"
+//usage: "Format and print ARG(s) according to FORMAT (a-la C printf)"
+//usage:
+//usage:#define printf_example_usage
+//usage: "$ printf \"Val=%d\\n\" 5\n"
+//usage: "Val=5\n"
+
+/* A note on bad input: neither bash 3.2 nor coreutils 6.10 stop on it.
+ * They report it:
+ * bash: printf: XXX: invalid number
+ * printf: XXX: expected a numeric value
+ * bash: printf: 123XXX: invalid number
+ * printf: 123XXX: value not completely converted
+ * but then they use 0 (or partially converted numeric prefix) as a value
+ * and continue. They exit with 1 in this case.
+ * Both accept insane field width/precision (e.g. %9999999999.9999999999d).
+ * Both print error message and assume 0 if %*.*f width/precision is "bad"
+ * (but negative numbers are not "bad").
+ * Both accept negative numbers for %u specifier.
+ *
+ * We try to be compatible.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <vsprintf.h>
+
+#define WANT_HEX_ESCAPES 0
+#define PRINT_CONVERSION_ERROR 1
+#define PRINT_TRUNCATED_ERROR 2
+#define PRINT_SIZE_ERROR 4
+
+struct print_inf {
+ char *str;
+ size_t size;
+ size_t offset;
+ unsigned int error;
+};
+
+typedef void (*converter)(const char *arg, void *result);
+
+/**
+ * printf_str() - print formatted into char array with length checks
+ *
+ * This function povides a printf like function for printing into a char array
+ * with checking the boundaries.
+ * Unlike snprintf, all checks are performed inside this function and status
+ * reports are stored inside the print_inf struct. That way, this function can
+ * be used almost as drop-in replacement without needing much code changes.
+ * Unlike snprintf errors are not reported by return value, but inside the
+ * error member of struct print_inf. The output stored inside the struct
+ * print_inf str member shall only be used when the error member is 0.
+ *
+ * @inf: Info structure for print operation
+ * @char: format string with optional arguments
+ */
+static void printf_str(struct print_inf *inf, char *format, ...)
+{
+ va_list args;
+ int i;
+
+ if (!inf)
+ return;
+
+ /* Do not write anything if previous error is pending */
+ if (inf->error)
+ return;
+
+ /* Check if end of receiving buffer is already reached */
+ if (inf->offset >= inf->size) {
+ inf->error |= PRINT_SIZE_ERROR;
+ return;
+ }
+
+ size_t remaining = inf->size - inf->offset;
+
+ va_start(args, format);
+ i = vsnprintf(inf->str + inf->offset, remaining, format, args);
+ va_end(args);
+
+ if (i < 0)
+ inf->error |= PRINT_CONVERSION_ERROR;
+ else if ((unsigned int)i >= remaining)
+ inf->error |= PRINT_TRUNCATED_ERROR;
+ else
+ inf->offset += i;
+}
+
+/**
+ * putchar_str() - Print single character into char array with length checks
+ *
+ * This function provices a putchar like function, which stores the output
+ * into a char array with checking boundaries.
+ *
+ * @inf: Info structure for print operation
+ * @char: Single character to be printed
+ */
+static void putchar_str(struct print_inf *inf, char c)
+{
+ printf_str(inf, "%c", c);
+}
+
+static char process_escape_sequence(const char **ptr)
+{
+ const char *q;
+ unsigned int num_digits;
+ unsigned int n;
+ unsigned int base;
+
+ num_digits = 0;
+ n = 0;
+ base = 8;
+ q = *ptr;
+
+ if (WANT_HEX_ESCAPES && *q == 'x') {
+ ++q;
+ base = 16;
+ ++num_digits;
+ }
+
+ /* bash requires leading 0 in octal escapes:
+ * \02 works, \2 does not (prints \ and 2).
+ * We treat \2 as a valid octal escape sequence.
+ */
+ do {
+ unsigned int r;
+ unsigned int d = (unsigned char)(*q) - '0';
+#if WANT_HEX_ESCAPES
+ if (d >= 10) {
+ d = (unsigned char)tolower(*q) - 'a';
+ //d += 10;
+ /* The above would map 'A'-'F' and 'a'-'f' to 10-15,
+ * however, some chars like '@' would map to 9 < base.
+ * Do not allow that, map invalid chars to N > base:
+ */
+ if ((int)d >= 0)
+ d += 10;
+ }
+#endif
+ if (d >= base) {
+ if (WANT_HEX_ESCAPES && base == 16) {
+ --num_digits;
+ if (num_digits == 0) {
+ /* \x<bad_char>: return '\',
+ * leave ptr pointing to x
+ */
+ return '\\';
+ }
+ }
+ break;
+ }
+
+ r = n * base + d;
+ if (r > 255)
+ break;
+
+ n = r;
+ ++q;
+ } while (++num_digits < 3);
+
+ if (num_digits == 0) {
+ /* Not octal or hex escape sequence.
+ * Is it one-letter one?
+ */
+ /* bash builtin "echo -e '\ec'" interprets \e as ESC,
+ * but coreutils "/bin/echo -e '\ec'" does not.
+ * Manpages tend to support coreutils way.
+ * Update: coreutils added support for \e on 28 Oct 2009.
+ */
+ static const char charmap[] = {
+ 'a', 'b', 'e', 'f', 'n', 'r', 't', 'v', '\\', '\0',
+ '\a', '\b', 27, '\f', '\n', '\r', '\t', '\v', '\\', '\\',
+ };
+
+ const char *p = charmap;
+
+ do {
+ if (*p == *q) {
+ q++;
+ break;
+ }
+ } while (*++p != '\0');
+ /* p points to found escape char or NUL,
+ * advance it and find what it translates to.
+ * Note that \NUL and unrecognized sequence \z return '\'
+ * and leave ptr pointing to NUL or z.
+ */
+ n = p[sizeof(charmap) / 2];
+ }
+
+ *ptr = q;
+
+ return (char)n;
+}
+
+static char *skip_whitespace(const char *s)
+{
+ /* In POSIX/C locale (the only locale we care about: do we REALLY want
+ * to allow Unicode whitespace in, say, .conf files? nuts!)
+ * isspace is only these chars: "\t\n\v\f\r" and space.
+ * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13.
+ * Use that.
+ */
+ while (*s == ' ' || (unsigned char)(*s - 9) <= (13 - 9))
+ s++;
+
+ return (char *)s;
+}
+
+/* Like strcpy but can copy overlapping strings. */
+static void overlapping_strcpy(char *dst, const char *src)
+{
+ /* Cheap optimization for dst == src case -
+ * better to have it here than in many callers.
+ */
+ if (dst != src) {
+ while ((*dst = *src) != '\0') {
+ dst++;
+ src++;
+ }
+ }
+}
+
+static int multiconvert(const char *arg, void *result, converter convert)
+{
+ if (*arg == '"' || *arg == '\'')
+ sprintf((char *)arg + strlen(arg), "%u", (unsigned char)arg[1]);
+ //errno = 0;
+ convert(arg, result);
+ /* Unlike their Posix counterparts, simple_strtoll and
+ * simple_strtoull do not set errno
+ *
+ * if (errno) {
+ * printf("error invalid number '%s'", arg);
+ * return 1;
+ * }
+ */
+ return 0;
+}
+
+static void conv_strtoull(const char *arg, void *result)
+{
+ /* both coreutils 6.10 and bash 3.2:
+ * $ printf '%x\n' -2
+ * fffffffffffffffe
+ * Mimic that:
+ */
+ if (arg[0] == '-') {
+ *(unsigned long long *)result = simple_strtoll(arg, NULL, 16);
+ return;
+ }
+ /* Allow leading '+' - simple_strtoull() by itself does not allow it,
+ * and probably shouldn't (other callers might require purely numeric
+ * inputs to be allowed.
+ */
+ if (arg[0] == '+')
+ arg++;
+ *(unsigned long long *)result = simple_strtoull(arg, NULL, 16);
+}
+
+static void conv_strtoll(const char *arg, void *result)
+{
+ if (arg[0] == '+')
+ arg++;
+ *(long long *)result = simple_strtoll(arg, NULL, 16);
+}
+
+/* Callers should check errno to detect errors */
+static unsigned long long my_xstrtoull(const char *arg)
+{
+ unsigned long long result;
+
+ if (multiconvert(arg, &result, conv_strtoull))
+ result = 0;
+ return result;
+}
+
+static long long my_xstrtoll(const char *arg)
+{
+ long long result;
+
+ if (multiconvert(arg, &result, conv_strtoll))
+ result = 0;
+ return result;
+}
+
+/* Handles %b; return 1 if output is to be short-circuited by \c */
+static int print_esc_string(struct print_inf *inf, const char *str)
+{
+ char c;
+
+ while ((c = *str) != '\0') {
+ str++;
+ if (c == '\\') {
+ /* %b also accepts 4-digit octals of the form \0### */
+ if (*str == '0') {
+ if ((unsigned char)(str[1] - '0') < 8) {
+ /* 2nd char is 0..7: skip leading '0' */
+ str++;
+ }
+ } else if (*str == 'c') {
+ return 1;
+ }
+ {
+ /* optimization: don't force arg to be on-stack,
+ * use another variable for that.
+ */
+ const char *z = str;
+
+ c = process_escape_sequence(&z);
+ str = z;
+ }
+ }
+ putchar_str(inf, c);
+ }
+
+ return 0;
+}
+
+static void print_direc(struct print_inf *inf, char *format, unsigned int fmt_length,
+ int field_width, int precision,
+ const char *argument)
+{
+ long long llv;
+ char saved;
+ char *have_prec, *have_width;
+
+ saved = format[fmt_length];
+ format[fmt_length] = '\0';
+
+ have_prec = strstr(format, ".*");
+ have_width = strchr(format, '*');
+ if (have_width - 1 == have_prec)
+ have_width = NULL;
+
+ /* multiconvert sets errno = 0, but %s needs it cleared */
+ errno = 0;
+
+ switch (format[fmt_length - 1]) {
+ case 'c':
+ printf_str(inf, format, *argument);
+ break;
+ case 'd':
+ case 'i':
+ llv = my_xstrtoll(skip_whitespace(argument));
+ print_long:
+ if (!have_width) {
+ if (!have_prec)
+ printf_str(inf, format, llv);
+ else
+ printf_str(inf, format, precision, llv);
+ } else {
+ if (!have_prec)
+ printf_str(inf, format, field_width, llv);
+ else
+ printf_str(inf, format, field_width, precision, llv);
+ }
+ break;
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ llv = my_xstrtoull(skip_whitespace(argument));
+ /* cheat: unsigned long and long have same width, so... */
+ goto print_long;
+ case 's':
+ /* Are char* and long long the same? */
+ if (sizeof(argument) == sizeof(llv)) {
+ llv = (long long)(ptrdiff_t)argument;
+ goto print_long;
+ } else {
+ /* Hope compiler will optimize it out by moving call
+ * instruction after the ifs...
+ */
+ if (!have_width) {
+ if (!have_prec)
+ printf_str(inf, format, argument,
+ /*unused:*/ argument, argument);
+ else
+ printf_str(inf, format, precision,
+ argument, /*unused:*/ argument);
+ } else {
+ if (!have_prec)
+ printf_str(inf, format, field_width,
+ argument, /*unused:*/ argument);
+ else
+ printf_str(inf, format, field_width,
+ precision, argument);
+ }
+ break;
+ }
+ break;
+ } /* switch */
+
+ format[fmt_length] = saved;
+}
+
+/* Handle params for "%*.*f". Negative numbers are ok (compat). */
+static int get_width_prec(const char *str)
+{
+ long v = simple_strtol(str, NULL, 10);
+
+ /* Unlike its Posix counterpart, simple_strtol does not set errno
+ *
+ * if (errno) {
+ * printf("error invalid number '%s'", str);
+ * v = 0;
+ * }
+ */
+ return (int)v;
+}
+
+/* Print the text in FORMAT, using ARGV for arguments to any '%' directives.
+ * Return advanced ARGV.
+ */
+static char **print_formatted(struct print_inf *inf, char *f, char **argv, int *conv_err)
+{
+ char *direc_start; /* Start of % directive. */
+ unsigned int direc_length; /* Length of % directive. */
+ int field_width; /* Arg to first '*' */
+ int precision; /* Arg to second '*' */
+ char **saved_argv = argv;
+
+ for (; *f; ++f) {
+ switch (*f) {
+ case '%':
+ direc_start = f++;
+ direc_length = 1;
+ field_width = 0;
+ precision = 0;
+ if (*f == '%') {
+ putchar_str(inf, '%');
+ break;
+ }
+ if (*f == 'b') {
+ if (*argv) {
+ if (print_esc_string(inf, *argv))
+ return saved_argv; /* causes main() to exit */
+ ++argv;
+ }
+ break;
+ }
+ if (*f && strchr("-+ #", *f)) {
+ ++f;
+ ++direc_length;
+ }
+ if (*f == '*') {
+ ++f;
+ ++direc_length;
+ if (*argv)
+ field_width = get_width_prec(*argv++);
+ } else {
+ while (isdigit(*f)) {
+ ++f;
+ ++direc_length;
+ }
+ }
+ if (*f == '.') {
+ ++f;
+ ++direc_length;
+ if (*f == '*') {
+ ++f;
+ ++direc_length;
+ if (*argv)
+ precision = get_width_prec(*argv++);
+ } else {
+ while (isdigit(*f)) {
+ ++f;
+ ++direc_length;
+ }
+ }
+ }
+
+ /* Remove "lLhz" size modifiers, repeatedly.
+ * bash does not like "%lld", but coreutils
+ * happily takes even "%Llllhhzhhzd"!
+ * We are permissive like coreutils
+ */
+ while ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z')
+ overlapping_strcpy(f, f + 1);
+ /* Add "ll" if integer modifier, then print */
+ {
+ static const char format_chars[] = "diouxXcs";
+ char *p = strchr(format_chars, *f);
+ /* needed - try "printf %" without it */
+ if (!p || *f == '\0') {
+ printf("`%s': invalid format\n", direc_start);
+ /* causes main() to exit with error */
+ return saved_argv - 1;
+ }
+ ++direc_length;
+ if (p - format_chars <= 5) {
+ /* it is one of "diouxX" */
+ p = malloc(direc_length + 3);
+ if (!p) {
+ /* exit with error */
+ return saved_argv - 1;
+ }
+ memcpy(p, direc_start, direc_length);
+ p[direc_length + 1] = p[direc_length - 1];
+ p[direc_length - 1] = 'l';
+ p[direc_length] = 'l';
+ //bb_error_msg("<%s>", p);
+ direc_length += 2;
+ direc_start = p;
+ } else {
+ p = NULL;
+ }
+ if (*argv) {
+ print_direc(inf, direc_start, direc_length,
+ field_width, precision, *argv++);
+ } else {
+ print_direc(inf, direc_start, direc_length,
+ field_width, precision, "");
+ }
+ *conv_err |= errno;
+ free(p);
+ }
+ break;
+ case '\\':
+ if (*++f == 'c')
+ return saved_argv; /* causes main() to exit */
+ putchar_str(inf, process_escape_sequence((const char **)&f));
+ f--;
+ break;
+ default:
+ putchar_str(inf, *f);
+ }
+ }
+
+ return argv;
+}
+
+/**
+ * printf_setexpr() - Implements the setexpr <name> fmt <format> command
+ *
+ * This function implements the format string evaluation for the
+ * setexpr <name> fmt <format> <value> command.
+ *
+ * @str: Output string of the evaluated expression
+ * @size: Length of @str buffer
+ * @argc: Number of arguments
+ * @argv: Argument list
+ * @return: 0 if OK, 1 on error
+ */
+int printf_setexpr(char *str, size_t size, int argc, char *const *argv)
+{
+ int conv_err;
+ char *format;
+ char **argv2;
+ struct print_inf inf = {
+ .str = str,
+ .size = size,
+ .offset = 0,
+ .error = 0,
+ };
+
+ if (!str || !size)
+ return 1;
+
+ inf.str[0] = '\0';
+
+ format = argv[0];
+ argv2 = (char **)argv + 1;
+
+ conv_err = 0;
+ argv = argv2;
+ /* In case any print_str call raises an error inf.error will be
+ * set after print_formatted returns.
+ */
+ argv2 = print_formatted(&inf, format, (char **)argv, &conv_err);
+
+ /* coreutils compat (bash doesn't do this):
+ *if (*argv)
+ * fprintf(stderr, "excess args ignored");
+ */
+
+ return (argv2 < argv) || /* if true, print_formatted errored out */
+ conv_err || /* print_formatted saw invalid number */
+ inf.error; /* print_str reported error */
+}
diff --git a/cmd/printf.h b/cmd/printf.h
new file mode 100644
index 00000000000..dcaff6d0972
--- /dev/null
+++ b/cmd/printf.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef __PRINTF_H
+#define __PRINTF_H
+
+int printf_setexpr(char *str, size_t size, int argc, char *const *argv);
+
+#endif
diff --git a/cmd/pstore.c b/cmd/pstore.c
new file mode 100644
index 00000000000..9795eea2dbc
--- /dev/null
+++ b/cmd/pstore.c
@@ -0,0 +1,580 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright © 2019 Collabora Ltd
+ */
+
+#include <config.h>
+#include <command.h>
+#include <fdtdec.h>
+#include <fs.h>
+#include <log.h>
+#include <mapmem.h>
+#include <memalign.h>
+#include <part.h>
+#include <fdt_support.h>
+
+struct persistent_ram_buffer {
+ u32 sig;
+ u32 start;
+ u32 size;
+ u8 data[0];
+};
+
+#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */
+#define RAMOOPS_KERNMSG_HDR "===="
+
+#define PSTORE_TYPE_DMESG 0
+#define PSTORE_TYPE_CONSOLE 2
+#define PSTORE_TYPE_FTRACE 3
+#define PSTORE_TYPE_PMSG 7
+#define PSTORE_TYPE_ALL 255
+
+static phys_addr_t pstore_addr = CONFIG_CMD_PSTORE_MEM_ADDR;
+static phys_size_t pstore_length = CONFIG_CMD_PSTORE_MEM_SIZE;
+static unsigned int pstore_record_size = CONFIG_CMD_PSTORE_RECORD_SIZE;
+static unsigned int pstore_console_size = CONFIG_CMD_PSTORE_CONSOLE_SIZE;
+static unsigned int pstore_ftrace_size = CONFIG_CMD_PSTORE_FTRACE_SIZE;
+static unsigned int pstore_pmsg_size = CONFIG_CMD_PSTORE_PMSG_SIZE;
+static unsigned int pstore_ecc_size = CONFIG_CMD_PSTORE_ECC_SIZE;
+static unsigned int buffer_size;
+
+ /**
+ * pstore_read_kmsg_hdr() - Check kernel header and get compression flag if
+ * available.
+ * @buffer: Kernel messages buffer.
+ * @compressed: Returns TRUE if kernel buffer is compressed, else FALSE.
+ *
+ * Check if buffer starts with a kernel header of the form:
+ * ====<secs>.<nsecs>[-<compression>]\n
+ * If <compression> is equal to 'C' then the buffer is compressed, else iter
+ * should be 'D'.
+ *
+ * Return: Length of kernel header.
+ */
+static int pstore_read_kmsg_hdr(char *buffer, bool *compressed)
+{
+ char *ptr = buffer;
+ *compressed = false;
+
+ if (strncmp(RAMOOPS_KERNMSG_HDR, ptr, strlen(RAMOOPS_KERNMSG_HDR)) != 0)
+ return 0;
+
+ ptr += strlen(RAMOOPS_KERNMSG_HDR);
+
+ ptr = strchr(ptr, '\n');
+ if (!ptr)
+ return 0;
+
+ if (ptr[-2] == '-' && ptr[-1] == 'C')
+ *compressed = true;
+
+ return ptr - buffer + 1;
+}
+
+/**
+ * pstore_get_buffer() - Get unwrapped record buffer
+ * @sig: Signature to check
+ * @buffer: Buffer containing wrapped record
+ * @size: wrapped record size
+ * @dest: Buffer used to store unwrapped record
+ *
+ * The record starts with <signature><start><size> header.
+ * The signature is 'DBGC' for all records except for Ftrace's record(s) wich
+ * use LINUX_VERSION_CODE ^ 'DBGC'.
+ * Use 0 for @sig to prevent checking signature.
+ * Start and size are 4 bytes long.
+ *
+ * Return: record's length
+ */
+static u32 pstore_get_buffer(u32 sig, phys_addr_t buffer, u32 size, char *dest)
+{
+ struct persistent_ram_buffer *prb =
+ (struct persistent_ram_buffer *)map_sysmem(buffer, size);
+ u32 dest_size;
+
+ if (sig == 0 || prb->sig == sig) {
+ if (prb->size == 0) {
+ log_debug("found existing empty buffer\n");
+ return 0;
+ }
+
+ if (prb->size > size) {
+ log_debug("found existing invalid buffer, size %u, start %u\n",
+ prb->size, prb->start);
+ return 0;
+ }
+ } else {
+ log_debug("no valid data in buffer (sig = 0x%08x)\n", prb->sig);
+ return 0;
+ }
+
+ log_debug("found existing buffer, size %u, start %u\n",
+ prb->size, prb->start);
+
+ memcpy(dest, &prb->data[prb->start], prb->size - prb->start);
+ memcpy(dest + prb->size - prb->start, &prb->data[0], prb->start);
+
+ dest_size = prb->size;
+ unmap_sysmem(prb);
+
+ return dest_size;
+}
+
+/**
+ * pstore_init_buffer_size() - Init buffer size to largest record size
+ *
+ * Records, console, FTrace and user logs can use different buffer sizes.
+ * This function allows to retrieve the biggest one.
+ */
+static void pstore_init_buffer_size(void)
+{
+ if (pstore_record_size > buffer_size)
+ buffer_size = pstore_record_size;
+
+ if (pstore_console_size > buffer_size)
+ buffer_size = pstore_console_size;
+
+ if (pstore_ftrace_size > buffer_size)
+ buffer_size = pstore_ftrace_size;
+
+ if (pstore_pmsg_size > buffer_size)
+ buffer_size = pstore_pmsg_size;
+}
+
+/**
+ * pstore_set() - Initialize PStore settings from command line arguments
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * Set pstore reserved memory info, starting at 'addr' for 'len' bytes.
+ * Default length for records is 4K.
+ * Mandatory arguments:
+ * - addr: ramoops starting address
+ * - len: ramoops total length
+ * Optional arguments:
+ * - record-size: size of one panic or oops record ('dump' type)
+ * - console-size: size of the kernel logs record
+ * - ftrace-size: size of the ftrace record(s), this can be a single record or
+ * divided in parts based on number of CPUs
+ * - pmsg-size: size of the user space logs record
+ * - ecc-size: enables/disables ECC support and specifies ECC buffer size in
+ * bytes (0 disables it, 1 is a special value, means 16 bytes ECC)
+ *
+ * Return: zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ */
+static int pstore_set(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ /* Address is specified since argc > 2
+ */
+ pstore_addr = hextoul(argv[1], NULL);
+
+ /* Length is specified since argc > 2
+ */
+ pstore_length = hextoul(argv[2], NULL);
+
+ if (argc > 3)
+ pstore_record_size = hextoul(argv[3], NULL);
+
+ if (argc > 4)
+ pstore_console_size = hextoul(argv[4], NULL);
+
+ if (argc > 5)
+ pstore_ftrace_size = hextoul(argv[5], NULL);
+
+ if (argc > 6)
+ pstore_pmsg_size = hextoul(argv[6], NULL);
+
+ if (argc > 7)
+ pstore_ecc_size = hextoul(argv[7], NULL);
+
+ if (pstore_length < (pstore_record_size + pstore_console_size
+ + pstore_ftrace_size + pstore_pmsg_size)) {
+ printf("pstore <len> should be larger than the sum of all records sizes\n");
+ pstore_length = 0;
+ }
+
+ log_debug("pstore set done: start 0x%08llx - length 0x%llx\n",
+ (unsigned long long)pstore_addr,
+ (unsigned long long)pstore_length);
+
+ return 0;
+}
+
+/**
+ * pstore_print_buffer() - Print buffer
+ * @type: buffer type
+ * @buffer: buffer to print
+ * @size: buffer size
+ *
+ * Print buffer type and content
+ */
+static void pstore_print_buffer(char *type, char *buffer, u32 size)
+{
+ u32 i = 0;
+
+ printf("**** %s\n", type);
+ while (i < size && buffer[i] != 0) {
+ putc(buffer[i]);
+ i++;
+ }
+}
+
+/**
+ * pstore_display() - Display existing records in pstore reserved memory
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * A 'record-type' can be given to only display records of this kind.
+ * If no 'record-type' is given, all valid records are dispayed.
+ * 'record-type' can be one of 'dump', 'console', 'ftrace' or 'user'. For 'dump'
+ * and 'ftrace' types, a 'nb' can be given to only display one record.
+ *
+ * Return: zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ */
+static int pstore_display(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ int type = PSTORE_TYPE_ALL;
+ phys_addr_t ptr;
+ char *buffer;
+ u32 size;
+ int header_len = 0;
+ bool compressed;
+
+ if (argc > 1) {
+ if (!strcmp(argv[1], "dump"))
+ type = PSTORE_TYPE_DMESG;
+ else if (!strcmp(argv[1], "console"))
+ type = PSTORE_TYPE_CONSOLE;
+ else if (!strcmp(argv[1], "ftrace"))
+ type = PSTORE_TYPE_FTRACE;
+ else if (!strcmp(argv[1], "user"))
+ type = PSTORE_TYPE_PMSG;
+ else
+ return CMD_RET_USAGE;
+ }
+
+ if (pstore_length == 0) {
+ printf("Please set PStore configuration\n");
+ return CMD_RET_USAGE;
+ }
+
+ if (buffer_size == 0)
+ pstore_init_buffer_size();
+
+ buffer = malloc_cache_aligned(buffer_size);
+
+ if (type == PSTORE_TYPE_DMESG || type == PSTORE_TYPE_ALL) {
+ ptr = pstore_addr;
+ phys_addr_t ptr_end = ptr + pstore_length - pstore_pmsg_size
+ - pstore_ftrace_size - pstore_console_size;
+
+ if (argc > 2) {
+ ptr += dectoul(argv[2], NULL)
+ * pstore_record_size;
+ ptr_end = ptr + pstore_record_size;
+ }
+
+ while (ptr < ptr_end) {
+ size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr,
+ pstore_record_size, buffer);
+ ptr += pstore_record_size;
+
+ if (size == 0)
+ continue;
+
+ header_len = pstore_read_kmsg_hdr(buffer, &compressed);
+ if (header_len == 0) {
+ log_debug("no valid kernel header\n");
+ continue;
+ }
+
+ if (compressed) {
+ printf("Compressed buffer, display not available\n");
+ continue;
+ }
+
+ pstore_print_buffer("Dump", buffer + header_len,
+ size - header_len);
+ }
+ }
+
+ if (type == PSTORE_TYPE_CONSOLE || type == PSTORE_TYPE_ALL) {
+ ptr = pstore_addr + pstore_length - pstore_pmsg_size
+ - pstore_ftrace_size - pstore_console_size;
+ size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr,
+ pstore_console_size, buffer);
+ if (size != 0)
+ pstore_print_buffer("Console", buffer, size);
+ }
+
+ if (type == PSTORE_TYPE_FTRACE || type == PSTORE_TYPE_ALL) {
+ ptr = pstore_addr + pstore_length - pstore_pmsg_size
+ - pstore_ftrace_size;
+ /* The FTrace record(s) uses LINUX_VERSION_CODE ^ 'DBGC'
+ * signature, pass 0 to pstore_get_buffer to prevent
+ * checking it
+ */
+ size = pstore_get_buffer(0, ptr, pstore_ftrace_size, buffer);
+ if (size != 0)
+ pstore_print_buffer("FTrace", buffer, size);
+ }
+
+ if (type == PSTORE_TYPE_PMSG || type == PSTORE_TYPE_ALL) {
+ ptr = pstore_addr + pstore_length - pstore_pmsg_size;
+ size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr,
+ pstore_pmsg_size, buffer);
+ if (size != 0)
+ pstore_print_buffer("User", buffer, size);
+ }
+
+ free(buffer);
+
+ return 0;
+}
+
+/**
+ * pstore_save() - Save existing records from pstore reserved memory
+ * @cmdtp: Command data struct pointer
+ * @flag: Command flag
+ * @argc: Command-line argument count
+ * @argv: Array of command-line arguments
+ *
+ * the records are saved under 'directory path', which should already exist,
+ * to partition 'part' on device type 'interface' instance 'dev'
+ * Filenames are automatically generated, depending on record type, like in
+ * /sys/fs/pstore under Linux
+ *
+ * Return: zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ */
+static int pstore_save(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ phys_addr_t ptr, ptr_end;
+ char *buffer;
+ char *save_argv[6];
+ char addr[19], length[19];
+ char path[256];
+ u32 size;
+ unsigned int index;
+ int header_len = 0;
+ bool compressed;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ if (pstore_length == 0) {
+ printf("Please set PStore configuration\n");
+ return CMD_RET_USAGE;
+ }
+
+ if (buffer_size == 0)
+ pstore_init_buffer_size();
+
+ buffer = malloc_cache_aligned(buffer_size);
+ sprintf(addr, "0x%p", buffer);
+
+ save_argv[0] = argv[0];
+ save_argv[1] = argv[1];
+ save_argv[2] = argv[2];
+ save_argv[3] = addr;
+ save_argv[4] = path;
+ save_argv[5] = length;
+
+ /* Save all Dump records */
+ ptr = pstore_addr;
+ ptr_end = ptr + pstore_length - pstore_pmsg_size - pstore_ftrace_size
+ - pstore_console_size;
+ index = 0;
+ while (ptr < ptr_end) {
+ size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr,
+ pstore_record_size, buffer);
+ ptr += pstore_record_size;
+
+ if (size == 0)
+ continue;
+
+ header_len = pstore_read_kmsg_hdr(buffer, &compressed);
+ if (header_len == 0) {
+ log_debug("no valid kernel header\n");
+ continue;
+ }
+
+ sprintf(addr, "0x%08lx", (ulong)map_to_sysmem(buffer + header_len));
+ sprintf(length, "0x%X", size - header_len);
+ sprintf(path, "%s/dmesg-ramoops-%u%s", argv[3], index,
+ compressed ? ".enc.z" : "");
+ do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY);
+ index++;
+ }
+
+ sprintf(addr, "0x%08lx", (ulong)map_to_sysmem(buffer));
+
+ /* Save Console record */
+ size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, pstore_console_size,
+ buffer);
+ if (size != 0) {
+ sprintf(length, "0x%X", size);
+ sprintf(path, "%s/console-ramoops-0", argv[3]);
+ do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY);
+ }
+ ptr += pstore_console_size;
+
+ /* Save FTrace record(s)
+ * The FTrace record(s) uses LINUX_VERSION_CODE ^ 'DBGC' signature,
+ * pass 0 to pstore_get_buffer to prevent checking it
+ */
+ size = pstore_get_buffer(0, ptr, pstore_ftrace_size, buffer);
+ if (size != 0) {
+ sprintf(length, "0x%X", size);
+ sprintf(path, "%s/ftrace-ramoops-0", argv[3]);
+ do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY);
+ }
+ ptr += pstore_ftrace_size;
+
+ /* Save Console record */
+ size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, pstore_pmsg_size,
+ buffer);
+ if (size != 0) {
+ sprintf(length, "0x%X", size);
+ sprintf(path, "%s/pmsg-ramoops-0", argv[3]);
+ do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY);
+ }
+
+ free(buffer);
+
+ return 0;
+}
+
+static struct cmd_tbl cmd_pstore_sub[] = {
+ U_BOOT_CMD_MKENT(set, 8, 0, pstore_set, "", ""),
+ U_BOOT_CMD_MKENT(display, 3, 0, pstore_display, "", ""),
+ U_BOOT_CMD_MKENT(save, 4, 0, pstore_save, "", ""),
+};
+
+static int do_pstore(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ struct cmd_tbl *c;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* Strip off leading argument */
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], cmd_pstore_sub, ARRAY_SIZE(cmd_pstore_sub));
+
+ if (!c)
+ return CMD_RET_USAGE;
+
+ return c->cmd(cmdtp, flag, argc, argv);
+}
+
+void fdt_fixup_pstore(void *blob)
+{
+ char node[32];
+ int nodeoffset; /* node offset from libfdt */
+ u32 addr_cells_root;
+ u32 size_cells_root;
+ u32 addr_cells;
+ u32 size_cells;
+
+ nodeoffset = fdt_path_offset(blob, "/");
+ if (nodeoffset < 0) {
+ /* Not found or something else bad happened. */
+ log_err("fdt_path_offset() returned %s\n", fdt_strerror(nodeoffset));
+ return;
+ }
+ addr_cells_root = fdt_getprop_u32_default_node(blob, nodeoffset, 0, "#address-cells", 2);
+ size_cells_root = fdt_getprop_u32_default_node(blob, nodeoffset, 0, "#size-cells", 2);
+
+ nodeoffset = fdt_find_or_add_subnode(blob, nodeoffset, "reserved-memory");
+ if (nodeoffset < 0) {
+ log_err("Add 'reserved-memory' node failed: %s\n",
+ fdt_strerror(nodeoffset));
+ return;
+ }
+
+ addr_cells = fdt_getprop_u32_default_node(blob, nodeoffset, 0,
+ "#address-cells", addr_cells_root);
+ size_cells = fdt_getprop_u32_default_node(blob, nodeoffset, 0,
+ "#size-cells", size_cells_root);
+ fdt_setprop_u32(blob, nodeoffset, "#address-cells", addr_cells);
+ fdt_setprop_u32(blob, nodeoffset, "#size-cells", size_cells);
+
+ fdt_setprop_empty(blob, nodeoffset, "ranges");
+
+ sprintf(node, "ramoops@%llx", (unsigned long long)pstore_addr);
+ nodeoffset = fdt_add_subnode(blob, nodeoffset, node);
+ if (nodeoffset < 0) {
+ log_err("Add '%s' node failed: %s\n", node, fdt_strerror(nodeoffset));
+ return;
+ }
+
+ fdt_setprop_string(blob, nodeoffset, "compatible", "ramoops");
+
+ if (addr_cells == 1) {
+ fdt_setprop_u32(blob, nodeoffset, "reg", pstore_addr);
+ } else if (addr_cells == 2) {
+ fdt_setprop_u64(blob, nodeoffset, "reg", pstore_addr);
+ } else {
+ log_err("Unsupported #address-cells: %u\n", addr_cells);
+ goto clean_ramoops;
+ }
+
+ if (size_cells == 1) {
+ // Let's consider that the pstore_length fits in a 32 bits value
+ fdt_appendprop_u32(blob, nodeoffset, "reg", pstore_length);
+ } else if (size_cells == 2) {
+ fdt_appendprop_u64(blob, nodeoffset, "reg", pstore_length);
+ } else {
+ log_err("Unsupported #size-cells: %u\n", addr_cells);
+ goto clean_ramoops;
+ }
+
+ fdt_setprop_u32(blob, nodeoffset, "record-size", pstore_record_size);
+ fdt_setprop_u32(blob, nodeoffset, "console-size", pstore_console_size);
+ fdt_setprop_u32(blob, nodeoffset, "ftrace-size", pstore_ftrace_size);
+ fdt_setprop_u32(blob, nodeoffset, "pmsg-size", pstore_pmsg_size);
+ fdt_setprop_u32(blob, nodeoffset, "ecc-size", pstore_ecc_size);
+
+clean_ramoops:
+ fdt_del_node_and_alias(blob, node);
+}
+
+U_BOOT_CMD(pstore, 10, 0, do_pstore,
+ "Manage Linux Persistent Storage",
+ "set <addr> <len> [record-size] [console-size] [ftrace-size] [pmsg_size] [ecc-size]\n"
+ "- Set pstore reserved memory info, starting at 'addr' for 'len' bytes.\n"
+ " Default length for records is 4K.\n"
+ " 'record-size' is the size of one panic or oops record ('dump' type).\n"
+ " 'console-size' is the size of the kernel logs record.\n"
+ " 'ftrace-size' is the size of the ftrace record(s), this can be a single\n"
+ " record or divided in parts based on number of CPUs.\n"
+ " 'pmsg-size' is the size of the user space logs record.\n"
+ " 'ecc-size' enables/disables ECC support and specifies ECC buffer size in\n"
+ " bytes (0 disables it, 1 is a special value, means 16 bytes ECC).\n"
+ "pstore display [record-type] [nb]\n"
+ "- Display existing records in pstore reserved memory. A 'record-type' can\n"
+ " be given to only display records of this kind. 'record-type' can be one\n"
+ " of 'dump', 'console', 'ftrace' or 'user'. For 'dump' and 'ftrace' types,\n"
+ " a 'nb' can be given to only display one record.\n"
+ "pstore save <interface> <dev[:part]> <directory-path>\n"
+ "- Save existing records in pstore reserved memory under 'directory path'\n"
+ " to partition 'part' on device type 'interface' instance 'dev'.\n"
+ " Filenames are automatically generated, depending on record type, like\n"
+ " in /sys/fs/pstore under Linux.\n"
+ " The 'directory-path' should already exist.\n"
+);
diff --git a/cmd/pvblock.c b/cmd/pvblock.c
new file mode 100644
index 00000000000..3a83ac9cd92
--- /dev/null
+++ b/cmd/pvblock.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2020 EPAM Systems Inc.
+ *
+ * XEN para-virtualized block device support
+ */
+
+#include <blk.h>
+#include <command.h>
+
+/* Current I/O Device */
+static int pvblock_curr_device;
+
+int do_pvblock(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ return blk_common_cmd(argc, argv, UCLASS_PVBLOCK,
+ &pvblock_curr_device);
+}
+
+U_BOOT_CMD(pvblock, 5, 1, do_pvblock,
+ "Xen para-virtualized block device",
+ "info - show available block devices\n"
+ "pvblock device [dev] - show or set current device\n"
+ "pvblock part [dev] - print partition table of one or all devices\n"
+ "pvblock read addr blk# cnt\n"
+ "pvblock write addr blk# cnt - read/write `cnt'"
+ " blocks starting at block `blk#'\n"
+ " to/from memory address `addr'");
diff --git a/cmd/pwm.c b/cmd/pwm.c
new file mode 100644
index 00000000000..7e82955239f
--- /dev/null
+++ b/cmd/pwm.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Control PWM channels
+ *
+ * Copyright (c) 2020 SiFive, Inc
+ * author: Pragnesh Patel <pragnesh.patel@sifive.com>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <pwm.h>
+
+enum pwm_cmd {
+ PWM_SET_INVERT,
+ PWM_SET_CONFIG,
+ PWM_SET_ENABLE,
+ PWM_SET_DISABLE,
+};
+
+static int do_pwm(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *str_cmd, *str_channel = NULL, *str_enable = NULL;
+ const char *str_pwm = NULL, *str_period = NULL, *str_duty = NULL;
+ enum pwm_cmd sub_cmd;
+ struct udevice *dev;
+ u32 channel, pwm_enable, pwm_dev, period_ns = 0, duty_ns = 0;
+ int ret;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ str_cmd = argv[1];
+ argc -= 2;
+ argv += 2;
+
+ str_pwm = *argv;
+ argc--;
+ argv++;
+
+ if (!str_pwm)
+ return CMD_RET_USAGE;
+
+ switch (*str_cmd) {
+ case 'i':
+ sub_cmd = PWM_SET_INVERT;
+ if (argc != 2)
+ return CMD_RET_USAGE;
+ break;
+ case 'c':
+ sub_cmd = PWM_SET_CONFIG;
+ if (argc != 3)
+ return CMD_RET_USAGE;
+ break;
+ case 'e':
+ sub_cmd = PWM_SET_ENABLE;
+ if (argc != 1)
+ return CMD_RET_USAGE;
+ break;
+ case 'd':
+ sub_cmd = PWM_SET_DISABLE;
+ if (argc != 1)
+ return CMD_RET_USAGE;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ pwm_dev = dectoul(str_pwm, NULL);
+ ret = uclass_get_device(UCLASS_PWM, pwm_dev, &dev);
+ if (ret) {
+ printf("pwm: '%s' not found\n", str_pwm);
+ return cmd_process_error(cmdtp, ret);
+ }
+
+ str_channel = *argv;
+ channel = dectoul(str_channel, NULL);
+ argc--;
+ argv++;
+
+ if (sub_cmd == PWM_SET_INVERT) {
+ str_enable = *argv;
+ pwm_enable = dectoul(str_enable, NULL);
+ ret = pwm_set_invert(dev, channel, pwm_enable);
+ } else if (sub_cmd == PWM_SET_CONFIG) {
+ str_period = *argv;
+ argc--;
+ argv++;
+ period_ns = dectoul(str_period, NULL);
+
+ str_duty = *argv;
+ duty_ns = dectoul(str_duty, NULL);
+
+ ret = pwm_set_config(dev, channel, period_ns, duty_ns);
+ } else if (sub_cmd == PWM_SET_ENABLE) {
+ ret = pwm_set_enable(dev, channel, 1);
+ } else if (sub_cmd == PWM_SET_DISABLE) {
+ ret = pwm_set_enable(dev, channel, 0);
+ }
+
+ if (ret) {
+ printf("error(%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(pwm, 6, 0, do_pwm,
+ "control pwm channels",
+ "invert <pwm_dev_num> <channel> <polarity> - invert polarity\n"
+ "pwm config <pwm_dev_num> <channel> <period_ns> <duty_ns> - config PWM\n"
+ "pwm enable <pwm_dev_num> <channel> - enable PWM output\n"
+ "pwm disable <pwm_dev_num> <channel> - disable PWM output\n"
+ "Note: All input values are in decimal");
diff --git a/cmd/pxe.c b/cmd/pxe.c
new file mode 100644
index 00000000000..3deae5e6d47
--- /dev/null
+++ b/cmd/pxe.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2010-2011 Calxeda, Inc.
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <command.h>
+#include <env.h>
+#include <fs.h>
+#include <net.h>
+#include <net6.h>
+#include <malloc.h>
+#include <vsprintf.h>
+
+#include "pxe_utils.h"
+
+const char *pxe_default_paths[] = {
+#ifdef CONFIG_SYS_SOC
+#ifdef CONFIG_SYS_BOARD
+ "default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC "-" CONFIG_SYS_BOARD,
+#endif
+ "default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC,
+#endif
+ "default-" CONFIG_SYS_ARCH,
+ "default",
+ NULL
+};
+
+static int do_get_tftp(struct pxe_context *ctx, const char *file_path,
+ char *file_addr, enum bootflow_img_t type, ulong *sizep)
+{
+ char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
+ int ret;
+ int num_args;
+
+ tftp_argv[1] = file_addr;
+ tftp_argv[2] = (void *)file_path;
+ if (ctx->use_ipv6) {
+ tftp_argv[3] = USE_IP6_CMD_PARAM;
+ num_args = 4;
+ } else {
+ num_args = 3;
+ }
+
+ if (do_tftpb(ctx->cmdtp, 0, num_args, tftp_argv))
+ return -ENOENT;
+
+ ret = pxe_get_file_size(sizep);
+ if (ret)
+ return log_msg_ret("tftp", ret);
+ ctx->pxe_file_size = *sizep;
+
+ return 1;
+}
+
+/*
+ * Looks for a pxe file with specified config file name,
+ * which is received from DHCPv4 option 209 or
+ * DHCPv6 option 60.
+ *
+ * Returns 1 on success or < 0 on error.
+ */
+static int pxe_dhcp_option_path(struct pxe_context *ctx, unsigned long pxefile_addr_r)
+{
+ int ret = get_pxe_file(ctx, pxelinux_configfile, pxefile_addr_r);
+
+ free(pxelinux_configfile);
+ /* set to NULL to avoid double-free if DHCP is tried again */
+ pxelinux_configfile = NULL;
+
+ return ret;
+}
+
+/*
+ * Looks for a pxe file with a name based on the pxeuuid environment variable.
+ *
+ * Returns 1 on success or < 0 on error.
+ */
+static int pxe_uuid_path(struct pxe_context *ctx, unsigned long pxefile_addr_r)
+{
+ char *uuid_str;
+
+ uuid_str = from_env("pxeuuid");
+
+ if (!uuid_str)
+ return -ENOENT;
+
+ return get_pxelinux_path(ctx, uuid_str, pxefile_addr_r);
+}
+
+/*
+ * Looks for a pxe file with a name based on the 'ethaddr' environment
+ * variable.
+ *
+ * Returns 1 on success or < 0 on error.
+ */
+static int pxe_mac_path(struct pxe_context *ctx, unsigned long pxefile_addr_r)
+{
+ char mac_str[21];
+ int err;
+
+ err = format_mac_pxe(mac_str, sizeof(mac_str));
+
+ if (err < 0)
+ return err;
+
+ return get_pxelinux_path(ctx, mac_str, pxefile_addr_r);
+}
+
+/*
+ * Looks for pxe files with names based on our IP address. See pxelinux
+ * documentation for details on what these file names look like. We match
+ * that exactly.
+ *
+ * Returns 1 on success or < 0 on error.
+ */
+static int pxe_ipaddr_paths(struct pxe_context *ctx, unsigned long pxefile_addr_r)
+{
+ char ip_addr[9];
+ int mask_pos, err;
+
+ sprintf(ip_addr, "%08X", ntohl(net_ip.s_addr));
+
+ for (mask_pos = 7; mask_pos >= 0; mask_pos--) {
+ err = get_pxelinux_path(ctx, ip_addr, pxefile_addr_r);
+
+ if (err > 0)
+ return err;
+
+ ip_addr[mask_pos] = '\0';
+ }
+
+ return -ENOENT;
+}
+
+int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep, bool use_ipv6)
+{
+ struct cmd_tbl cmdtp[] = {}; /* dummy */
+ struct pxe_context ctx;
+ int i;
+
+ if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false,
+ env_get("bootfile"), use_ipv6, false))
+ return -ENOMEM;
+
+ if (IS_ENABLED(CONFIG_BOOTP_PXE_DHCP_OPTION) &&
+ pxelinux_configfile && !use_ipv6) {
+ if (pxe_dhcp_option_path(&ctx, pxefile_addr_r) > 0)
+ goto done;
+
+ goto error_exit;
+ }
+
+ if (IS_ENABLED(CONFIG_DHCP6_PXE_DHCP_OPTION) &&
+ pxelinux_configfile && use_ipv6) {
+ if (pxe_dhcp_option_path(&ctx, pxefile_addr_r) > 0)
+ goto done;
+
+ goto error_exit;
+ }
+
+ /*
+ * Keep trying paths until we successfully get a file we're looking
+ * for.
+ */
+ if (pxe_uuid_path(&ctx, pxefile_addr_r) > 0 ||
+ pxe_mac_path(&ctx, pxefile_addr_r) > 0 ||
+ pxe_ipaddr_paths(&ctx, pxefile_addr_r) > 0)
+ goto done;
+
+ i = 0;
+ while (pxe_default_paths[i]) {
+ if (get_pxelinux_path(&ctx, pxe_default_paths[i],
+ pxefile_addr_r) > 0)
+ goto done;
+ i++;
+ }
+
+error_exit:
+ pxe_destroy_ctx(&ctx);
+
+ return -ENOENT;
+done:
+ *bootdirp = env_get("bootfile");
+
+ /*
+ * The PXE file size is returned but not the name. It is probably not
+ * that useful.
+ */
+ *sizep = ctx.pxe_file_size;
+ pxe_destroy_ctx(&ctx);
+
+ return 0;
+}
+
+/*
+ * Entry point for the 'pxe get' command.
+ * This Follows pxelinux's rules to download a config file from a tftp server.
+ * The file is stored at the location given by the pxefile_addr_r environment
+ * variable, which must be set.
+ *
+ * UUID comes from pxeuuid env variable, if defined
+ * MAC addr comes from ethaddr env variable, if defined
+ * IP
+ *
+ * see http://syslinux.zytor.com/wiki/index.php/PXELINUX
+ *
+ * Returns 0 on success or 1 on error.
+ */
+static int
+do_pxe_get(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char *pxefile_addr_str;
+ ulong pxefile_addr_r;
+ char *fname;
+ ulong size;
+ int ret;
+ bool use_ipv6 = false;
+
+ if (IS_ENABLED(CONFIG_IPV6)) {
+ if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM))
+ use_ipv6 = true;
+
+ if (!(argc == 1 || (argc == 2 && use_ipv6)))
+ return CMD_RET_USAGE;
+ } else {
+ if (argc != 1)
+ return CMD_RET_USAGE;
+ }
+
+ pxefile_addr_str = from_env("pxefile_addr_r");
+
+ if (!pxefile_addr_str)
+ return 1;
+
+ ret = strict_strtoul(pxefile_addr_str, 16,
+ (unsigned long *)&pxefile_addr_r);
+ if (ret < 0)
+ return 1;
+
+ ret = pxe_get(pxefile_addr_r, &fname, &size, use_ipv6);
+ switch (ret) {
+ case 0:
+ printf("Config file '%s' found\n", fname);
+ break;
+ case -ENOMEM:
+ printf("Out of memory\n");
+ return CMD_RET_FAILURE;
+ default:
+ printf("Config file not found\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+/*
+ * Boots a system using a pxe file
+ *
+ * Returns 0 on success, 1 on error.
+ */
+static int
+do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ unsigned long pxefile_addr_r;
+ char *pxefile_addr_str;
+ struct pxe_context ctx;
+ int ret;
+ bool use_ipv6 = false;
+
+ if (IS_ENABLED(CONFIG_IPV6)) {
+ if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM))
+ use_ipv6 = true;
+ }
+
+ if (argc == 1 || (argc == 2 && use_ipv6)) {
+ pxefile_addr_str = from_env("pxefile_addr_r");
+ if (!pxefile_addr_str)
+ return 1;
+
+ } else if (argc == 2 || (argc == 3 && use_ipv6)) {
+ pxefile_addr_str = argv[1];
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
+ printf("Invalid pxefile address: %s\n", pxefile_addr_str);
+ return 1;
+ }
+
+ if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false,
+ env_get("bootfile"), use_ipv6, false)) {
+ printf("Out of memory\n");
+ return CMD_RET_FAILURE;
+ }
+ ret = pxe_process(&ctx, pxefile_addr_r, false);
+ pxe_destroy_ctx(&ctx);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ copy_filename(net_boot_file_name, "", sizeof(net_boot_file_name));
+
+ return 0;
+}
+
+static struct cmd_tbl cmd_pxe_sub[] = {
+ U_BOOT_CMD_MKENT(get, 2, 1, do_pxe_get, "", ""),
+ U_BOOT_CMD_MKENT(boot, 3, 1, do_pxe_boot, "", "")
+};
+
+static int do_pxe(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* drop initial "pxe" arg */
+ argc--;
+ argv++;
+
+ cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub));
+
+ if (cp)
+ return cp->cmd(cmdtp, flag, argc, argv);
+
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(pxe, 4, 1, do_pxe,
+ "get and boot from pxe files",
+ "get [" USE_IP6_CMD_PARAM "] - try to retrieve a pxe file using tftp\n"
+ "pxe boot [pxefile_addr_r] [-ipv6] - boot from the pxe file at pxefile_addr_r\n"
+);
diff --git a/cmd/qfw.c b/cmd/qfw.c
new file mode 100644
index 00000000000..1b108118658
--- /dev/null
+++ b/cmd/qfw.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
+ */
+
+#include <command.h>
+#include <env.h>
+#include <errno.h>
+#include <qfw.h>
+#include <dm.h>
+
+static struct udevice *qfw_dev;
+
+static int qemu_fwcfg_cmd_list_firmware(void)
+{
+ int ret;
+ struct fw_cfg_file_iter iter;
+ struct fw_file *file;
+
+ /* make sure fw_list is loaded */
+ ret = qfw_read_firmware_list(qfw_dev);
+ if (ret)
+ return ret;
+
+ for (file = qfw_file_iter_init(qfw_dev, &iter);
+ !qfw_file_iter_end(&iter);
+ file = qfw_file_iter_next(&iter)) {
+ printf("%08lx %-56s\n", file->addr, file->cfg.name);
+ }
+
+ return 0;
+}
+
+static int qemu_fwcfg_do_list(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ if (qemu_fwcfg_cmd_list_firmware() < 0)
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+static int qemu_fwcfg_do_cpus(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ printf("%d cpu(s) online\n", qfw_online_cpus(qfw_dev));
+ return 0;
+}
+
+static int qemu_fwcfg_do_load(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ char *env;
+ ulong load_addr;
+ ulong initrd_addr;
+
+ env = env_get("loadaddr");
+ load_addr = env ?
+ hextoul(env, NULL) :
+ CONFIG_SYS_LOAD_ADDR;
+
+ env = env_get("ramdiskaddr");
+ initrd_addr = env ?
+ hextoul(env, NULL) :
+#ifdef CFG_RAMDISK_ADDR
+ CFG_RAMDISK_ADDR;
+#else
+ 0;
+#endif
+
+ if (argc == 2) {
+ load_addr = hextoul(argv[0], NULL);
+ initrd_addr = hextoul(argv[1], NULL);
+ } else if (argc == 1) {
+ load_addr = hextoul(argv[0], NULL);
+ }
+
+ if (!load_addr || !initrd_addr) {
+ printf("missing load or initrd address\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return qemu_fwcfg_setup_kernel(qfw_dev, load_addr, initrd_addr);
+}
+
+static struct cmd_tbl fwcfg_commands[] = {
+ U_BOOT_CMD_MKENT(list, 0, 1, qemu_fwcfg_do_list, "", ""),
+ U_BOOT_CMD_MKENT(cpus, 0, 1, qemu_fwcfg_do_cpus, "", ""),
+ U_BOOT_CMD_MKENT(load, 2, 1, qemu_fwcfg_do_load, "", ""),
+};
+
+static int do_qemu_fw(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+ struct cmd_tbl *fwcfg_cmd;
+
+ ret = qfw_get_dev(&qfw_dev);
+ if (ret) {
+ printf("QEMU fw_cfg interface not found\n");
+ return CMD_RET_USAGE;
+ }
+
+ fwcfg_cmd = find_cmd_tbl(argv[1], fwcfg_commands,
+ ARRAY_SIZE(fwcfg_commands));
+ argc -= 2;
+ argv += 2;
+ if (!fwcfg_cmd || argc > fwcfg_cmd->maxargs)
+ return CMD_RET_USAGE;
+
+ ret = fwcfg_cmd->cmd(fwcfg_cmd, flag, argc, argv);
+
+ return cmd_process_error(fwcfg_cmd, ret);
+}
+
+U_BOOT_CMD(
+ qfw, 4, 1, do_qemu_fw,
+ "QEMU firmware interface",
+ "<command>\n"
+ " - list : print firmware(s) currently loaded\n"
+ " - cpus : print online cpu number\n"
+ " - load <kernel addr> <initrd addr> : load kernel and initrd (if any), and setup for zboot\n"
+);
diff --git a/cmd/read.c b/cmd/read.c
new file mode 100644
index 00000000000..8e21f004423
--- /dev/null
+++ b/cmd/read.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ */
+
+#include <command.h>
+#include <mapmem.h>
+#include <part.h>
+#include <vsprintf.h>
+
+static int
+do_rw(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct blk_desc *dev_desc = NULL;
+ struct disk_partition part_info;
+ ulong offset, limit;
+ uint blk, cnt, res;
+ void *ptr;
+ int part;
+
+ if (argc != 6) {
+ cmd_usage(cmdtp);
+ return 1;
+ }
+
+ part = part_get_info_by_dev_and_name_or_num(argv[1], argv[2],
+ &dev_desc, &part_info, 1);
+ if (part < 0)
+ return 1;
+
+ ptr = map_sysmem(hextoul(argv[3], NULL), 0);
+ blk = hextoul(argv[4], NULL);
+ cnt = hextoul(argv[5], NULL);
+
+ if (part > 0) {
+ offset = part_info.start;
+ limit = part_info.size;
+ } else {
+ /* Largest address not available in struct blk_desc. */
+ offset = 0;
+ limit = ~0;
+ }
+
+ if (cnt + blk > limit) {
+ printf("%s out of range\n", cmdtp->name);
+ unmap_sysmem(ptr);
+ return 1;
+ }
+
+ if (IS_ENABLED(CONFIG_CMD_WRITE) && !strcmp(cmdtp->name, "write"))
+ res = blk_dwrite(dev_desc, offset + blk, cnt, ptr);
+ else
+ res = blk_dread(dev_desc, offset + blk, cnt, ptr);
+ unmap_sysmem(ptr);
+
+ if (res != cnt) {
+ printf("%s error\n", cmdtp->name);
+ return 1;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_CMD_READ
+U_BOOT_CMD(
+ read, 6, 0, do_rw,
+ "Load binary data from a partition",
+ "<interface> <dev[:part|#partname]> addr blk# cnt"
+);
+#endif
+
+#ifdef CONFIG_CMD_WRITE
+U_BOOT_CMD(
+ write, 6, 0, do_rw,
+ "Store binary data to a partition",
+ "<interface> <dev[:part|#partname]> addr blk# cnt"
+);
+#endif
diff --git a/cmd/reginfo.c b/cmd/reginfo.c
new file mode 100644
index 00000000000..53b8bc41bfe
--- /dev/null
+++ b/cmd/reginfo.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000
+ * Subodh Nijsure, SkyStream Networks, snijsure@skystream.com
+ */
+
+#include <command.h>
+#include <asm/ppc.h>
+
+static int do_reginfo(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ print_reginfo();
+
+ return 0;
+}
+
+ /**************************************************/
+
+U_BOOT_CMD(
+ reginfo, 2, 1, do_reginfo,
+ "print register information",
+ ""
+);
diff --git a/cmd/regulator.c b/cmd/regulator.c
new file mode 100644
index 00000000000..8d743c8d269
--- /dev/null
+++ b/cmd/regulator.c
@@ -0,0 +1,468 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ */
+#include <command.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/uclass-internal.h>
+#include <linux/printk.h>
+#include <power/regulator.h>
+
+#define LIMIT_DEVNAME 20
+#define LIMIT_OFNAME 32
+#define LIMIT_INFO 18
+
+static struct udevice *currdev;
+
+static int failure(int ret)
+{
+ printf("Error: %d (%s)\n", ret, errno_str(ret));
+
+ return CMD_RET_FAILURE;
+}
+
+static int do_dev(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct dm_regulator_uclass_plat *uc_pdata;
+ const char *name;
+ int ret = -ENXIO;
+
+ switch (argc) {
+ case 2:
+ name = argv[1];
+ ret = regulator_get_by_platname(name, &currdev);
+ if (ret) {
+ printf("Can't get the regulator: %s!\n", name);
+ return failure(ret);
+ }
+ fallthrough;
+ case 1:
+ if (!currdev) {
+ printf("Regulator device is not set!\n\n");
+ return CMD_RET_USAGE;
+ }
+
+ uc_pdata = dev_get_uclass_plat(currdev);
+ if (!uc_pdata) {
+ printf("%s: no regulator platform data!\n", currdev->name);
+ return failure(ret);
+ }
+
+ printf("dev: %s @ %s\n", uc_pdata->name, currdev->name);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int curr_dev_and_plat(struct udevice **devp,
+ struct dm_regulator_uclass_plat **uc_pdata,
+ bool allow_type_fixed)
+{
+ *devp = NULL;
+ *uc_pdata = NULL;
+
+ if (!currdev) {
+ printf("First, set the regulator device!\n");
+ return CMD_RET_FAILURE;
+ }
+
+ *devp = currdev;
+
+ *uc_pdata = dev_get_uclass_plat(*devp);
+ if (!*uc_pdata) {
+ pr_err("Regulator: %s - missing platform data!\n", currdev->name);
+ return CMD_RET_FAILURE;
+ }
+
+ if (!allow_type_fixed && (*uc_pdata)->type == REGULATOR_TYPE_FIXED) {
+ printf("Operation not allowed for fixed regulator!\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct dm_regulator_uclass_plat *uc_pdata;
+ struct udevice *dev;
+ int ret;
+
+ printf("| %-*.*s| %-*.*s| %s\n",
+ LIMIT_DEVNAME, LIMIT_DEVNAME, "Device",
+ LIMIT_OFNAME, LIMIT_OFNAME, "regulator-name",
+ "Parent");
+
+ ret = uclass_find_first_device(UCLASS_REGULATOR, &dev);
+ if (ret)
+ return ret;
+
+ for (; dev; uclass_find_next_device(&dev)) {
+ uc_pdata = dev_get_uclass_plat(dev);
+ printf("| %-*.*s| %-*.*s| %s\n",
+ LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
+ LIMIT_OFNAME, LIMIT_OFNAME, uc_pdata->name,
+ dev->parent->name);
+ }
+
+ return ret;
+}
+
+static int constraint(const char *name, int val, const char *val_name)
+{
+ printf("%-*s", LIMIT_INFO, name);
+ if (val < 0) {
+ printf(" %s (err: %d)\n", errno_str(val), val);
+ return val;
+ }
+
+ if (val_name)
+ printf(" %d (%s)\n", val, val_name);
+ else
+ printf(" %d\n", val);
+
+ return 0;
+}
+
+static const char *get_mode_name(struct dm_regulator_mode *mode,
+ int mode_count,
+ int mode_id)
+{
+ while (mode_count--) {
+ if (mode->id == mode_id)
+ return mode->name;
+ mode++;
+ }
+
+ return NULL;
+}
+
+static int do_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ struct dm_regulator_uclass_plat *uc_pdata;
+ struct dm_regulator_mode *modes;
+ const char *parent_uc;
+ int mode_count;
+ int ret;
+ int i;
+
+ ret = curr_dev_and_plat(&dev, &uc_pdata, true);
+ if (ret)
+ return ret;
+
+ parent_uc = dev_get_uclass_name(dev->parent);
+
+ printf("%s\n%-*s %s\n%-*s %s\n%-*s %s\n%-*s %s\n%-*s\n",
+ "Regulator info:",
+ LIMIT_INFO, "* regulator-name:", uc_pdata->name,
+ LIMIT_INFO, "* device name:", dev->name,
+ LIMIT_INFO, "* parent name:", dev->parent->name,
+ LIMIT_INFO, "* parent uclass:", parent_uc,
+ LIMIT_INFO, "* constraints:");
+
+ constraint(" - min uV:", uc_pdata->min_uV, NULL);
+ constraint(" - max uV:", uc_pdata->max_uV, NULL);
+ constraint(" - min uA:", uc_pdata->min_uA, NULL);
+ constraint(" - max uA:", uc_pdata->max_uA, NULL);
+ constraint(" - always on:", uc_pdata->always_on,
+ uc_pdata->always_on ? "true" : "false");
+ constraint(" - boot on:", uc_pdata->boot_on,
+ uc_pdata->boot_on ? "true" : "false");
+
+ mode_count = regulator_mode(dev, &modes);
+ constraint("* op modes:", mode_count, NULL);
+
+ for (i = 0; i < mode_count; i++, modes++)
+ constraint(" - mode id:", modes->id, modes->name);
+
+ return CMD_RET_SUCCESS;
+}
+
+static void do_status_detail(struct udevice *dev,
+ struct dm_regulator_uclass_plat *uc_pdata)
+{
+ int current, value, mode;
+ const char *mode_name;
+ bool enabled;
+
+ printf("Regulator %s status:\n", uc_pdata->name);
+
+ enabled = regulator_get_enable(dev);
+ constraint(" * enable:", enabled, enabled ? "true" : "false");
+
+ value = regulator_get_value(dev);
+ constraint(" * value uV:", value, NULL);
+
+ current = regulator_get_current(dev);
+ constraint(" * current uA:", current, NULL);
+
+ mode = regulator_get_mode(dev);
+ mode_name = get_mode_name(uc_pdata->mode, uc_pdata->mode_count, mode);
+ constraint(" * mode id:", mode, mode_name);
+}
+
+static void do_status_line(struct udevice *dev, int status)
+{
+ struct dm_regulator_uclass_plat *pdata;
+ int current, value, mode;
+ const char *mode_name;
+ bool enabled;
+
+ pdata = dev_get_uclass_plat(dev);
+ enabled = regulator_get_enable(dev);
+ value = regulator_get_value(dev);
+ current = regulator_get_current(dev);
+ mode = regulator_get_mode(dev);
+ mode_name = get_mode_name(pdata->mode, pdata->mode_count, mode);
+ printf("%-20s %-10s ", pdata->name, enabled ? "enabled" : "disabled");
+ if (value >= 0)
+ printf("%10d ", value);
+ else
+ printf("%10s ", "-");
+ if (current >= 0)
+ printf("%10d ", current);
+ else
+ printf("%10s ", "-");
+ if (mode >= 0)
+ printf("%-10s", mode_name);
+ else
+ printf("%-10s", "-");
+ printf(" %i", status);
+ printf("\n");
+}
+
+static int do_status(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct dm_regulator_uclass_plat *uc_pdata;
+ struct udevice *dev;
+ int ret;
+
+ if (currdev && (argc < 2 || strcmp(argv[1], "-a"))) {
+ ret = curr_dev_and_plat(&dev, &uc_pdata, true);
+ if (ret)
+ return CMD_RET_FAILURE;
+ do_status_detail(dev, uc_pdata);
+ return 0;
+ }
+
+ /* Show all of them in a list, probing them as needed */
+ printf("%-20s %-10s %10s %10s %-10s %s\n", "Name", "Enabled", "uV", "mA",
+ "Mode", "Status");
+ for (ret = uclass_first_device_check(UCLASS_REGULATOR, &dev); dev;
+ ret = uclass_next_device_check(&dev))
+ do_status_line(dev, ret);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_value(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ struct dm_regulator_uclass_plat *uc_pdata;
+ int value;
+ int force;
+ int ret;
+
+ ret = curr_dev_and_plat(&dev, &uc_pdata, argc == 1);
+ if (ret)
+ return ret;
+
+ if (argc == 1) {
+ ret = regulator_get_value(dev);
+ if (ret < 0) {
+ printf("Regulator: %s - can't get the Voltage!\n",
+ uc_pdata->name);
+ return failure(ret);
+ }
+
+ printf("%d uV\n", ret);
+ return CMD_RET_SUCCESS;
+ }
+
+ if (argc == 3)
+ force = !strcmp("-f", argv[2]);
+ else
+ force = 0;
+
+ value = simple_strtoul(argv[1], NULL, 0);
+ if ((value < uc_pdata->min_uV || value > uc_pdata->max_uV) && !force) {
+ printf("Value exceeds regulator constraint limits %d..%d uV\n",
+ uc_pdata->min_uV, uc_pdata->max_uV);
+ return CMD_RET_FAILURE;
+ }
+
+ if (!force)
+ ret = regulator_set_value(dev, value);
+ else
+ ret = regulator_set_value_force(dev, value);
+ if (ret) {
+ printf("Regulator: %s - can't set the Voltage!\n",
+ uc_pdata->name);
+ return failure(ret);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_current(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ struct dm_regulator_uclass_plat *uc_pdata;
+ int current;
+ int ret;
+
+ ret = curr_dev_and_plat(&dev, &uc_pdata, argc == 1);
+ if (ret)
+ return ret;
+
+ if (argc == 1) {
+ ret = regulator_get_current(dev);
+ if (ret < 0) {
+ printf("Regulator: %s - can't get the Current!\n",
+ uc_pdata->name);
+ return failure(ret);
+ }
+
+ printf("%d uA\n", ret);
+ return CMD_RET_SUCCESS;
+ }
+
+ current = simple_strtoul(argv[1], NULL, 0);
+ if (current < uc_pdata->min_uA || current > uc_pdata->max_uA) {
+ printf("Current exceeds regulator constraint limits\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = regulator_set_current(dev, current);
+ if (ret) {
+ printf("Regulator: %s - can't set the Current!\n",
+ uc_pdata->name);
+ return failure(ret);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_mode(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ struct dm_regulator_uclass_plat *uc_pdata;
+ int mode;
+ int ret;
+
+ ret = curr_dev_and_plat(&dev, &uc_pdata, false);
+ if (ret)
+ return ret;
+
+ if (argc == 1) {
+ ret = regulator_get_mode(dev);
+ if (ret < 0) {
+ printf("Regulator: %s - can't get the operation mode!\n",
+ uc_pdata->name);
+ return failure(ret);
+ }
+
+ printf("mode id: %d\n", ret);
+ return CMD_RET_SUCCESS;
+ }
+
+ mode = simple_strtoul(argv[1], NULL, 0);
+
+ ret = regulator_set_mode(dev, mode);
+ if (ret) {
+ printf("Regulator: %s - can't set the operation mode!\n",
+ uc_pdata->name);
+ return failure(ret);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_enable(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ struct dm_regulator_uclass_plat *uc_pdata;
+ int ret;
+
+ ret = curr_dev_and_plat(&dev, &uc_pdata, true);
+ if (ret)
+ return ret;
+
+ ret = regulator_set_enable(dev, true);
+ if (ret) {
+ printf("Regulator: %s - can't enable!\n", uc_pdata->name);
+ return failure(ret);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_disable(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ struct dm_regulator_uclass_plat *uc_pdata;
+ int ret;
+
+ ret = curr_dev_and_plat(&dev, &uc_pdata, true);
+ if (ret)
+ return ret;
+
+ ret = regulator_set_enable(dev, false);
+ if (ret) {
+ printf("Regulator: %s - can't disable!\n", uc_pdata->name);
+ return failure(ret);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static struct cmd_tbl subcmd[] = {
+ U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
+ U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
+ U_BOOT_CMD_MKENT(info, 2, 1, do_info, "", ""),
+ U_BOOT_CMD_MKENT(status, 2, 1, do_status, "", ""),
+ U_BOOT_CMD_MKENT(value, 3, 1, do_value, "", ""),
+ U_BOOT_CMD_MKENT(current, 3, 1, do_current, "", ""),
+ U_BOOT_CMD_MKENT(mode, 2, 1, do_mode, "", ""),
+ U_BOOT_CMD_MKENT(enable, 1, 1, do_enable, "", ""),
+ U_BOOT_CMD_MKENT(disable, 1, 1, do_disable, "", ""),
+};
+
+static int do_regulator(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *cmd;
+
+ argc--;
+ argv++;
+
+ cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
+ if (cmd == NULL || argc > cmd->maxargs)
+ return CMD_RET_USAGE;
+
+ return cmd->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(regulator, CONFIG_SYS_MAXARGS, 1, do_regulator,
+ "uclass operations",
+ "list - list UCLASS regulator devices\n"
+ "regulator dev [regulator-name] - show/[set] operating regulator device\n"
+ "regulator info - print constraints info\n"
+ "regulator status [-a] - print operating status [for all]\n"
+ "regulator value [val] [-f] - print/[set] voltage value [uV] (force)\n"
+ "regulator current [val] - print/[set] current value [uA]\n"
+ "regulator mode [id] - print/[set] operating mode id\n"
+ "regulator enable - enable the regulator output\n"
+ "regulator disable - disable the regulator output\n"
+);
diff --git a/cmd/remoteproc.c b/cmd/remoteproc.c
new file mode 100644
index 00000000000..3c5b6a05b1a
--- /dev/null
+++ b/cmd/remoteproc.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015
+ * Texas Instruments Incorporated - https://www.ti.com/
+ */
+#include <command.h>
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <remoteproc.h>
+
+/**
+ * print_remoteproc_list() - print all the remote processor devices
+ *
+ * Return: 0 if no error, else returns appropriate error value.
+ */
+static int print_remoteproc_list(void)
+{
+ struct udevice *dev;
+ struct uclass *uc;
+ int ret;
+ char *type;
+
+ ret = uclass_get(UCLASS_REMOTEPROC, &uc);
+ if (ret) {
+ printf("Cannot find Remote processor class\n");
+ return ret;
+ }
+
+ uclass_foreach_dev(dev, uc) {
+ struct dm_rproc_uclass_pdata *uc_pdata;
+ const struct dm_rproc_ops *ops = rproc_get_ops(dev);
+
+ uc_pdata = dev_get_uclass_plat(dev);
+
+ /* Do not print if rproc is not probed */
+ if (!(dev_get_flags(dev) & DM_FLAG_ACTIVATED))
+ continue;
+
+ switch (uc_pdata->mem_type) {
+ case RPROC_INTERNAL_MEMORY_MAPPED:
+ type = "internal memory mapped";
+ break;
+ default:
+ type = "unknown";
+ break;
+ }
+ printf("%d - Name:'%s' type:'%s' supports: %s%s%s%s%s%s\n",
+ dev_seq(dev),
+ uc_pdata->name,
+ type,
+ ops->load ? "load " : "",
+ ops->start ? "start " : "",
+ ops->stop ? "stop " : "",
+ ops->reset ? "reset " : "",
+ ops->is_running ? "is_running " : "",
+ ops->ping ? "ping " : "");
+ }
+ return 0;
+}
+
+/**
+ * do_rproc_init() - do basic initialization
+ * @cmdtp: unused
+ * @flag: unused
+ * @argc: unused
+ * @argv: unused
+ *
+ * Return: 0 if no error, else returns appropriate error value.
+ */
+static int do_rproc_init(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int id;
+
+ if (rproc_is_initialized()) {
+ printf("\tRemote Processors are already initialized\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc == 1) {
+ if (!rproc_init())
+ return 0;
+ printf("Few Remote Processors failed to be initialized\n");
+ } else if (argc == 2) {
+ id = (int)dectoul(argv[1], NULL);
+ if (!rproc_dev_init(id))
+ return 0;
+ printf("Remote Processor %d failed to be initialized\n", id);
+ }
+
+ return CMD_RET_FAILURE;
+}
+
+/**
+ * do_remoteproc_list() - print list of remote proc devices.
+ * @cmdtp: unused
+ * @flag: unused
+ * @argc: unused
+ * @argv: unused
+ *
+ * Return: 0 if no error, else returns appropriate error value.
+ */
+static int do_remoteproc_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (print_remoteproc_list())
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+/**
+ * do_remoteproc_load() - Load a remote processor with binary image
+ * @cmdtp: unused
+ * @flag: unused
+ * @argc: argument count for the load function
+ * @argv: arguments for the load function
+ *
+ * Return: 0 if no error, else returns appropriate error value.
+ */
+static int do_remoteproc_load(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr, size;
+ int id, ret;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ id = (int)dectoul(argv[1], NULL);
+ addr = hextoul(argv[2], NULL);
+
+ size = hextoul(argv[3], NULL);
+
+ if (!size) {
+ printf("\t Expect some size??\n");
+ return CMD_RET_USAGE;
+ }
+
+ ret = rproc_load(id, addr, size);
+ printf("Load Remote Processor %d with data@addr=0x%08lx %lu bytes:%s\n",
+ id, addr, size, ret ? " Failed!" : " Success!");
+
+ return ret ? CMD_RET_FAILURE : 0;
+}
+
+/**
+ * do_remoteproc_wrapper() - wrapper for various rproc commands
+ * @cmdtp: unused
+ * @flag: unused
+ * @argc: argument count for the rproc command
+ * @argv: arguments for the rproc command
+ *
+ * Most of the commands just take id as a parameter andinvoke various
+ * helper routines in remote processor core. by using a set of
+ * common checks, we can reduce the amount of code used for this.
+ *
+ * Return: 0 if no error, else returns appropriate error value.
+ */
+static int do_remoteproc_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int id, ret = CMD_RET_USAGE;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ id = (int)dectoul(argv[1], NULL);
+
+ if (!strcmp(argv[0], "start")) {
+ ret = rproc_start(id);
+ } else if (!strcmp(argv[0], "stop")) {
+ ret = rproc_stop(id);
+ } else if (!strcmp(argv[0], "reset")) {
+ ret = rproc_reset(id);
+ } else if (!strcmp(argv[0], "is_running")) {
+ ret = rproc_is_running(id);
+ if (!ret) {
+ printf("Remote processor is Running\n");
+ } else if (ret == 1) {
+ printf("Remote processor is NOT Running\n");
+ ret = 0;
+ }
+ /* Else error.. */
+ } else if (!strcmp(argv[0], "ping")) {
+ ret = rproc_ping(id);
+ if (!ret) {
+ printf("Remote processor responds 'Pong'\n");
+ } else if (ret == 1) {
+ printf("No response from Remote processor\n");
+ ret = 0;
+ }
+ /* Else error.. */
+ }
+
+ if (ret < 0)
+ printf("Operation Failed with error (%d)\n", ret);
+
+ return ret ? CMD_RET_FAILURE : 0;
+}
+
+static struct cmd_tbl cmd_remoteproc_sub[] = {
+ U_BOOT_CMD_MKENT(init, 1, 1, do_rproc_init,
+ "Enumerate and initialize the remote processor(s)",
+ "id - ID of the remote processor\n"
+ "If id is not passed, initialize all the remote processors"),
+ U_BOOT_CMD_MKENT(list, 0, 1, do_remoteproc_list,
+ "list remote processors", ""),
+ U_BOOT_CMD_MKENT(load, 5, 1, do_remoteproc_load,
+ "Load remote processor with provided image",
+ "<id> [addr] [size]\n"
+ "- id: ID of the remote processor(see 'list' cmd)\n"
+ "- addr: Address in memory of the image to loadup\n"
+ "- size: Size of the image to loadup\n"),
+ U_BOOT_CMD_MKENT(start, 1, 1, do_remoteproc_wrapper,
+ "Start remote processor",
+ "id - ID of the remote processor (see 'list' cmd)\n"),
+ U_BOOT_CMD_MKENT(stop, 1, 1, do_remoteproc_wrapper,
+ "Stop remote processor",
+ "id - ID of the remote processor (see 'list' cmd)\n"),
+ U_BOOT_CMD_MKENT(reset, 1, 1, do_remoteproc_wrapper,
+ "Reset remote processor",
+ "id - ID of the remote processor (see 'list' cmd)\n"),
+ U_BOOT_CMD_MKENT(is_running, 1, 1, do_remoteproc_wrapper,
+ "Check to see if remote processor is running\n",
+ "id - ID of the remote processor (see 'list' cmd)\n"),
+ U_BOOT_CMD_MKENT(ping, 1, 1, do_remoteproc_wrapper,
+ "Ping to communicate with remote processor\n",
+ "id - ID of the remote processor (see 'list' cmd)\n"),
+};
+
+/**
+ * do_remoteproc() - (replace: short desc)
+ * @cmdtp: unused
+ * @flag: unused
+ * @argc: argument count
+ * @argv: argument list
+ *
+ * parses up the command table to invoke the correct command.
+ *
+ * Return: 0 if no error, else returns appropriate error value.
+ */
+static int do_remoteproc(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *c = NULL;
+
+ /* Strip off leading 'rproc' command argument */
+ argc--;
+ argv++;
+
+ if (argc)
+ c = find_cmd_tbl(argv[0], cmd_remoteproc_sub,
+ ARRAY_SIZE(cmd_remoteproc_sub));
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(rproc, 5, 1, do_remoteproc,
+ "Control operation of remote processors in an SoC",
+ " [init|list|load|start|stop|reset|is_running|ping]\n"
+ "\t\t Where:\n"
+ "\t\t[addr] is a memory address\n"
+ "\t\t<id> is a numerical identifier for the remote processor\n"
+ "\t\t provided by 'list' command.\n"
+ "\t\tNote: Remote processors must be initalized prior to usage\n"
+ "\t\tNote: Services are dependent on the driver capability\n"
+ "\t\t 'list' command shows the capability of each device\n"
+ "\n\tSubcommands:\n"
+ "\tinit <id> - Enumerate and initalize the remote processor.\n"
+ "\t if id is not passed, initialize all the remote prcessors\n"
+ "\tlist - list available remote processors\n"
+ "\tload <id> [addr] [size]- Load the remote processor with binary\n"
+ "\t image stored at address [addr] in memory\n"
+ "\tstart <id> - Start the remote processor(must be loaded)\n"
+ "\tstop <id> - Stop the remote processor\n"
+ "\treset <id> - Reset the remote processor\n"
+ "\tis_running <id> - Reports if the remote processor is running\n"
+ "\tping <id> - Ping the remote processor for communication\n");
diff --git a/cmd/riscv/Makefile b/cmd/riscv/Makefile
new file mode 100644
index 00000000000..1e6ac364e34
--- /dev/null
+++ b/cmd/riscv/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_CMD_EXCEPTION) += exception.o
+obj-$(CONFIG_CMD_SBI) += sbi.o
diff --git a/cmd/riscv/exception.c b/cmd/riscv/exception.c
new file mode 100644
index 00000000000..16e335327f1
--- /dev/null
+++ b/cmd/riscv/exception.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The 'exception' command can be used for testing exception handling.
+ *
+ * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <command.h>
+
+static int do_compressed(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ /* c.li a0, 0; c.li a0, 0 */
+ asm volatile (".long 0x45014501\n");
+ printf("The system supports compressed instructions.\n");
+ return CMD_RET_SUCCESS;
+}
+
+static int do_ebreak(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ asm volatile ("ebreak\n");
+ return CMD_RET_FAILURE;
+}
+
+static int do_ialign16(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ asm volatile (
+ /* jump skipping 2 bytes */
+ ".long 0x0060006f\n"
+ ".long 0x006f0000\n"
+ ".long 0x00000060\n"
+ );
+ printf("The system supports 16 bit aligned instructions.\n");
+ return CMD_RET_SUCCESS;
+}
+
+static int do_rdcycle(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ printf("cycle = 0x%lx\n", csr_read(CSR_CYCLE));
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_unaligned(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ asm volatile (
+ "auipc a1, 0\n"
+ "ori a1, a1, 3\n"
+ "lw a2, (0)(a1)\n"
+ );
+ printf("The system supports unaligned access.\n");
+ return CMD_RET_SUCCESS;
+}
+
+static int do_undefined(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ asm volatile (".word 0xffffffff\n");
+ return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl cmd_sub[] = {
+ U_BOOT_CMD_MKENT(compressed, CONFIG_SYS_MAXARGS, 1, do_compressed,
+ "", ""),
+ U_BOOT_CMD_MKENT(ebreak, CONFIG_SYS_MAXARGS, 1, do_ebreak,
+ "", ""),
+ U_BOOT_CMD_MKENT(ialign16, CONFIG_SYS_MAXARGS, 1, do_ialign16,
+ "", ""),
+ U_BOOT_CMD_MKENT(rdcycle, CONFIG_SYS_MAXARGS, 1, do_rdcycle,
+ "", ""),
+ U_BOOT_CMD_MKENT(unaligned, CONFIG_SYS_MAXARGS, 1, do_unaligned,
+ "", ""),
+ U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined,
+ "", ""),
+};
+
+U_BOOT_LONGHELP(exception,
+ "<ex>\n"
+ " The following exceptions are available:\n"
+ " compressed - compressed instruction\n"
+ " ebreak - breakpoint\n"
+ " ialign16 - 16 bit aligned instruction\n"
+ " rdcycle - read cycle CSR\n"
+ " unaligned - load address misaligned\n"
+ " undefined - illegal instruction\n");
+
+#include <exception.h>
diff --git a/cmd/riscv/sbi.c b/cmd/riscv/sbi.c
new file mode 100644
index 00000000000..b013c8c8d4e
--- /dev/null
+++ b/cmd/riscv/sbi.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The 'sbi' command displays information about the SBI implementation.
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <command.h>
+#include <asm/sbi.h>
+
+struct sbi_imp {
+ const long id;
+ const char *name;
+};
+
+struct sbi_ext {
+ const u32 id;
+ const char *name;
+};
+
+static struct sbi_imp implementations[] = {
+ { 0, "Berkeley Boot Loader (BBL)" },
+ { 1, "OpenSBI" },
+ { 2, "Xvisor" },
+ { 3, "KVM" },
+ { 4, "RustSBI" },
+ { 5, "Diosix" },
+ { 6, "Coffer" },
+ { 7, "Xen Project" },
+ { 8, "PolarFire Hart Software Services" },
+ { 9, "coreboot" },
+ { 10, "oreboot" },
+ { 11, "bhyve" },
+};
+
+static struct sbi_ext extensions[] = {
+ { SBI_EXT_0_1_SET_TIMER, "Set Timer" },
+ { SBI_EXT_0_1_CONSOLE_PUTCHAR, "Console Putchar" },
+ { SBI_EXT_0_1_CONSOLE_GETCHAR, "Console Getchar" },
+ { SBI_EXT_0_1_CLEAR_IPI, "Clear IPI" },
+ { SBI_EXT_0_1_SEND_IPI, "Send IPI" },
+ { SBI_EXT_0_1_REMOTE_FENCE_I, "Remote FENCE.I" },
+ { SBI_EXT_0_1_REMOTE_SFENCE_VMA, "Remote SFENCE.VMA" },
+ { SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID, "Remote SFENCE.VMA with ASID" },
+ { SBI_EXT_0_1_SHUTDOWN, "System Shutdown" },
+ { SBI_EXT_BASE, "SBI Base Functionality" },
+ { SBI_EXT_TIME, "Timer Extension" },
+ { SBI_EXT_IPI, "IPI Extension" },
+ { SBI_EXT_RFENCE, "RFENCE Extension" },
+ { SBI_EXT_HSM, "Hart State Management Extension" },
+ { SBI_EXT_SRST, "System Reset Extension" },
+ { SBI_EXT_PMU, "Performance Monitoring Unit Extension" },
+ { SBI_EXT_DBCN, "Debug Console Extension" },
+ { SBI_EXT_SUSP, "System Suspend Extension" },
+ { SBI_EXT_CPPC, "Collaborative Processor Performance Control Extension" },
+ { SBI_EXT_NACL, "Nested Acceleration Extension" },
+ { SBI_EXT_STA, "Steal-time Accounting Extension" },
+ { SBI_EXT_SSE, "Supervisor Software Events" },
+ { SBI_EXT_FWFT, "Firmware Features Extension" },
+ { SBI_EXT_DBTR, "Debug Triggers Extension" },
+ { SBI_EXT_MPXY, "Message Proxy Extension" },
+};
+
+static int do_sbi(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int i, impl_id;
+ long ret;
+ long mvendorid, marchid, mimpid;
+
+ ret = sbi_get_spec_version();
+ if (ret < 0) {
+ printf("No SBI 0.2+\n");
+ return CMD_RET_FAILURE;
+ }
+ printf("SBI %ld.%ld", ret >> 24, ret & 0xffffff);
+ impl_id = sbi_get_impl_id();
+ if (impl_id >= 0) {
+ for (i = 0; i < ARRAY_SIZE(implementations); ++i) {
+ if (impl_id == implementations[i].id) {
+ long vers;
+
+ printf("\n%s ", implementations[i].name);
+ ret = sbi_get_impl_version(&vers);
+ if (ret < 0)
+ break;
+ switch (impl_id) {
+ case 1: /* OpenSBI */
+ case 8: /* PolarFire Hart Software Services */
+ printf("%ld.%ld",
+ vers >> 16, vers & 0xffff);
+ break;
+ case 3: /* KVM */
+ case 4: /* RustSBI */
+ printf("%ld.%ld.%ld",
+ vers >> 16,
+ (vers >> 8) & 0xff,
+ vers & 0xff);
+ break;
+ default:
+ printf("0x%lx", vers);
+ break;
+ }
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(implementations))
+ printf("\nUnknown implementation ID 0x%x", impl_id);
+ }
+ printf("\nMachine:\n");
+ ret = sbi_get_mvendorid(&mvendorid);
+ if (!ret)
+ printf(" Vendor ID %lx\n", mvendorid);
+ ret = sbi_get_marchid(&marchid);
+ if (!ret)
+ printf(" Architecture ID %lx\n", marchid);
+ ret = sbi_get_mimpid(&mimpid);
+ if (!ret)
+ printf(" Implementation ID %lx\n", mimpid);
+ printf("Extensions:\n");
+ for (i = 0; i < ARRAY_SIZE(extensions); ++i) {
+ ret = sbi_probe_extension(extensions[i].id);
+ if (ret > 0)
+ printf(" %s\n", extensions[i].name);
+ }
+ return 0;
+}
+
+U_BOOT_LONGHELP(sbi,
+ "- display SBI spec version, implementation, and available extensions");
+
+U_BOOT_CMD_COMPLETE(
+ sbi, 1, 0, do_sbi,
+ "display SBI information",
+ sbi_help_text, NULL
+);
diff --git a/cmd/rkmtd.c b/cmd/rkmtd.c
new file mode 100644
index 00000000000..a870c119110
--- /dev/null
+++ b/cmd/rkmtd.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *
+ * Driver interface derived from:
+ * /cmd/host.c
+ * Copyright (c) 2012, Google Inc.
+ *
+ * Copyright (C) 2023 Johan Jonker <jbx6244@gmail.com>
+ */
+
+#include <blk.h>
+#include <command.h>
+#include <dm.h>
+#include <rkmtd.h>
+#include <stdio.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+
+static int do_rkmtd_bind(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ const char *label;
+ int ret;
+
+ argc--;
+ argv++;
+
+ if (argc < 1)
+ return CMD_RET_USAGE;
+
+ if (argc > 1)
+ return CMD_RET_USAGE;
+
+ label = argv[0];
+ ret = rkmtd_create_attach_mtd(label, &dev);
+ if (ret) {
+ printf("Cannot create device / bind mtd\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static struct udevice *parse_rkmtd_label(const char *label)
+{
+ struct udevice *dev;
+
+ dev = rkmtd_find_by_label(label);
+ if (!dev) {
+ int devnum;
+ char *ep;
+
+ devnum = hextoul(label, &ep);
+ if (*ep ||
+ uclass_find_device_by_seq(UCLASS_RKMTD, devnum, &dev)) {
+ printf("No such device '%s'\n", label);
+ return NULL;
+ }
+ }
+
+ return dev;
+}
+
+static int do_rkmtd_unbind(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ const char *label;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ label = argv[1];
+ dev = parse_rkmtd_label(label);
+ if (!dev)
+ return CMD_RET_FAILURE;
+
+ ret = rkmtd_detach(dev);
+ if (ret) {
+ printf("Cannot detach mtd\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = device_unbind(dev);
+ if (ret) {
+ printf("Cannot unbind device '%s'\n", dev->name);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static void show_rkmtd_dev(struct udevice *dev)
+{
+ struct rkmtd_dev *plat = dev_get_plat(dev);
+ struct blk_desc *desc;
+ struct udevice *blk;
+ int ret;
+
+ printf("%3d ", dev_seq(dev));
+
+ ret = blk_get_from_parent(dev, &blk);
+ if (ret)
+ return;
+
+ desc = dev_get_uclass_plat(blk);
+ printf("%12lu %-15s\n", (unsigned long)desc->lba, plat->label);
+}
+
+static int do_rkmtd_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+
+ if (argc < 1)
+ return CMD_RET_USAGE;
+
+ dev = NULL;
+ if (argc >= 2) {
+ dev = parse_rkmtd_label(argv[1]);
+ if (!dev)
+ return CMD_RET_FAILURE;
+ }
+
+ printf("%3s %12s %-15s\n", "dev", "blocks", "label");
+ if (dev) {
+ show_rkmtd_dev(dev);
+ } else {
+ struct uclass *uc;
+
+ uclass_id_foreach_dev(UCLASS_RKMTD, dev, uc)
+ show_rkmtd_dev(dev);
+ }
+
+ return 0;
+}
+
+static int do_rkmtd_dev(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ const char *label;
+
+ if (argc < 1 || argc > 3)
+ return CMD_RET_USAGE;
+
+ if (argc == 1) {
+ struct rkmtd_dev *plat;
+
+ dev = rkmtd_get_cur_dev();
+ if (!dev) {
+ printf("No current rkmtd device\n");
+ return CMD_RET_FAILURE;
+ }
+ plat = dev_get_plat(dev);
+ printf("Current rkmtd device: %d: %s\n", dev_seq(dev),
+ plat->label);
+ return 0;
+ }
+
+ label = argv[1];
+ dev = parse_rkmtd_label(argv[1]);
+ if (!dev)
+ return CMD_RET_FAILURE;
+
+ rkmtd_set_cur_dev(dev);
+
+ return 0;
+}
+
+static struct cmd_tbl cmd_rkmtd_sub[] = {
+ U_BOOT_CMD_MKENT(bind, 4, 0, do_rkmtd_bind, "", ""),
+ U_BOOT_CMD_MKENT(unbind, 4, 0, do_rkmtd_unbind, "", ""),
+ U_BOOT_CMD_MKENT(info, 3, 0, do_rkmtd_info, "", ""),
+ U_BOOT_CMD_MKENT(dev, 0, 1, do_rkmtd_dev, "", ""),
+};
+
+static int do_rkmtd(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *c;
+
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], cmd_rkmtd_sub, ARRAY_SIZE(cmd_rkmtd_sub));
+
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+ else
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+ rkmtd, 8, 1, do_rkmtd,
+ "Rockchip MTD sub-system",
+ "bind <label> - bind RKMTD device\n"
+ "rkmtd unbind <label> - unbind RKMTD device\n"
+ "rkmtd info [<label>] - show all available RKMTD devices\n"
+ "rkmtd dev [<label>] - show or set current RKMTD device\n"
+);
diff --git a/cmd/rng.c b/cmd/rng.c
new file mode 100644
index 00000000000..4d91094a8ec
--- /dev/null
+++ b/cmd/rng.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The 'rng' command prints bytes from the hardware random number generator.
+ *
+ * Copyright (c) 2019, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+#include <command.h>
+#include <dm.h>
+#include <hexdump.h>
+#include <malloc.h>
+#include <rng.h>
+
+static int do_rng(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ size_t n;
+ u8 buf[64];
+ int devnum;
+ struct udevice *dev;
+ int ret = CMD_RET_SUCCESS, err;
+
+ if (argc == 2 && !strcmp(argv[1], "list")) {
+ int idx = 0;
+
+ uclass_foreach_dev_probe(UCLASS_RNG, dev) {
+ idx++;
+ printf("RNG #%d - %s\n", dev->seq_, dev->name);
+ }
+
+ if (!idx) {
+ log_err("No RNG device\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+ }
+
+ switch (argc) {
+ case 1:
+ devnum = 0;
+ n = 0x40;
+ break;
+ case 2:
+ devnum = hextoul(argv[1], NULL);
+ n = 0x40;
+ break;
+ case 3:
+ devnum = hextoul(argv[1], NULL);
+ n = hextoul(argv[2], NULL);
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ if (uclass_get_device_by_seq(UCLASS_RNG, devnum, &dev) || !dev) {
+ printf("No RNG device\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (!n)
+ return 0;
+
+ n = min(n, sizeof(buf));
+
+ err = dm_rng_read(dev, buf, n);
+ if (err) {
+ puts(err == -EINTR ? "Abort\n" : "Reading RNG failed\n");
+ ret = CMD_RET_FAILURE;
+ } else {
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, n);
+ }
+
+ return ret;
+}
+
+U_BOOT_CMD(
+ rng, 3, 0, do_rng,
+ "print bytes from the hardware random number generator",
+ "list - list all probed rng devices\n"
+ "rng [dev [n]] - print n random bytes (max 64) read from dev\n"
+);
diff --git a/cmd/rockusb.c b/cmd/rockusb.c
new file mode 100644
index 00000000000..48497aa8764
--- /dev/null
+++ b/cmd/rockusb.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Eddie Cai <eddie.cai.linux@gmail.com>
+ */
+
+#include <command.h>
+#include <console.h>
+#include <g_dnl.h>
+#include <usb.h>
+#include <asm/arch-rockchip/f_rockusb.h>
+
+static int do_rockusb(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int controller_index, dev_index;
+ char *usb_controller;
+ struct udevice *udc;
+ char *devtype;
+ char *devnum;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ usb_controller = argv[1];
+ controller_index = simple_strtoul(usb_controller, NULL, 0);
+
+ if (argc >= 4) {
+ devtype = argv[2];
+ devnum = argv[3];
+ } else {
+ return CMD_RET_USAGE;
+ }
+ dev_index = simple_strtoul(devnum, NULL, 0);
+ rockusb_dev_init(devtype, dev_index);
+
+ ret = udc_device_get_by_index(controller_index, &udc);
+ if (ret) {
+ printf("USB init failed: %d\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ g_dnl_clear_detach();
+ ret = g_dnl_register("usb_dnl_rockusb");
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ if (!g_dnl_board_usb_cable_connected()) {
+ puts("\rUSB cable not detected, Command exit.\n");
+ ret = CMD_RET_FAILURE;
+ goto exit;
+ }
+
+ while (1) {
+ if (g_dnl_detach())
+ break;
+ if (ctrlc())
+ break;
+ dm_usb_gadget_handle_interrupts(udc);
+ }
+ ret = CMD_RET_SUCCESS;
+
+exit:
+ g_dnl_unregister();
+ g_dnl_clear_detach();
+ udc_device_put(udc);
+
+ return ret;
+}
+
+U_BOOT_CMD(rockusb, 4, 1, do_rockusb,
+ "use the rockusb protocol",
+ "<USB_controller> <devtype> <dev[:part]> e.g. rockusb 0 mmc 0\n"
+);
diff --git a/cmd/rtc.c b/cmd/rtc.c
new file mode 100644
index 00000000000..a931fd9d54f
--- /dev/null
+++ b/cmd/rtc.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <command.h>
+#include <display_options.h>
+#include <dm.h>
+#include <hexdump.h>
+#include <i2c.h>
+#include <mapmem.h>
+#include <rtc.h>
+
+#define MAX_RTC_BYTES 32
+
+static int do_rtc_read(struct udevice *dev, int argc, char * const argv[])
+{
+ u8 buf[MAX_RTC_BYTES];
+ int reg, len, ret, r;
+
+ if (argc < 2 || argc > 3)
+ return CMD_RET_USAGE;
+
+ reg = hextoul(argv[0], NULL);
+ len = hextoul(argv[1], NULL);
+
+ if (argc == 3) {
+ u8 *addr;
+
+ addr = map_sysmem(hextoul(argv[2], NULL), len);
+ ret = dm_rtc_read(dev, reg, addr, len);
+ unmap_sysmem(addr);
+ if (ret) {
+ printf("dm_rtc_read() failed: %d\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ return CMD_RET_SUCCESS;
+ }
+
+ while (len) {
+ r = min_t(int, len, sizeof(buf));
+ ret = dm_rtc_read(dev, reg, buf, r);
+ if (ret) {
+ printf("dm_rtc_read() failed: %d\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ print_buffer(reg, buf, 1, r, 0);
+ len -= r;
+ reg += r;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_rtc_write(struct udevice *dev, int argc, char * const argv[])
+{
+ u8 buf[MAX_RTC_BYTES];
+ int reg, len, ret;
+ const char *s;
+ int slen;
+
+ if (argc < 2 || argc > 3)
+ return CMD_RET_USAGE;
+
+ reg = hextoul(argv[0], NULL);
+
+ if (argc == 3) {
+ u8 *addr;
+
+ len = hextoul(argv[1], NULL);
+ addr = map_sysmem(hextoul(argv[2], NULL), len);
+ ret = dm_rtc_write(dev, reg, addr, len);
+ unmap_sysmem(addr);
+ if (ret) {
+ printf("dm_rtc_write() failed: %d\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ return CMD_RET_SUCCESS;
+ }
+
+ s = argv[1];
+ slen = strlen(s);
+
+ if (slen % 2) {
+ printf("invalid hex string\n");
+ return CMD_RET_FAILURE;
+ }
+
+ while (slen) {
+ len = min_t(int, slen / 2, sizeof(buf));
+ if (hex2bin(buf, s, len)) {
+ printf("invalid hex string\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = dm_rtc_write(dev, reg, buf, len);
+ if (ret) {
+ printf("dm_rtc_write() failed: %d\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ s += 2 * len;
+ slen -= 2 * len;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+int do_rtc(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ static int curr_rtc;
+ struct udevice *dev;
+ int ret, idx;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ argc--;
+ argv++;
+
+ if (!strcmp(argv[0], "list")) {
+ struct uclass *uc;
+
+ idx = 0;
+ uclass_id_foreach_dev(UCLASS_RTC, dev, uc) {
+ printf("RTC #%d - %s\n", idx++, dev->name);
+ }
+ if (!idx) {
+ printf("*** no RTC devices available ***\n");
+ return CMD_RET_FAILURE;
+ }
+ return CMD_RET_SUCCESS;
+ }
+
+ idx = curr_rtc;
+ if (!strcmp(argv[0], "dev") && argc >= 2)
+ idx = dectoul(argv[1], NULL);
+
+ ret = uclass_get_device(UCLASS_RTC, idx, &dev);
+ if (ret) {
+ printf("Cannot find RTC #%d: err=%d\n", idx, ret);
+ return CMD_RET_FAILURE;
+ }
+
+ if (!strcmp(argv[0], "dev")) {
+ /* Show the existing or newly selected RTC */
+ if (argc >= 2)
+ curr_rtc = idx;
+ printf("RTC #%d - %s\n", idx, dev->name);
+ return CMD_RET_SUCCESS;
+ }
+
+ if (!strcmp(argv[0], "read"))
+ return do_rtc_read(dev, argc - 1, argv + 1);
+
+ if (!strcmp(argv[0], "write"))
+ return do_rtc_write(dev, argc - 1, argv + 1);
+
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+ rtc, 5, 0, do_rtc,
+ "RTC subsystem",
+ "list - show available rtc devices\n"
+ "rtc dev [n] - show or set current rtc device\n"
+ "rtc read <reg> <count> - read and display 8-bit registers starting at <reg>\n"
+ "rtc read <reg> <count> <addr> - read 8-bit registers starting at <reg> to memory <addr>\n"
+ "rtc write <reg> <hexstring> - write 8-bit registers starting at <reg>\n"
+ "rtc write <reg> <count> <addr> - write from memory <addr> to 8-bit registers starting at <reg>\n"
+);
diff --git a/cmd/sandbox/Makefile b/cmd/sandbox/Makefile
new file mode 100644
index 00000000000..24df023ece9
--- /dev/null
+++ b/cmd/sandbox/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_CMD_EXCEPTION) += exception.o
diff --git a/cmd/sandbox/exception.c b/cmd/sandbox/exception.c
new file mode 100644
index 00000000000..e015acf60e2
--- /dev/null
+++ b/cmd/sandbox/exception.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The 'exception' command can be used for testing exception handling.
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <command.h>
+#include <env.h>
+
+static int do_sigsegv(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u8 *ptr = NULL;
+
+ *ptr = 0;
+ return CMD_RET_FAILURE;
+}
+
+static int do_undefined(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+#ifdef __powerpc__
+ asm volatile (".long 0xffffffff\n");
+#else
+ asm volatile (".word 0xffff\n");
+#endif
+ return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl cmd_sub[] = {
+ U_BOOT_CMD_MKENT(sigsegv, CONFIG_SYS_MAXARGS, 1, do_sigsegv,
+ "", ""),
+ U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined,
+ "", ""),
+};
+
+U_BOOT_LONGHELP(exception,
+ "<ex>\n"
+ " The following exceptions are available:\n"
+ " undefined - undefined instruction\n"
+ " sigsegv - illegal memory access\n");
+
+#include <exception.h>
diff --git a/cmd/sata.c b/cmd/sata.c
new file mode 100644
index 00000000000..8b923f9378b
--- /dev/null
+++ b/cmd/sata.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2000-2005, DENX Software Engineering
+ * Wolfgang Denk <wd@denx.de>
+ * Copyright (C) Procsys. All rights reserved.
+ * Mushtaq Khan <mushtaq_k@procsys.com>
+ * <mushtaqk_921@yahoo.co.in>
+ * Copyright (C) 2008 Freescale Semiconductor, Inc.
+ * Dave Liu <daveliu@freescale.com>
+ */
+
+#include <ahci.h>
+#include <blk.h>
+#include <dm.h>
+#include <command.h>
+#include <part.h>
+#include <sata.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+
+static int sata_curr_device = -1;
+
+int sata_remove(int devnum)
+{
+#ifdef CONFIG_AHCI
+ struct udevice *dev;
+ int rc;
+
+ blk_unbind_all(UCLASS_AHCI);
+
+ rc = uclass_find_device(UCLASS_AHCI, devnum, &dev);
+ if (!rc && !dev)
+ rc = uclass_find_first_device(UCLASS_AHCI, &dev);
+ if (rc || !dev) {
+ printf("Cannot find SATA device %d (err=%d)\n", devnum, rc);
+ return CMD_RET_FAILURE;
+ }
+
+ rc = device_remove(dev, DM_REMOVE_NORMAL);
+ if (rc) {
+ printf("Cannot remove SATA device '%s' (err=%d)\n", dev->name,
+ rc);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+#else
+ return sata_stop();
+#endif
+}
+
+int sata_probe(int devnum)
+{
+#ifdef CONFIG_AHCI
+ struct udevice *dev;
+ int rc;
+
+ rc = uclass_get_device(UCLASS_AHCI, devnum, &dev);
+ if (rc)
+ rc = uclass_find_first_device(UCLASS_AHCI, &dev);
+ if (rc) {
+ printf("Cannot probe SATA device %d (err=%d)\n", devnum, rc);
+ return CMD_RET_FAILURE;
+ }
+ if (!dev) {
+ printf("No SATA device found!\n");
+ return CMD_RET_FAILURE;
+ }
+ rc = sata_scan(dev);
+ if (rc) {
+ printf("Cannot scan SATA device %d (err=%d)\n", devnum, rc);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+#else
+ return sata_initialize() < 0 ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+#endif
+}
+
+static int do_sata(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int rc = 0;
+
+ if (argc >= 2) {
+ int devnum = 0;
+
+ if (argc == 3)
+ devnum = (int)dectoul(argv[2], NULL);
+ if (!strcmp(argv[1], "stop"))
+ return sata_remove(devnum);
+
+ if (!strcmp(argv[1], "init")) {
+ if (sata_curr_device != -1) {
+ rc = sata_remove(devnum);
+ if (rc)
+ return rc;
+ }
+
+ return sata_probe(devnum);
+ }
+ }
+
+ /* If the user has not yet run `sata init`, do it now */
+ if (sata_curr_device == -1) {
+ rc = sata_probe(0);
+ if (rc)
+ return rc;
+ sata_curr_device = 0;
+ }
+
+ return blk_common_cmd(argc, argv, UCLASS_AHCI, &sata_curr_device);
+}
+
+U_BOOT_CMD(
+ sata, 5, 1, do_sata,
+ "SATA sub system",
+ "init - init SATA sub system\n"
+ "sata stop [dev] - disable SATA sub system or device\n"
+ "sata info - show available SATA devices\n"
+ "sata device [dev] - show or set current device\n"
+ "sata part [dev] - print partition table\n"
+ "sata read addr blk# cnt\n"
+ "sata write addr blk# cnt"
+);
diff --git a/cmd/sb.c b/cmd/sb.c
new file mode 100644
index 00000000000..79f3fb0aacd
--- /dev/null
+++ b/cmd/sb.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018, Google Inc.
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <spl.h>
+#include <asm/cpu.h>
+#include <asm/global_data.h>
+#include <asm/state.h>
+
+static int do_sb_handoff(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+#if CONFIG_IS_ENABLED(HANDOFF)
+ if (gd->spl_handoff)
+ printf("SPL handoff magic %lx\n", gd->spl_handoff->arch.magic);
+ else
+ printf("SPL handoff info not received\n");
+
+ return 0;
+#else
+ printf("Command not supported\n");
+
+ return CMD_RET_USAGE;
+#endif
+}
+
+static int do_sb_map(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ sandbox_map_list();
+
+ return 0;
+}
+
+static int do_sb_state(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct sandbox_state *state;
+
+ state = state_get_current();
+ state_show(state);
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(sb,
+ "handoff - Show handoff data received from SPL\n"
+ "sb map - Show mapped memory\n"
+ "sb state - Show sandbox state");
+
+U_BOOT_CMD_WITH_SUBCMDS(sb, "Sandbox status commands", sb_help_text,
+ U_BOOT_SUBCMD_MKENT(handoff, 1, 1, do_sb_handoff),
+ U_BOOT_SUBCMD_MKENT(map, 1, 1, do_sb_map),
+ U_BOOT_SUBCMD_MKENT(state, 1, 1, do_sb_state));
diff --git a/cmd/scmi.c b/cmd/scmi.c
new file mode 100644
index 00000000000..d0498b816aa
--- /dev/null
+++ b/cmd/scmi.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SCMI (System Control and Management Interface) utility command
+ *
+ * Copyright (c) 2023 Linaro Limited
+ * Author: AKASHI Takahiro
+ */
+
+#include <command.h>
+#include <exports.h>
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <stdlib.h>
+#include <asm/types.h>
+#include <dm/device.h>
+#include <dm/uclass.h> /* uclass_get_device */
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+
+struct {
+ enum scmi_std_protocol id;
+ const char *name;
+} protocol_name[] = {
+ {SCMI_PROTOCOL_ID_BASE, "Base"},
+ {SCMI_PROTOCOL_ID_POWER_DOMAIN, "Power domain management"},
+ {SCMI_PROTOCOL_ID_SYSTEM, "System power management"},
+ {SCMI_PROTOCOL_ID_PERF, "Performance domain management"},
+ {SCMI_PROTOCOL_ID_CLOCK, "Clock management"},
+ {SCMI_PROTOCOL_ID_SENSOR, "Sensor management"},
+ {SCMI_PROTOCOL_ID_RESET_DOMAIN, "Reset domain management"},
+ {SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, "Voltage domain management"},
+ {SCMI_PROTOCOL_ID_PINCTRL, "Pin control management"},
+};
+
+/**
+ * get_agent() - get SCMI agent device
+ *
+ * Return: Pointer to SCMI agent device on success, NULL on failure
+ */
+static struct udevice *get_agent(void)
+{
+ struct udevice *agent;
+
+ if (uclass_get_device(UCLASS_SCMI_AGENT, 0, &agent)) {
+ printf("Cannot find any SCMI agent\n");
+ return NULL;
+ }
+
+ return agent;
+}
+
+/**
+ * get_base_proto() - get SCMI base protocol device
+ * @agent: SCMI agent device
+ *
+ * Return: Pointer to SCMI base protocol device on success,
+ * NULL on failure
+ */
+static struct udevice *get_base_proto(struct udevice *agent)
+{
+ struct udevice *base_proto;
+
+ if (!agent) {
+ agent = get_agent();
+ if (!agent)
+ return NULL;
+ }
+
+ base_proto = scmi_get_protocol(agent, SCMI_PROTOCOL_ID_BASE);
+ if (!base_proto) {
+ printf("SCMI base protocol not found\n");
+ return NULL;
+ }
+
+ return base_proto;
+}
+
+/**
+ * get_proto_name() - get the name of SCMI protocol
+ *
+ * @id: SCMI Protocol ID
+ *
+ * Get the printable name of the protocol, @id
+ *
+ * Return: Name string on success, NULL on failure
+ */
+static const char *get_proto_name(enum scmi_std_protocol id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(protocol_name); i++)
+ if (id == protocol_name[i].id)
+ return protocol_name[i].name;
+
+ return NULL;
+}
+
+/**
+ * do_scmi_info() - get the information of SCMI services
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ *
+ * Get the information of SCMI services using various interfaces
+ * provided by the Base protocol.
+ *
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ */
+static int do_scmi_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ struct udevice *agent, *base_proto;
+ u32 agent_id, num_protocols;
+ u8 *agent_name, *protocols;
+ int i, ret;
+
+ if (argc != 1)
+ return CMD_RET_USAGE;
+
+ agent = get_agent();
+ if (!agent)
+ return CMD_RET_FAILURE;
+ base_proto = get_base_proto(agent);
+ if (!base_proto)
+ return CMD_RET_FAILURE;
+
+ printf("SCMI device: %s\n", agent->name);
+ printf(" protocol version: 0x%x\n", scmi_version(agent));
+ printf(" # of agents: %d\n", scmi_num_agents(agent));
+ for (i = 0; i < scmi_num_agents(agent); i++) {
+ ret = scmi_base_discover_agent(base_proto, i, &agent_id,
+ &agent_name);
+ if (ret) {
+ if (ret != -EOPNOTSUPP)
+ printf("base_discover_agent() failed for id: %d (%d)\n",
+ i, ret);
+ break;
+ }
+ printf(" %c%2d: %s\n", i == scmi_agent_id(agent) ? '>' : ' ',
+ i, agent_name);
+ free(agent_name);
+ }
+ printf(" # of protocols: %d\n", scmi_num_protocols(agent));
+ num_protocols = scmi_num_protocols(agent);
+ protocols = scmi_protocols(agent);
+ if (protocols)
+ for (i = 0; i < num_protocols; i++)
+ printf(" %s\n", get_proto_name(protocols[i]));
+ printf(" vendor: %s\n", scmi_vendor(agent));
+ printf(" sub vendor: %s\n", scmi_sub_vendor(agent));
+ printf(" impl version: 0x%x\n", scmi_impl_version(agent));
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * do_scmi_set_dev() - set access permission to device
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ *
+ * Set access permission to device with SCMI_BASE_SET_DEVICE_PERMISSIONS
+ *
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ */
+static int do_scmi_set_dev(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ u32 agent_id, device_id, flags, attributes;
+ char *end;
+ struct udevice *base_proto;
+ int ret;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ agent_id = simple_strtoul(argv[1], &end, 16);
+ if (*end != '\0')
+ return CMD_RET_USAGE;
+
+ device_id = simple_strtoul(argv[2], &end, 16);
+ if (*end != '\0')
+ return CMD_RET_USAGE;
+
+ flags = simple_strtoul(argv[3], &end, 16);
+ if (*end != '\0')
+ return CMD_RET_USAGE;
+
+ base_proto = get_base_proto(NULL);
+ if (!base_proto)
+ return CMD_RET_FAILURE;
+
+ ret = scmi_base_protocol_message_attrs(base_proto,
+ SCMI_BASE_SET_DEVICE_PERMISSIONS,
+ &attributes);
+ if (ret) {
+ printf("This operation is not supported\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = scmi_base_set_device_permissions(base_proto, agent_id,
+ device_id, flags);
+ if (ret) {
+ printf("%s access to device:%u failed (%d)\n",
+ flags ? "Allowing" : "Denying", device_id, ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * do_scmi_set_proto() - set protocol permission to device
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ *
+ * Set protocol permission to device with SCMI_BASE_SET_PROTOCOL_PERMISSIONS
+ *
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ */
+static int do_scmi_set_proto(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ u32 agent_id, device_id, protocol_id, flags, attributes;
+ char *end;
+ struct udevice *base_proto;
+ int ret;
+
+ if (argc != 5)
+ return CMD_RET_USAGE;
+
+ agent_id = simple_strtoul(argv[1], &end, 16);
+ if (*end != '\0')
+ return CMD_RET_USAGE;
+
+ device_id = simple_strtoul(argv[2], &end, 16);
+ if (*end != '\0')
+ return CMD_RET_USAGE;
+
+ protocol_id = simple_strtoul(argv[3], &end, 16);
+ if (*end != '\0')
+ return CMD_RET_USAGE;
+
+ flags = simple_strtoul(argv[4], &end, 16);
+ if (*end != '\0')
+ return CMD_RET_USAGE;
+
+ base_proto = get_base_proto(NULL);
+ if (!base_proto)
+ return CMD_RET_FAILURE;
+
+ ret = scmi_base_protocol_message_attrs(base_proto,
+ SCMI_BASE_SET_PROTOCOL_PERMISSIONS,
+ &attributes);
+ if (ret) {
+ printf("This operation is not supported\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = scmi_base_set_protocol_permissions(base_proto, agent_id,
+ device_id, protocol_id,
+ flags);
+ if (ret) {
+ printf("%s access to protocol:0x%x on device:%u failed (%d)\n",
+ flags ? "Allowing" : "Denying", protocol_id, device_id,
+ ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * do_scmi_reset() - reset platform resource settings
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ *
+ * Reset platform resource settings with BASE_RESET_AGENT_CONFIGURATION
+ *
+ * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ */
+static int do_scmi_reset(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ u32 agent_id, flags, attributes;
+ char *end;
+ struct udevice *base_proto;
+ int ret;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ agent_id = simple_strtoul(argv[1], &end, 16);
+ if (*end != '\0')
+ return CMD_RET_USAGE;
+
+ flags = simple_strtoul(argv[2], &end, 16);
+ if (*end != '\0')
+ return CMD_RET_USAGE;
+
+ base_proto = get_base_proto(NULL);
+ if (!base_proto)
+ return CMD_RET_FAILURE;
+
+ ret = scmi_base_protocol_message_attrs(base_proto,
+ SCMI_BASE_RESET_AGENT_CONFIGURATION,
+ &attributes);
+ if (ret) {
+ printf("Reset is not supported\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = scmi_base_reset_agent_configuration(base_proto, agent_id, flags);
+ if (ret) {
+ printf("Reset failed (%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static struct cmd_tbl cmd_scmi_sub[] = {
+ U_BOOT_CMD_MKENT(info, CONFIG_SYS_MAXARGS, 1,
+ do_scmi_info, "", ""),
+ U_BOOT_CMD_MKENT(perm_dev, CONFIG_SYS_MAXARGS, 1,
+ do_scmi_set_dev, "", ""),
+ U_BOOT_CMD_MKENT(perm_proto, CONFIG_SYS_MAXARGS, 1,
+ do_scmi_set_proto, "", ""),
+ U_BOOT_CMD_MKENT(reset, CONFIG_SYS_MAXARGS, 1,
+ do_scmi_reset, "", ""),
+};
+
+/**
+ * do_scmi() - SCMI utility
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ *
+ * Provide user interfaces to SCMI protocols.
+ *
+ * Return: CMD_RET_SUCCESS on success,
+ * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ */
+static int do_scmi(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ argc--; argv++;
+
+ cp = find_cmd_tbl(argv[0], cmd_scmi_sub, ARRAY_SIZE(cmd_scmi_sub));
+ if (!cp)
+ return CMD_RET_USAGE;
+
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_LONGHELP(scmi,
+ " - SCMI utility\n"
+ " info - get the info of SCMI services\n"
+ " perm_dev <agent-id in hex> <device-id in hex> <flags in hex>\n"
+ " - set access permission to device\n"
+ " perm_proto <agent-id in hex> <device-id in hex> <protocol-id in hex> <flags in hex>\n"
+ " - set protocol permission to device\n"
+ " reset <agent-id in hex> <flags in hex>\n"
+ " - reset platform resource settings\n");
+
+U_BOOT_CMD(scmi, CONFIG_SYS_MAXARGS, 0, do_scmi, "SCMI utility",
+ scmi_help_text);
diff --git a/cmd/scp03.c b/cmd/scp03.c
new file mode 100644
index 00000000000..9c749d19af8
--- /dev/null
+++ b/cmd/scp03.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2021, Foundries.IO
+ *
+ */
+
+#include <command.h>
+#include <env.h>
+#include <scp03.h>
+
+int do_scp03_enable(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (argc != 1)
+ return CMD_RET_USAGE;
+
+ if (tee_enable_scp03()) {
+ printf("TEE failed to enable SCP03\n");
+ return CMD_RET_FAILURE;
+ }
+
+ printf("SCP03 is enabled\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+int do_scp03_provision(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (argc != 1)
+ return CMD_RET_USAGE;
+
+ if (tee_provision_scp03()) {
+ printf("TEE failed to provision SCP03 keys\n");
+ return CMD_RET_FAILURE;
+ }
+
+ printf("SCP03 is provisioned\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_LONGHELP(scp03,
+ "provides a command to enable SCP03 and provision the SCP03 keys\n"
+ " enable - enable SCP03 on the TEE\n"
+ " provision - provision SCP03 on the TEE\n");
+
+U_BOOT_CMD_WITH_SUBCMDS(scp03, "Secure Channel Protocol 03 control",
+ scp03_help_text,
+ U_BOOT_SUBCMD_MKENT(enable, 1, 1, do_scp03_enable),
+ U_BOOT_SUBCMD_MKENT(provision, 1, 1, do_scp03_provision));
diff --git a/cmd/scsi.c b/cmd/scsi.c
new file mode 100644
index 00000000000..9f7613424e5
--- /dev/null
+++ b/cmd/scsi.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2001
+ * Denis Peter, MPL AG Switzerland
+ */
+
+/*
+ * SCSI support.
+ */
+#include <blk.h>
+#include <command.h>
+#include <scsi.h>
+
+static int scsi_curr_dev; /* current device */
+
+/*
+ * scsi boot command intepreter. Derived from diskboot
+ */
+static int do_scsiboot(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return common_diskboot(cmdtp, "scsi", argc, argv);
+}
+
+/*
+ * scsi command intepreter
+ */
+static int do_scsi(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+
+ if (argc == 2) {
+ if (strncmp(argv[1], "res", 3) == 0) {
+ printf("\nReset SCSI\n");
+ ret = scsi_scan(true);
+ if (ret)
+ return CMD_RET_FAILURE;
+ return ret;
+ }
+ if (strncmp(argv[1], "scan", 4) == 0) {
+ ret = scsi_scan(true);
+ if (ret)
+ return CMD_RET_FAILURE;
+ return ret;
+ }
+ }
+
+ return blk_common_cmd(argc, argv, UCLASS_SCSI, &scsi_curr_dev);
+}
+
+U_BOOT_CMD(
+ scsi, 5, 1, do_scsi,
+ "SCSI sub-system",
+ "reset - reset SCSI controller\n"
+ "scsi info - show available SCSI devices\n"
+ "scsi scan - (re-)scan SCSI bus\n"
+ "scsi device [dev] - show or set current device\n"
+ "scsi part [dev] - print partition table of one or all SCSI devices\n"
+ "scsi read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n"
+ " to memory address `addr'\n"
+ "scsi write addr blk# cnt - write `cnt' blocks starting at block\n"
+ " `blk#' from memory address `addr'\n"
+ "scsi erase blk# cnt - erase `cnt' blocks starting at block `blk#'"
+);
+
+U_BOOT_CMD(
+ scsiboot, 3, 1, do_scsiboot,
+ "boot from SCSI device",
+ "loadAddr dev:part"
+);
diff --git a/cmd/seama.c b/cmd/seama.c
new file mode 100644
index 00000000000..d6287978090
--- /dev/null
+++ b/cmd/seama.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2023 Linus Walleij <linus.walleij@linaro.org>
+ * Support for the "SEAttle iMAge" SEAMA NAND image format
+ */
+
+#include <command.h>
+#include <env.h>
+#include <nand.h>
+
+/*
+ * All SEAMA data is stored in the flash in "network endianness"
+ * i.e. big endian, which means that it needs to be byte-swapped
+ * on all little endian platforms.
+ *
+ * structure for a SEAMA entity in NAND flash:
+ *
+ * 32 bit SEAMA magic 0x5EA3A417
+ * 16 bit reserved
+ * 16 bit metadata size (following the header)
+ * 32 bit image size
+ * 16 bytes MD5 digest of the image
+ * meta data
+ * ... image data ...
+ *
+ * Then if a new SEAMA magic follows, that is the next image.
+ */
+
+#define SEAMA_MAGIC 0x5EA3A417
+#define SEAMA_HDR_NO_META_SZ 28
+#define SEAMA_MAX_META_SZ (1024 - SEAMA_HDR_NO_META_SZ)
+
+struct seama_header {
+ u32 magic;
+ u32 meta_size;
+ u32 image_size;
+ u8 md5[16];
+ u8 metadata[SEAMA_MAX_META_SZ];
+};
+
+static struct seama_header shdr;
+
+static int env_set_val(const char *varname, ulong val)
+{
+ int ret;
+
+ ret = env_set_hex(varname, val);
+ if (ret)
+ printf("Failed to %s env var\n", varname);
+
+ return ret;
+}
+
+static int do_seama_load_image(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct mtd_info *mtd;
+ uintptr_t load_addr;
+ unsigned long image_index;
+ u32 len;
+ size_t readsz;
+ int ret;
+ u32 *start;
+ u32 *offset;
+ u32 *end;
+ u32 tmp;
+
+ if (argc < 2 || argc > 3)
+ return CMD_RET_USAGE;
+
+ load_addr = hextoul(argv[1], NULL);
+ if (!load_addr) {
+ printf("Invalid load address\n");
+ return CMD_RET_USAGE;
+ }
+
+ /* Can be 0 for first image */
+ image_index = hextoul(argv[2], NULL);
+
+ /* We only support one NAND, the first one */
+ nand_curr_device = 0;
+ mtd = get_nand_dev_by_index(0);
+ if (!mtd) {
+ printf("NAND Device 0 not available\n");
+ return CMD_RET_FAILURE;
+ }
+
+#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
+ board_nand_select_device(mtd_to_nand(mtd), 0);
+#endif
+
+ printf("Loading SEAMA image %lu from %s\n", image_index, mtd->name);
+
+ readsz = sizeof(shdr);
+ offset = 0;
+ ret = nand_read_skip_bad(mtd, 0, &readsz, NULL, mtd->size,
+ (u_char *)&shdr);
+ if (ret) {
+ printf("Read error reading SEAMA header\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (shdr.magic != SEAMA_MAGIC) {
+ printf("Invalid SEAMA image magic: 0x%08x\n", shdr.magic);
+ return CMD_RET_FAILURE;
+ }
+
+ /* Only the lower 16 bits are valid */
+ shdr.meta_size &= 0xFFFF;
+
+ if (env_set_val("seama_image_size", 0))
+ return CMD_RET_FAILURE;
+
+ printf("SEMA IMAGE:\n");
+ printf(" metadata size %d\n", shdr.meta_size);
+ printf(" image size %d\n", shdr.image_size);
+ printf(" checksum %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ shdr.md5[0], shdr.md5[1], shdr.md5[2], shdr.md5[3],
+ shdr.md5[4], shdr.md5[5], shdr.md5[6], shdr.md5[7],
+ shdr.md5[8], shdr.md5[9], shdr.md5[10], shdr.md5[11],
+ shdr.md5[12], shdr.md5[13], shdr.md5[14], shdr.md5[15]);
+
+ /* TODO: handle metadata if needed */
+
+ len = shdr.image_size;
+ if (env_set_val("seama_image_size", len))
+ return CMD_RET_FAILURE;
+
+ /* We need to include the header (read full pages) */
+ readsz = shdr.image_size + SEAMA_HDR_NO_META_SZ + shdr.meta_size;
+ ret = nand_read_skip_bad(mtd, 0, &readsz, NULL, mtd->size,
+ (u_char *)load_addr);
+ if (ret) {
+ printf("Read error reading SEAMA main image\n");
+ return CMD_RET_FAILURE;
+ }
+
+ /* We use a temporary variable tmp to avoid to hairy casts */
+ start = (u32 *)load_addr;
+ tmp = (u32)start;
+ tmp += SEAMA_HDR_NO_META_SZ + shdr.meta_size;
+ offset = (u32 *)tmp;
+ tmp += shdr.image_size;
+ end = (u32 *)tmp;
+
+ printf("Decoding SEAMA image 0x%08x..0x%08x to 0x%08x\n",
+ (u32)offset, (u32)end, (u32)start);
+ for (; start < end; start++, offset++)
+ *start = be32_to_cpu(*offset);
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD
+ (seama, 3, 1, do_seama_load_image,
+ "Load the SEAMA image and sets envs",
+ "seama <addr> <imageindex>\n"
+);
diff --git a/cmd/setexpr.c b/cmd/setexpr.c
new file mode 100644
index 00000000000..c45fa85c887
--- /dev/null
+++ b/cmd/setexpr.c
@@ -0,0 +1,569 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ * Copyright 2013 Wolfgang Denk <wd@denx.de>
+ */
+
+/*
+ * This file provides a shell like 'expr' function to return.
+ */
+
+#include <config.h>
+#include <command.h>
+#include <ctype.h>
+#include <env.h>
+#include <log.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <vsprintf.h>
+#include <linux/errno.h>
+#include <linux/sizes.h>
+#include "printf.h"
+
+#define MAX_STR_LEN 128
+
+/**
+ * struct expr_arg: Holds an argument to an expression
+ *
+ * @ival: Integer value (if width is not CMD_DATA_SIZE_STR)
+ * @sval: String value (if width is CMD_DATA_SIZE_STR)
+ */
+struct expr_arg {
+ union {
+ ulong ival;
+ char *sval;
+ };
+};
+
+/**
+ * arg_set_str() - copy string to expression argument
+ *
+ * The string is truncated to 64 KiB plus NUL terminator.
+ *
+ * @p: pointer to string
+ * @argp: pointer to expression argument
+ * Return: 0 on success, -ENOMEM if out of memory
+ */
+static int arg_set_str(void *p, struct expr_arg *argp)
+{
+ int len;
+ char *str;
+
+ /* Maximum string length of 64 KiB plus NUL terminator */
+ len = strnlen((char *)p, SZ_64K) + 1;
+ str = malloc(len);
+ if (!str) {
+ printf("Out of memory\n");
+ return -ENOMEM;
+ }
+ memcpy(str, p, len);
+ str[len - 1] = '\0';
+ argp->sval = str;
+ return 0;
+}
+
+static int get_arg(char *s, int w, struct expr_arg *argp)
+{
+ struct expr_arg arg;
+ int ret;
+
+ /*
+ * If the parameter starts with a '*' then assume it is a pointer to
+ * the value we want.
+ */
+ if (s[0] == '*') {
+ ulong *p;
+ ulong addr;
+ ulong val;
+
+ addr = hextoul(&s[1], NULL);
+ switch (w) {
+ case 1:
+ p = map_sysmem(addr, sizeof(uchar));
+ val = (ulong)*(uchar *)p;
+ unmap_sysmem(p);
+ arg.ival = val;
+ break;
+ case 2:
+ p = map_sysmem(addr, sizeof(ushort));
+ val = (ulong)*(ushort *)p;
+ unmap_sysmem(p);
+ arg.ival = val;
+ break;
+ case CMD_DATA_SIZE_STR:
+ p = map_sysmem(addr, SZ_64K);
+ ret = arg_set_str(p, &arg);
+ unmap_sysmem(p);
+ if (ret)
+ return ret;
+ break;
+ case 4:
+ p = map_sysmem(addr, sizeof(u32));
+ val = *(u32 *)p;
+ unmap_sysmem(p);
+ arg.ival = val;
+ break;
+ default:
+ p = map_sysmem(addr, sizeof(ulong));
+ val = *p;
+ unmap_sysmem(p);
+ arg.ival = val;
+ break;
+ }
+ } else {
+ if (w == CMD_DATA_SIZE_STR) {
+ ret = arg_set_str(s, &arg);
+ if (ret)
+ return ret;
+ } else {
+ arg.ival = hextoul(s, NULL);
+ }
+ }
+ *argp = arg;
+
+ return 0;
+}
+
+#ifdef CONFIG_REGEX
+
+#include <slre.h>
+
+/*
+ * memstr - Find the first substring in memory
+ * @s1: The string to be searched
+ * @s2: The string to search for
+ *
+ * Similar to and based on strstr(),
+ * but strings do not need to be NUL terminated.
+ */
+static char *memstr(const char *s1, int l1, const char *s2, int l2)
+{
+ if (!l2)
+ return (char *)s1;
+
+ while (l1 >= l2) {
+ l1--;
+ if (!memcmp(s1, s2, l2))
+ return (char *)s1;
+ s1++;
+ }
+ return NULL;
+}
+
+/**
+ * substitute() - Substitute part of one string with another
+ *
+ * This updates @string so that the first occurrence of @old is replaced with
+ * @new
+ *
+ * @string: String buffer containing string to update at the start
+ * @slen: Pointer to current string length, updated on success
+ * @ssize: Size of string buffer
+ * @old: Old string to find in the buffer (no terminator needed)
+ * @olen: Length of @old excluding terminator
+ * @new: New string to replace @old with
+ * @nlen: Length of @new excluding terminator
+ * Return: pointer to immediately after the copied @new in @string, or NULL if
+ * no replacement took place
+ */
+static char *substitute(char *string, int *slen, int ssize,
+ const char *old, int olen, const char *new, int nlen)
+{
+ char *p = memstr(string, *slen, old, olen);
+
+ if (p == NULL)
+ return NULL;
+
+ debug("## Match at pos %ld: match len %d, subst len %d\n",
+ (long)(p - string), olen, nlen);
+
+ /* make sure replacement matches */
+ if (*slen + nlen - olen > ssize) {
+ printf("## error: substitution buffer overflow\n");
+ return NULL;
+ }
+
+ /* move tail if needed */
+ if (olen != nlen) {
+ int tail, len;
+
+ len = (olen > nlen) ? olen : nlen;
+
+ tail = ssize - (p + len - string);
+
+ debug("## tail len %d\n", tail);
+
+ memmove(p + nlen, p + olen, tail);
+ }
+
+ /* insert substitute */
+ memcpy(p, new, nlen);
+
+ *slen += nlen - olen;
+
+ return p + nlen;
+}
+
+int setexpr_regex_sub(char *data, uint data_size, char *nbuf, uint nbuf_size,
+ const char *r, const char *s, bool global)
+{
+ struct slre slre;
+ char *datap = data;
+ int res, len, nlen, loop;
+
+ if (slre_compile(&slre, r) == 0) {
+ printf("Error compiling regex: %s\n", slre.err_str);
+ return 1;
+ }
+
+ len = strlen(data);
+ for (loop = 0;; loop++) {
+ struct cap caps[slre.num_caps + 2];
+ const char *old;
+ char *np;
+ int i, olen;
+
+ (void) memset(caps, 0, sizeof(caps));
+
+ res = slre_match(&slre, datap, len - (datap - data), caps);
+
+ debug("Result: %d\n", res);
+
+ for (i = 0; i <= slre.num_caps; i++) {
+ if (caps[i].len > 0) {
+ debug("Substring %d: [%.*s]\n", i,
+ caps[i].len, caps[i].ptr);
+ }
+ }
+
+ if (res == 0) {
+ if (loop == 0) {
+ debug("%s: No match\n", data);
+ } else {
+ debug("## MATCH ## %s\n", data);
+ }
+ break;
+ }
+
+ if (!s)
+ return 1;
+
+ old = caps[0].ptr;
+ olen = caps[0].len;
+ nlen = strlen(s);
+
+ if (nlen + 1 >= nbuf_size) {
+ printf("## error: pattern buffer overflow: have %d, need %d\n",
+ nbuf_size, nlen + 1);
+ return 1;
+ }
+ strcpy(nbuf, s);
+
+ debug("## SUBST(1) ## %s\n", nbuf);
+
+ /*
+ * Handle back references
+ *
+ * Support for \0 ... \9, where \0 is the
+ * whole matched pattern (similar to &).
+ *
+ * Implementation is a bit simpleminded as
+ * backrefs are substituted sequentially, one
+ * by one. This will lead to somewhat
+ * unexpected results if the replacement
+ * strings contain any \N strings then then
+ * may get substitued, too. We accept this
+ * restriction for the sake of simplicity.
+ */
+ for (i = 0; i < 10; ++i) {
+ char backref[2] = {
+ '\\',
+ '0',
+ };
+
+ if (caps[i].len == 0)
+ break;
+
+ backref[1] += i;
+
+ debug("## BACKREF %d: replace \"%.*s\" by \"%.*s\" in \"%s\"\n",
+ i,
+ 2, backref,
+ caps[i].len, caps[i].ptr,
+ nbuf);
+
+ for (np = nbuf;;) {
+ char *p = memstr(np, nlen, backref, 2);
+
+ if (p == NULL)
+ break;
+
+ np = substitute(np, &nlen,
+ nbuf_size - (np - nbuf),
+ backref, 2,
+ caps[i].ptr, caps[i].len);
+
+ if (np == NULL)
+ return 1;
+ }
+ }
+ debug("## SUBST(2) ## %s\n", nbuf);
+
+ datap = substitute(datap, &len, data_size - (datap - data),
+ old, olen, nbuf, nlen);
+
+ if (datap == NULL)
+ return 1;
+
+ debug("## REMAINDER: %s\n", datap);
+
+ debug("## RESULT: %s\n", data);
+
+ if (!global)
+ break;
+ }
+ debug("## FINAL (now env_set()) : %s\n", data);
+
+ return 0;
+}
+
+#define SLRE_BUFSZ 16384
+#define SLRE_PATSZ 4096
+
+/*
+ * Perform regex operations on a environment variable
+ *
+ * Returns 0 if OK, 1 in case of errors.
+ */
+static int regex_sub_var(const char *name, const char *r, const char *s,
+ const char *t, int global)
+{
+ struct slre slre;
+ char data[SLRE_BUFSZ];
+ char nbuf[SLRE_PATSZ];
+ const char *value;
+ int len;
+ int ret;
+
+ if (!name)
+ return 1;
+
+ if (slre_compile(&slre, r) == 0) {
+ printf("Error compiling regex: %s\n", slre.err_str);
+ return 1;
+ }
+
+ if (!t) {
+ value = env_get(name);
+ if (!value) {
+ printf("## Error: variable \"%s\" not defined\n", name);
+ return 1;
+ }
+ t = value;
+ }
+
+ debug("REGEX on %s=%s\n", name, t);
+ debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n", r, s ? s : "<NULL>",
+ global);
+
+ len = strlen(t);
+ if (len + 1 > SLRE_BUFSZ) {
+ printf("## error: subst buffer overflow: have %d, need %d\n",
+ SLRE_BUFSZ, len + 1);
+ return 1;
+ }
+
+ strcpy(data, t);
+
+ ret = setexpr_regex_sub(data, SLRE_BUFSZ, nbuf, SLRE_PATSZ, r, s,
+ global);
+ if (ret)
+ return 1;
+
+ debug("%s=%s\n", name, data);
+
+ return env_set(name, data);
+}
+#endif
+
+static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct expr_arg aval, bval;
+ ulong value;
+ int ret = 0;
+ int w;
+
+ /*
+ * We take 3, 5, or 6 arguments, except fmt operation, which
+ * takes 4 to 8 arguments (limited by _maxargs):
+ * 3 : setexpr name value
+ * 5 : setexpr name val1 op val2
+ * setexpr name [g]sub r s
+ * 6 : setexpr name [g]sub r s t
+ * setexpr name fmt format [val1] [val2] [val3] [val4]
+ */
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ w = cmd_get_data_size(argv[0], 4);
+
+ if (get_arg(argv[2], w, &aval))
+ return CMD_RET_FAILURE;
+
+ /* format string assignment: "setexpr name fmt %d value" */
+ if (strcmp(argv[2], "fmt") == 0 && IS_ENABLED(CONFIG_CMD_SETEXPR_FMT)) {
+ char str[MAX_STR_LEN];
+ int result;
+
+ if (argc == 3)
+ return CMD_RET_USAGE;
+
+ result = printf_setexpr(str, sizeof(str), argc - 3, &argv[3]);
+ if (result)
+ return result;
+
+ return env_set(argv[1], str);
+ }
+
+ if (argc == 4 || argc > 6)
+ return CMD_RET_USAGE;
+
+ /* plain assignment: "setexpr name value" */
+ if (argc == 3) {
+ if (w == CMD_DATA_SIZE_STR) {
+ ret = env_set(argv[1], aval.sval);
+ free(aval.sval);
+ } else {
+ ret = env_set_hex(argv[1], aval.ival);
+ }
+
+ return ret;
+ }
+
+ /* 5 or 6 args (6 args only with [g]sub) */
+#ifdef CONFIG_REGEX
+ /*
+ * rexep handling: "setexpr name [g]sub r s [t]"
+ * with 5 args, "t" will be NULL
+ */
+ if (strcmp(argv[2], "gsub") == 0)
+ return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 1);
+
+ if (strcmp(argv[2], "sub") == 0)
+ return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 0);
+#endif
+
+ /* standard operators: "setexpr name val1 op val2" */
+ if (argc != 5)
+ return CMD_RET_USAGE;
+
+ if (strlen(argv[3]) != 1)
+ return CMD_RET_USAGE;
+
+ if (get_arg(argv[4], w, &bval)) {
+ if (w == CMD_DATA_SIZE_STR)
+ free(aval.sval);
+ return CMD_RET_FAILURE;
+ }
+
+ if (w == CMD_DATA_SIZE_STR) {
+ int len;
+ char *str;
+
+ switch (argv[3][0]) {
+ case '+':
+ len = strlen(aval.sval) + strlen(bval.sval) + 1;
+ str = malloc(len);
+ if (!str) {
+ printf("Out of memory\n");
+ ret = CMD_RET_FAILURE;
+ } else {
+ /* These were copied out and checked earlier */
+ strcpy(str, aval.sval);
+ strcat(str, bval.sval);
+ ret = env_set(argv[1], str);
+ if (ret)
+ printf("Could not set var\n");
+ free(str);
+ }
+ break;
+ default:
+ printf("invalid op\n");
+ ret = 1;
+ }
+ } else {
+ ulong a = aval.ival;
+ ulong b = bval.ival;
+
+ switch (argv[3][0]) {
+ case '|':
+ value = a | b;
+ break;
+ case '&':
+ value = a & b;
+ break;
+ case '+':
+ value = a + b;
+ break;
+ case '^':
+ value = a ^ b;
+ break;
+ case '-':
+ value = a - b;
+ break;
+ case '*':
+ value = a * b;
+ break;
+ case '/':
+ value = a / b;
+ break;
+ case '%':
+ value = a % b;
+ break;
+ default:
+ printf("invalid op\n");
+ return 1;
+ }
+
+ env_set_hex(argv[1], value);
+ }
+
+ if (w == CMD_DATA_SIZE_STR) {
+ free(aval.sval);
+ free(bval.sval);
+ }
+
+ return ret;
+}
+
+U_BOOT_CMD(
+ setexpr, 8, 0, do_setexpr,
+ "set environment variable as the result of eval expression",
+ "[.b, .w, .l, .s] name [*]value1 <op> [*]value2\n"
+ " - set environment variable 'name' to the result of the evaluated\n"
+ " expression specified by <op>. <op> can be &, |, ^, +, -, *, /, %\n"
+ " (for strings only + is supported)\n"
+ " size argument is only meaningful if value1 and/or value2 are\n"
+ " memory addresses (*)\n"
+ "setexpr[.b, .w, .l] name [*]value\n"
+ " - load a value into a variable"
+#ifdef CONFIG_CMD_SETEXPR_FMT
+ "\n"
+ "setexpr name fmt <format> [value1] [value2] [value3] [value4]\n"
+ " - set environment variable 'name' to the result of the bash like\n"
+ " format string evaluation of value."
+#endif
+#ifdef CONFIG_REGEX
+ "\n"
+ "setexpr name gsub r s [t]\n"
+ " - For each substring matching the regular expression <r> in the\n"
+ " string <t>, substitute the string <s>. The result is\n"
+ " assigned to <name>. If <t> is not supplied, use the old\n"
+ " value of <name>. If no substring matching <r> is found in <t>,\n"
+ " assign <t> to <name>.\n"
+ "setexpr name sub r s [t]\n"
+ " - Just like gsub(), but replace only the first matching substring"
+#endif
+);
diff --git a/cmd/sf.c b/cmd/sf.c
new file mode 100644
index 00000000000..08e364e1914
--- /dev/null
+++ b/cmd/sf.c
@@ -0,0 +1,653 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Command for accessing SPI flash.
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ */
+
+#include <command.h>
+#include <display_options.h>
+#include <div64.h>
+#include <dm.h>
+#include <log.h>
+#include <lmb.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <spi.h>
+#include <time.h>
+#include <spi_flash.h>
+#include <asm/cache.h>
+#include <jffs2/jffs2.h>
+#include <linux/mtd/mtd.h>
+
+#include <asm/io.h>
+#include <dm/device-internal.h>
+
+#include "legacy-mtd-utils.h"
+
+static struct spi_flash *flash;
+
+/*
+ * This function computes the length argument for the erase command.
+ * The length on which the command is to operate can be given in two forms:
+ * 1. <cmd> offset len - operate on <'offset', 'len')
+ * 2. <cmd> offset +len - operate on <'offset', 'round_up(len)')
+ * If the second form is used and the length doesn't fall on the
+ * sector boundary, than it will be adjusted to the next sector boundary.
+ * If it isn't in the flash, the function will fail (return -1).
+ * Input:
+ * arg: length specification (i.e. both command arguments)
+ * Output:
+ * len: computed length for operation
+ * Return:
+ * 1: success
+ * -1: failure (bad format, bad address).
+ */
+static int sf_parse_len_arg(char *arg, ulong *len)
+{
+ char *ep;
+ char round_up_len; /* indicates if the "+length" form used */
+ ulong len_arg;
+
+ round_up_len = 0;
+ if (*arg == '+') {
+ round_up_len = 1;
+ ++arg;
+ }
+
+ len_arg = hextoul(arg, &ep);
+ if (ep == arg || *ep != '\0')
+ return -1;
+
+ if (round_up_len && flash->sector_size > 0)
+ *len = ROUND(len_arg, flash->sector_size);
+ else
+ *len = len_arg;
+
+ return 1;
+}
+
+/**
+ * This function takes a byte length and a delta unit of time to compute the
+ * approximate bytes per second
+ *
+ * @param len amount of bytes currently processed
+ * @param start_ms start time of processing in ms
+ * Return: bytes per second if OK, 0 on error
+ */
+static ulong bytes_per_second(unsigned int len, ulong start_ms)
+{
+ /* less accurate but avoids overflow */
+ if (len >= ((unsigned int) -1) / 1024)
+ return len / (max(get_timer(start_ms) / 1024, 1UL));
+ else
+ return 1024 * len / max(get_timer(start_ms), 1UL);
+}
+
+static int do_spi_flash_probe(int argc, char *const argv[])
+{
+ unsigned int bus = CONFIG_SF_DEFAULT_BUS;
+ unsigned int cs = CONFIG_SF_DEFAULT_CS;
+ /* In DM mode, defaults speed and mode will be taken from DT */
+ unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
+ unsigned int mode = CONFIG_SF_DEFAULT_MODE;
+ char *endp;
+ bool use_dt = true;
+#if CONFIG_IS_ENABLED(DM_SPI_FLASH)
+ struct udevice *new, *bus_dev;
+ int ret;
+#else
+ struct spi_flash *new;
+#endif
+
+ if (argc >= 2) {
+ cs = simple_strtoul(argv[1], &endp, 0);
+ if (*argv[1] == 0 || (*endp != 0 && *endp != ':'))
+ return -1;
+ if (*endp == ':') {
+ if (endp[1] == 0)
+ return -1;
+
+ bus = cs;
+ cs = simple_strtoul(endp + 1, &endp, 0);
+ if (*endp != 0)
+ return -1;
+ }
+ }
+
+ if (argc >= 3) {
+ speed = simple_strtoul(argv[2], &endp, 0);
+ if (*argv[2] == 0 || *endp != 0)
+ return -1;
+ use_dt = false;
+ }
+ if (argc >= 4) {
+ mode = hextoul(argv[3], &endp);
+ if (*argv[3] == 0 || *endp != 0)
+ return -1;
+ use_dt = false;
+ }
+
+#if CONFIG_IS_ENABLED(DM_SPI_FLASH)
+ /* Remove the old device, otherwise probe will just be a nop */
+ ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new);
+ if (!ret) {
+ device_remove(new, DM_REMOVE_NORMAL);
+ }
+ flash = NULL;
+ if (use_dt) {
+ ret = spi_flash_probe_bus_cs(bus, cs, &new);
+ if (!ret)
+ flash = dev_get_uclass_priv(new);
+ } else {
+ flash = spi_flash_probe(bus, cs, speed, mode);
+ }
+
+ if (!flash) {
+ printf("Failed to initialize SPI flash at %u:%u (error %d)\n",
+ bus, cs, ret);
+ return 1;
+ }
+#else
+ if (flash)
+ spi_flash_free(flash);
+
+ new = spi_flash_probe(bus, cs, speed, mode);
+ flash = new;
+ if (!new) {
+ printf("Failed to initialize SPI flash at %u:%u\n", bus, cs);
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * Write a block of data to SPI flash, first checking if it is different from
+ * what is already there.
+ *
+ * If the data being written is the same, then *skipped is incremented by len.
+ *
+ * @param flash flash context pointer
+ * @param offset flash offset to write
+ * @param len number of bytes to write
+ * @param buf buffer to write from
+ * @param cmp_buf read buffer to use to compare data
+ * @param skipped Count of skipped data (incremented by this function)
+ * Return: NULL if OK, else a string containing the stage which failed
+ */
+static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset,
+ size_t len, const char *buf, char *cmp_buf, size_t *skipped)
+{
+ char *ptr = (char *)buf;
+ u32 start_offset = offset % flash->sector_size;
+ u32 read_offset = offset - start_offset;
+
+ debug("offset=%#x+%#x, sector_size=%#x, len=%#zx\n",
+ read_offset, start_offset, flash->sector_size, len);
+ /* Read the entire sector so to allow for rewriting */
+ if (spi_flash_read(flash, read_offset, flash->sector_size, cmp_buf))
+ return "read";
+ /* Compare only what is meaningful (len) */
+ if (memcmp(cmp_buf + start_offset, buf, len) == 0) {
+ debug("Skip region %x+%x size %zx: no change\n",
+ start_offset, read_offset, len);
+ *skipped += len;
+ return NULL;
+ }
+ /* Erase the entire sector */
+ if (spi_flash_erase(flash, offset, flash->sector_size))
+ return "erase";
+ /* If it's a partial sector, copy the data into the temp-buffer */
+ if (len != flash->sector_size) {
+ memcpy(cmp_buf + start_offset, buf, len);
+ ptr = cmp_buf;
+ }
+ /* Write one complete sector */
+ if (spi_flash_write(flash, offset, flash->sector_size, ptr))
+ return "write";
+
+ return NULL;
+}
+
+/**
+ * Update an area of SPI flash by erasing and writing any blocks which need
+ * to change. Existing blocks with the correct data are left unchanged.
+ *
+ * @param flash flash context pointer
+ * @param offset flash offset to write
+ * @param len number of bytes to write
+ * @param buf buffer to write from
+ * Return: 0 if ok, 1 on error
+ */
+static int spi_flash_update(struct spi_flash *flash, u32 offset,
+ size_t len, const char *buf)
+{
+ const char *err_oper = NULL;
+ char *cmp_buf;
+ const char *end = buf + len;
+ size_t todo; /* number of bytes to do in this pass */
+ size_t skipped = 0; /* statistics */
+ const ulong start_time = get_timer(0);
+ size_t scale = 1;
+ const char *start_buf = buf;
+ ulong delta;
+
+ if (end - buf >= 200)
+ scale = (end - buf) / 100;
+ cmp_buf = memalign(ARCH_DMA_MINALIGN, flash->sector_size);
+ if (cmp_buf) {
+ ulong last_update = get_timer(0);
+
+ for (; buf < end && !err_oper; buf += todo, offset += todo) {
+ todo = min_t(size_t, end - buf, flash->sector_size);
+ todo = min_t(size_t, end - buf,
+ flash->sector_size - (offset % flash->sector_size));
+ if (get_timer(last_update) > 100) {
+ printf(" \rUpdating, %zu%% %lu B/s",
+ 100 - (end - buf) / scale,
+ bytes_per_second(buf - start_buf,
+ start_time));
+ last_update = get_timer(0);
+ }
+ err_oper = spi_flash_update_block(flash, offset, todo,
+ buf, cmp_buf, &skipped);
+ }
+ } else {
+ err_oper = "malloc";
+ }
+ free(cmp_buf);
+ putc('\r');
+ if (err_oper) {
+ printf("SPI flash failed in %s step\n", err_oper);
+ return 1;
+ }
+
+ delta = get_timer(start_time);
+ printf("%zu bytes written, %zu bytes skipped", len - skipped,
+ skipped);
+ printf(" in %ld.%lds, speed %ld B/s\n",
+ delta / 1000, delta % 1000, bytes_per_second(len, start_time));
+
+ return 0;
+}
+
+static int do_spi_flash_read_write(int argc, char *const argv[])
+{
+ unsigned long addr;
+ void *buf;
+ char *endp;
+ int ret = 1;
+ int dev = 0;
+ loff_t offset, len, maxsize;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ addr = hextoul(argv[1], &endp);
+ if (*argv[1] == 0 || *endp != 0)
+ return CMD_RET_USAGE;
+
+ if (mtd_arg_off_size(argc - 2, &argv[2], &dev, &offset, &len,
+ &maxsize, MTD_DEV_TYPE_NOR, flash->size))
+ return CMD_RET_FAILURE;
+
+ /* Consistency checking */
+ if (offset + len > flash->size) {
+ printf("ERROR: attempting %s past flash size (%#x)\n",
+ argv[0], flash->size);
+ return CMD_RET_FAILURE;
+ }
+
+ if (strncmp(argv[0], "read", 4) != 0 && flash->flash_is_unlocked &&
+ !flash->flash_is_unlocked(flash, offset, len)) {
+ printf("ERROR: flash area is locked\n");
+ return CMD_RET_FAILURE;
+ }
+
+ buf = map_physmem(addr, len, MAP_WRBACK);
+ if (!buf && addr) {
+ puts("Failed to map physical memory\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (strcmp(argv[0], "update") == 0) {
+ ret = spi_flash_update(flash, offset, len, buf);
+ } else if (strncmp(argv[0], "read", 4) == 0 ||
+ strncmp(argv[0], "write", 5) == 0) {
+ int read;
+
+ if (CONFIG_IS_ENABLED(LMB)) {
+ if (lmb_read_check(addr, len)) {
+ printf("ERROR: trying to overwrite reserved memory...\n");
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ read = strncmp(argv[0], "read", 4) == 0;
+ if (read)
+ ret = spi_flash_read(flash, offset, len, buf);
+ else
+ ret = spi_flash_write(flash, offset, len, buf);
+
+ printf("SF: %zu bytes @ %#x %s: ", (size_t)len, (u32)offset,
+ read ? "Read" : "Written");
+ if (ret)
+ printf("ERROR %d\n", ret);
+ else
+ printf("OK\n");
+ }
+
+ unmap_physmem(buf, len);
+
+ return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+static int do_spi_flash_erase(int argc, char *const argv[])
+{
+ int ret;
+ int dev = 0;
+ loff_t offset, len, maxsize;
+ ulong size;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ if (mtd_arg_off(argv[1], &dev, &offset, &len, &maxsize,
+ MTD_DEV_TYPE_NOR, flash->size))
+ return CMD_RET_FAILURE;
+
+ ret = sf_parse_len_arg(argv[2], &size);
+ if (ret != 1)
+ return CMD_RET_USAGE;
+
+ if (size == 0) {
+ debug("ERROR: Invalid size 0\n");
+ return CMD_RET_FAILURE;
+ }
+
+ /* Consistency checking */
+ if (offset + size > flash->size) {
+ printf("ERROR: attempting %s past flash size (%#x)\n",
+ argv[0], flash->size);
+ return CMD_RET_FAILURE;
+ }
+
+ if (flash->flash_is_unlocked &&
+ !flash->flash_is_unlocked(flash, offset, size)) {
+ printf("ERROR: flash area is locked\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = spi_flash_erase(flash, offset, size);
+ printf("SF: %zu bytes @ %#x Erased: ", (size_t)size, (u32)offset);
+ if (ret)
+ printf("ERROR %d\n", ret);
+ else
+ printf("OK\n");
+
+ return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+static int do_spi_protect(int argc, char *const argv[])
+{
+ int ret = 0;
+ loff_t start, len;
+ bool prot = false;
+
+ if (argc != 4)
+ return -1;
+
+ if (!str2off(argv[2], &start)) {
+ puts("start sector is not a valid number\n");
+ return 1;
+ }
+
+ if (!str2off(argv[3], &len)) {
+ puts("len is not a valid number\n");
+ return 1;
+ }
+
+ if (strcmp(argv[1], "lock") == 0)
+ prot = true;
+ else if (strcmp(argv[1], "unlock") == 0)
+ prot = false;
+ else
+ return -1; /* Unknown parameter */
+
+ ret = spi_flash_protect(flash, start, len, prot);
+
+ return ret == 0 ? 0 : 1;
+}
+
+enum {
+ STAGE_ERASE,
+ STAGE_CHECK,
+ STAGE_WRITE,
+ STAGE_READ,
+
+ STAGE_COUNT,
+};
+
+static const char *stage_name[STAGE_COUNT] = {
+ "erase",
+ "check",
+ "write",
+ "read",
+};
+
+struct test_info {
+ int stage;
+ int bytes;
+ unsigned base_ms;
+ unsigned time_ms[STAGE_COUNT];
+};
+
+static void show_time(struct test_info *test, int stage)
+{
+ uint64_t speed; /* KiB/s */
+ int bps; /* Bits per second */
+
+ speed = (long long)test->bytes * 1000;
+ if (test->time_ms[stage])
+ do_div(speed, test->time_ms[stage] * 1024);
+ bps = speed * 8;
+
+ printf("%d %s: %u ticks, %d KiB/s %d.%03d Mbps\n", stage,
+ stage_name[stage], test->time_ms[stage],
+ (int)speed, bps / 1000, bps % 1000);
+}
+
+static void spi_test_next_stage(struct test_info *test)
+{
+ test->time_ms[test->stage] = get_timer(test->base_ms);
+ show_time(test, test->stage);
+ test->base_ms = get_timer(0);
+ test->stage++;
+}
+
+/**
+ * Run a test on the SPI flash
+ *
+ * @param flash SPI flash to use
+ * @param buf Source buffer for data to write
+ * @param len Size of data to read/write
+ * @param offset Offset within flash to check
+ * @param vbuf Verification buffer
+ * Return: 0 if ok, -1 on error
+ */
+static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len,
+ ulong offset, uint8_t *vbuf)
+{
+ struct test_info test;
+ int err, i;
+
+ printf("SPI flash test:\n");
+ memset(&test, '\0', sizeof(test));
+ test.base_ms = get_timer(0);
+ test.bytes = len;
+ err = spi_flash_erase(flash, offset, len);
+ if (err) {
+ printf("Erase failed (err = %d)\n", err);
+ return -1;
+ }
+ spi_test_next_stage(&test);
+
+ err = spi_flash_read(flash, offset, len, vbuf);
+ if (err) {
+ printf("Check read failed (err = %d)\n", err);
+ return -1;
+ }
+ for (i = 0; i < len; i++) {
+ if (vbuf[i] != 0xff) {
+ printf("Check failed at %d\n", i);
+ print_buffer(i, vbuf + i, 1,
+ min_t(uint, len - i, 0x40), 0);
+ return -1;
+ }
+ }
+ spi_test_next_stage(&test);
+
+ err = spi_flash_write(flash, offset, len, buf);
+ if (err) {
+ printf("Write failed (err = %d)\n", err);
+ return -1;
+ }
+ memset(vbuf, '\0', len);
+ spi_test_next_stage(&test);
+
+ err = spi_flash_read(flash, offset, len, vbuf);
+ if (err) {
+ printf("Read failed (ret = %d)\n", err);
+ return -1;
+ }
+ spi_test_next_stage(&test);
+
+ for (i = 0; i < len; i++) {
+ if (buf[i] != vbuf[i]) {
+ printf("Verify failed at %d, good data:\n", i);
+ print_buffer(i, buf + i, 1,
+ min_t(uint, len - i, 0x40), 0);
+ printf("Bad data:\n");
+ print_buffer(i, vbuf + i, 1,
+ min_t(uint, len - i, 0x40), 0);
+ return -1;
+ }
+ }
+ printf("Test passed\n");
+ for (i = 0; i < STAGE_COUNT; i++)
+ show_time(&test, i);
+
+ return 0;
+}
+
+static int do_spi_flash_test(int argc, char *const argv[])
+{
+ unsigned long offset;
+ unsigned long len;
+ uint8_t *buf, *from;
+ char *endp;
+ uint8_t *vbuf;
+ int ret;
+
+ if (argc < 3)
+ return -1;
+ offset = hextoul(argv[1], &endp);
+ if (*argv[1] == 0 || *endp != 0)
+ return -1;
+ len = hextoul(argv[2], &endp);
+ if (*argv[2] == 0 || *endp != 0)
+ return -1;
+
+ vbuf = memalign(ARCH_DMA_MINALIGN, len);
+ if (!vbuf) {
+ printf("Cannot allocate memory (%lu bytes)\n", len);
+ return 1;
+ }
+ buf = memalign(ARCH_DMA_MINALIGN, len);
+ if (!buf) {
+ free(vbuf);
+ printf("Cannot allocate memory (%lu bytes)\n", len);
+ return 1;
+ }
+
+ from = map_sysmem(CONFIG_TEXT_BASE, 0);
+ memcpy(buf, from, len);
+ ret = spi_flash_test(flash, buf, len, offset, vbuf);
+ free(vbuf);
+ free(buf);
+ if (ret) {
+ printf("Test failed\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int do_spi_flash(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *cmd;
+ int ret;
+
+ /* need at least two arguments */
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ cmd = argv[1];
+ --argc;
+ ++argv;
+
+ if (strcmp(cmd, "probe") == 0)
+ return do_spi_flash_probe(argc, argv);
+
+ /* The remaining commands require a selected device */
+ if (!flash) {
+ puts("No SPI flash selected. Please run `sf probe'\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 ||
+ strcmp(cmd, "update") == 0)
+ ret = do_spi_flash_read_write(argc, argv);
+ else if (strcmp(cmd, "erase") == 0)
+ ret = do_spi_flash_erase(argc, argv);
+ else if (IS_ENABLED(CONFIG_SPI_FLASH_LOCK) && strcmp(cmd, "protect") == 0)
+ ret = do_spi_protect(argc, argv);
+ else if (IS_ENABLED(CONFIG_CMD_SF_TEST) && !strcmp(cmd, "test"))
+ ret = do_spi_flash_test(argc, argv);
+ else
+ ret = CMD_RET_USAGE;
+
+ return ret;
+}
+
+U_BOOT_LONGHELP(sf,
+ "probe [[bus:]cs] [hz] [mode] - init flash device on given SPI bus\n"
+ " and chip select\n"
+ "sf read addr offset|partition len - read `len' bytes starting at\n"
+ " `offset' or from start of mtd\n"
+ " `partition'to memory at `addr'\n"
+ "sf write addr offset|partition len - write `len' bytes from memory\n"
+ " at `addr' to flash at `offset'\n"
+ " or to start of mtd `partition'\n"
+ "sf erase offset|partition [+]len - erase `len' bytes from `offset'\n"
+ " or from start of mtd `partition'\n"
+ " `+len' round up `len' to block size\n"
+ "sf update addr offset|partition len - erase and write `len' bytes from memory\n"
+ " at `addr' to flash at `offset'\n"
+ " or to start of mtd `partition'\n"
+#ifdef CONFIG_SPI_FLASH_LOCK
+ "sf protect lock/unlock sector len - protect/unprotect 'len' bytes starting\n"
+ " at address 'sector'"
+#endif
+#ifdef CONFIG_CMD_SF_TEST
+ "\nsf test offset len - run a very basic destructive test"
+#endif
+ );
+
+U_BOOT_CMD(
+ sf, 5, 1, do_spi_flash,
+ "SPI flash sub-system", sf_help_text
+);
diff --git a/cmd/sha1sum.c b/cmd/sha1sum.c
new file mode 100644
index 00000000000..f2757146bba
--- /dev/null
+++ b/cmd/sha1sum.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2011
+ * Joe Hershberger, National Instruments, joe.hershberger@ni.com
+ *
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <hash.h>
+#include <linux/string.h>
+#include <u-boot/sha1.h>
+
+int do_sha1sum(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int flags = HASH_FLAG_ENV;
+ int ac;
+ char * const *av;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ av = argv + 1;
+ ac = argc - 1;
+#ifdef CONFIG_SHA1SUM_VERIFY
+ if (strcmp(*av, "-v") == 0) {
+ flags |= HASH_FLAG_VERIFY;
+ av++;
+ ac--;
+ }
+#endif
+
+ return hash_command("sha1", flags, cmdtp, flag, ac, av);
+}
+
+#ifdef CONFIG_SHA1SUM_VERIFY
+U_BOOT_CMD(
+ sha1sum, 5, 1, do_sha1sum,
+ "compute SHA1 message digest",
+ "address count [[*]sum]\n"
+ " - compute SHA1 message digest [save to sum]\n"
+ "sha1sum -v address count [*]sum\n"
+ " - verify sha1sum of memory area"
+);
+#else
+U_BOOT_CMD(
+ sha1sum, 4, 1, do_sha1sum,
+ "compute SHA1 message digest",
+ "address count [[*]sum]\n"
+ " - compute SHA1 message digest [save to sum]"
+);
+#endif
diff --git a/cmd/sleep.c b/cmd/sleep.c
new file mode 100644
index 00000000000..a8c896e0c5e
--- /dev/null
+++ b/cmd/sleep.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2001
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <console.h>
+#include <time.h>
+#include <vsprintf.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+
+static int do_sleep(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong start = get_timer(0);
+ ulong mdelay = 0;
+ ulong delay;
+ char *frpart;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ delay = dectoul(argv[1], NULL) * CONFIG_SYS_HZ;
+
+ frpart = strchr(argv[1], '.');
+
+ if (frpart) {
+ uint mult = CONFIG_SYS_HZ / 10;
+ for (frpart++; *frpart != '\0' && mult > 0; frpart++) {
+ if (*frpart < '0' || *frpart > '9') {
+ mdelay = 0;
+ break;
+ }
+ mdelay += (*frpart - '0') * mult;
+ mult /= 10;
+ }
+ }
+
+ delay += mdelay;
+
+ while (get_timer(start) < delay) {
+ if (ctrlc())
+ return CMD_RET_FAILURE;
+
+ udelay(100);
+ }
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ sleep , 2, 1, do_sleep,
+ "delay execution for some time",
+ "N\n"
+ " - delay execution for N seconds (N is _decimal_ and can be\n"
+ " fractional)"
+);
diff --git a/cmd/smbios.c b/cmd/smbios.c
new file mode 100644
index 00000000000..ed419f19028
--- /dev/null
+++ b/cmd/smbios.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * The 'smbios' command displays information from the SMBIOS table.
+ *
+ * Copyright (c) 2023, Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
+ */
+
+#include <command.h>
+#include <hexdump.h>
+#include <mapmem.h>
+#include <smbios.h>
+#include <tables_csum.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const struct str_lookup_table wakeup_type_strings[] = {
+ { SMBIOS_WAKEUP_TYPE_RESERVED, "Reserved" },
+ { SMBIOS_WAKEUP_TYPE_OTHER, "Other" },
+ { SMBIOS_WAKEUP_TYPE_UNKNOWN, "Unknown" },
+ { SMBIOS_WAKEUP_TYPE_APM_TIMER, "APM Timer" },
+ { SMBIOS_WAKEUP_TYPE_MODEM_RING, "Modem Ring" },
+ { SMBIOS_WAKEUP_TYPE_LAN_REMOTE, "Lan Remote" },
+ { SMBIOS_WAKEUP_TYPE_POWER_SWITCH, "Power Switch" },
+ { SMBIOS_WAKEUP_TYPE_PCI_PME, "PCI PME#" },
+ { SMBIOS_WAKEUP_TYPE_AC_POWER_RESTORED, "AC Power Restored" },
+};
+
+static const struct str_lookup_table boardtype_strings[] = {
+ { SMBIOS_BOARD_TYPE_UNKNOWN, "Unknown" },
+ { SMBIOS_BOARD_TYPE_OTHER, "Other" },
+ { SMBIOS_BOARD_TYPE_SERVER_BLADE, "Server Blade" },
+ { SMBIOS_BOARD_TYPE_CON_SWITCH, "Connectivity Switch" },
+ { SMBIOS_BOARD_TYPE_SM_MODULE, "System Management Module" },
+ { SMBIOS_BOARD_TYPE_PROCESSOR_MODULE, "Processor Module" },
+ { SMBIOS_BOARD_TYPE_IO_MODULE, "I/O Module" },
+ { SMBIOS_BOARD_TYPE_MEM_MODULE, "Memory Module" },
+ { SMBIOS_BOARD_TYPE_DAUGHTER_BOARD, "Daughter board" },
+ { SMBIOS_BOARD_TYPE_MOTHERBOARD, "Motherboard" },
+ { SMBIOS_BOARD_TYPE_PROC_MEM_MODULE, "Processor/Memory Module" },
+ { SMBIOS_BOARD_TYPE_PROC_IO_MODULE, "Processor/IO Module" },
+ { SMBIOS_BOARD_TYPE_INTERCON, "Interconnect board" },
+};
+
+static const struct str_lookup_table chassis_state_strings[] = {
+ { SMBIOS_STATE_OTHER, "Other" },
+ { SMBIOS_STATE_UNKNOWN, "Unknown" },
+ { SMBIOS_STATE_SAFE, "Safe" },
+ { SMBIOS_STATE_WARNING, "Warning" },
+ { SMBIOS_STATE_CRITICAL, "Critical" },
+ { SMBIOS_STATE_NONRECOVERABLE, "Non-recoverable" },
+};
+
+static const struct str_lookup_table chassis_security_strings[] = {
+ { SMBIOS_SECURITY_OTHER, "Other" },
+ { SMBIOS_SECURITY_UNKNOWN, "Unknown" },
+ { SMBIOS_SECURITY_NONE, "None" },
+ { SMBIOS_SECURITY_EXTINT_LOCK, "External interface locked out" },
+ { SMBIOS_SECURITY_EXTINT_EN, "External interface enabled" },
+};
+
+static const struct str_lookup_table processor_type_strings[] = {
+ { SMBIOS_PROCESSOR_TYPE_OTHER, "Other" },
+ { SMBIOS_PROCESSOR_TYPE_UNKNOWN, "Unknown" },
+ { SMBIOS_PROCESSOR_TYPE_CENTRAL, "Central Processor" },
+ { SMBIOS_PROCESSOR_TYPE_MATH, "Math Processor" },
+ { SMBIOS_PROCESSOR_TYPE_DSP, "DSP Processor" },
+ { SMBIOS_PROCESSOR_TYPE_VIDEO, "Video Processor" },
+};
+
+static const struct str_lookup_table processor_family_strings[] = {
+ { SMBIOS_PROCESSOR_FAMILY_OTHER, "Other" },
+ { SMBIOS_PROCESSOR_FAMILY_UNKNOWN, "Unknown" },
+ { SMBIOS_PROCESSOR_FAMILY_RSVD, "Reserved" },
+ { SMBIOS_PROCESSOR_FAMILY_ARMV7, "ARMv7" },
+ { SMBIOS_PROCESSOR_FAMILY_ARMV8, "ARMv8" },
+ { SMBIOS_PROCESSOR_FAMILY_RV32, "RISC-V RV32" },
+ { SMBIOS_PROCESSOR_FAMILY_RV64, "RISC-V RV64" },
+};
+
+static const struct str_lookup_table processor_upgrade_strings[] = {
+ { SMBIOS_PROCESSOR_UPGRADE_OTHER, "Other" },
+ { SMBIOS_PROCESSOR_UPGRADE_UNKNOWN, "Unknown" },
+ { SMBIOS_PROCESSOR_UPGRADE_NONE, "None" },
+};
+
+static const struct str_lookup_table err_corr_type_strings[] = {
+ { SMBIOS_CACHE_ERRCORR_OTHER, "Other" },
+ { SMBIOS_CACHE_ERRCORR_UNKNOWN, "Unknown" },
+ { SMBIOS_CACHE_ERRCORR_NONE, "None" },
+ { SMBIOS_CACHE_ERRCORR_PARITY, "Parity" },
+ { SMBIOS_CACHE_ERRCORR_SBITECC, "Single-bit ECC" },
+ { SMBIOS_CACHE_ERRCORR_MBITECC, "Multi-bit ECC" },
+};
+
+static const struct str_lookup_table sys_cache_type_strings[] = {
+ { SMBIOS_CACHE_SYSCACHE_TYPE_OTHER, "Other" },
+ { SMBIOS_CACHE_SYSCACHE_TYPE_UNKNOWN, "Unknown" },
+ { SMBIOS_CACHE_SYSCACHE_TYPE_INST, "Instruction" },
+ { SMBIOS_CACHE_SYSCACHE_TYPE_DATA, "Data" },
+ { SMBIOS_CACHE_SYSCACHE_TYPE_UNIFIED, "Unified" },
+};
+
+static const struct str_lookup_table associativity_strings[] = {
+ { SMBIOS_CACHE_ASSOC_OTHER, "Other" },
+ { SMBIOS_CACHE_ASSOC_UNKNOWN, "Unknown" },
+ { SMBIOS_CACHE_ASSOC_DMAPPED, "Direct Mapped" },
+ { SMBIOS_CACHE_ASSOC_2WAY, "2-way Set-Associative" },
+ { SMBIOS_CACHE_ASSOC_4WAY, "4-way Set-Associative" },
+ { SMBIOS_CACHE_ASSOC_FULLY, "Fully Associative" },
+ { SMBIOS_CACHE_ASSOC_8WAY, "8-way Set-Associative" },
+ { SMBIOS_CACHE_ASSOC_16WAY, "16-way Set-Associative" },
+ { SMBIOS_CACHE_ASSOC_12WAY, "12-way Set-Associative" },
+ { SMBIOS_CACHE_ASSOC_24WAY, "24-way Set-Associative" },
+ { SMBIOS_CACHE_ASSOC_32WAY, "32-way Set-Associative" },
+ { SMBIOS_CACHE_ASSOC_48WAY, "48-way Set-Associative" },
+ { SMBIOS_CACHE_ASSOC_64WAY, "64-way Set-Associative" },
+ { SMBIOS_CACHE_ASSOC_20WAY, "20-way Set-Associative" },
+
+};
+
+/**
+ * smbios_get_string() - get SMBIOS string from table
+ *
+ * @table: SMBIOS table
+ * @index: index of the string
+ * Return: address of string, may point to empty string
+ */
+static const char *smbios_get_string(void *table, int index)
+{
+ const char *str = (char *)table +
+ ((struct smbios_header *)table)->length;
+ static const char fallback[] = "";
+
+ if (!index)
+ return fallback;
+
+ if (!*str)
+ ++str;
+ for (--index; *str && index; --index)
+ str += strlen(str) + 1;
+
+ return str;
+}
+
+static struct smbios_header *next_table(struct smbios_header *table)
+{
+ const char *str;
+
+ if (table->type == SMBIOS_END_OF_TABLE)
+ return NULL;
+
+ str = smbios_get_string(table, -1);
+ return (struct smbios_header *)(++str);
+}
+
+static void smbios_print_generic(struct smbios_header *table)
+{
+ char *str = (char *)table + table->length;
+
+ if (CONFIG_IS_ENABLED(HEXDUMP)) {
+ printf("Header and Data:\n");
+ print_hex_dump("\t", DUMP_PREFIX_OFFSET, 16, 1,
+ table, table->length, false);
+ }
+ if (*str) {
+ printf("Strings:\n");
+ for (int index = 1; *str; ++index) {
+ printf("\tString %u: %s\n", index, str);
+ str += strlen(str) + 1;
+ }
+ }
+}
+
+static void smbios_print_str(const char *label, void *table, u8 index)
+{
+ printf("\t%s: %s\n", label, smbios_get_string(table, index));
+}
+
+static void smbios_print_lookup_str(const struct str_lookup_table *table,
+ u16 index, u16 array_size,
+ const char *prefix)
+{
+ int i;
+ const char *str = NULL;
+
+ for (i = 0; i < array_size; i++) {
+ if ((table + i)->idx == index)
+ str = (table + i)->str;
+ }
+
+ if (str)
+ printf("\t%s: %s\n", prefix, str);
+ else
+ printf("\t%s: [%04x]\n", prefix, index);
+}
+
+static void smbios_print_type0(struct smbios_type0 *table)
+{
+ printf("BIOS Information\n");
+ smbios_print_str("Vendor", table, table->vendor);
+ smbios_print_str("BIOS Version", table, table->bios_ver);
+ /* Keep table->bios_start_segment as 0 for UEFI-based systems */
+ smbios_print_str("BIOS Release Date", table, table->bios_release_date);
+ printf("\tBIOS ROM Size: 0x%02x\n", table->bios_rom_size);
+ printf("\tBIOS Characteristics: 0x%016llx\n",
+ table->bios_characteristics);
+ printf("\tBIOS Characteristics Extension Byte 1: 0x%02x\n",
+ table->bios_characteristics_ext1);
+ printf("\tBIOS Characteristics Extension Byte 2: 0x%02x\n",
+ table->bios_characteristics_ext2);
+ printf("\tSystem BIOS Major Release: 0x%02x\n",
+ table->bios_major_release);
+ printf("\tSystem BIOS Minor Release: 0x%02x\n",
+ table->bios_minor_release);
+ printf("\tEmbedded Controller Firmware Major Release: 0x%02x\n",
+ table->ec_major_release);
+ printf("\tEmbedded Controller Firmware Minor Release: 0x%02x\n",
+ table->ec_minor_release);
+ printf("\tExtended BIOS ROM Size: 0x%04x\n",
+ table->extended_bios_rom_size);
+}
+
+static void smbios_print_type1(struct smbios_type1 *table)
+{
+ printf("System Information\n");
+ smbios_print_str("Manufacturer", table, table->manufacturer);
+ smbios_print_str("Product Name", table, table->product_name);
+ smbios_print_str("Version", table, table->version);
+ smbios_print_str("Serial Number", table, table->serial_number);
+ if (table->hdr.length >= SMBIOS_TYPE1_LENGTH_V21) {
+ printf("\tUUID: %pUl\n", table->uuid);
+ smbios_print_lookup_str(wakeup_type_strings,
+ table->wakeup_type,
+ ARRAY_SIZE(wakeup_type_strings),
+ "Wake-up Type");
+ }
+ if (table->hdr.length >= SMBIOS_TYPE1_LENGTH_V24) {
+ smbios_print_str("SKU Number", table, table->sku_number);
+ smbios_print_str("Family", table, table->family);
+ }
+}
+
+static void smbios_print_type2(struct smbios_type2 *table)
+{
+ int i;
+ u8 *addr = (u8 *)table + offsetof(struct smbios_type2, eos);
+
+ printf("Baseboard Information\n");
+ smbios_print_str("Manufacturer", table, table->manufacturer);
+ smbios_print_str("Product Name", table, table->product_name);
+ smbios_print_str("Version", table, table->version);
+ smbios_print_str("Serial Number", table, table->serial_number);
+ smbios_print_str("Asset Tag", table, table->asset_tag_number);
+ printf("\tFeature Flags: 0x%02x\n", table->feature_flags);
+ smbios_print_str("Chassis Location", table, table->chassis_location);
+ printf("\tChassis Handle: 0x%04x\n", table->chassis_handle);
+ smbios_print_lookup_str(boardtype_strings,
+ table->board_type,
+ ARRAY_SIZE(boardtype_strings),
+ "Board Type");
+ printf("\tNumber of Contained Object Handles: 0x%02x\n",
+ table->number_contained_objects);
+ if (!table->number_contained_objects)
+ return;
+
+ printf("\tContained Object Handles:\n");
+ for (i = 0; i < table->number_contained_objects; i++) {
+ printf("\t\tObject[%03d]:\n", i);
+ if (CONFIG_IS_ENABLED(HEXDUMP))
+ print_hex_dump("\t\t", DUMP_PREFIX_OFFSET, 16, 1, addr,
+ sizeof(u16), false);
+ addr += sizeof(u16);
+ }
+ printf("\n");
+}
+
+static void smbios_print_type3(struct smbios_type3 *table)
+{
+ int i;
+ u8 *addr = (u8 *)table + offsetof(struct smbios_type3, sku_number);
+
+ printf("Chassis Information\n");
+ smbios_print_str("Manufacturer", table, table->manufacturer);
+ printf("\tType: 0x%02x\n", table->chassis_type);
+ smbios_print_str("Version", table, table->version);
+ smbios_print_str("Serial Number", table, table->serial_number);
+ smbios_print_str("Asset Tag", table, table->asset_tag_number);
+ smbios_print_lookup_str(chassis_state_strings,
+ table->bootup_state,
+ ARRAY_SIZE(chassis_state_strings),
+ "Boot-up State");
+ smbios_print_lookup_str(chassis_state_strings,
+ table->power_supply_state,
+ ARRAY_SIZE(chassis_state_strings),
+ "Power Supply State");
+ smbios_print_lookup_str(chassis_state_strings,
+ table->thermal_state,
+ ARRAY_SIZE(chassis_state_strings),
+ "Thermal State");
+ smbios_print_lookup_str(chassis_security_strings,
+ table->security_status,
+ ARRAY_SIZE(chassis_security_strings),
+ "Security Status");
+ printf("\tOEM-defined: 0x%08x\n", table->oem_defined);
+ printf("\tHeight: 0x%02x\n", table->height);
+ printf("\tNumber of Power Cords: 0x%02x\n",
+ table->number_of_power_cords);
+ printf("\tContained Element Count: 0x%02x\n", table->element_count);
+ printf("\tContained Element Record Length: 0x%02x\n",
+ table->element_record_length);
+ if (table->element_count) {
+ printf("\tContained Elements:\n");
+ for (i = 0; i < table->element_count; i++) {
+ printf("\t\tElement[%03d]:\n", i);
+ if (CONFIG_IS_ENABLED(HEXDUMP))
+ print_hex_dump("\t\t", DUMP_PREFIX_OFFSET, 16,
+ 1, addr,
+ table->element_record_length,
+ false);
+ printf("\t\tContained Element Type: 0x%02x\n", *addr);
+ printf("\t\tContained Element Minimum: 0x%02x\n",
+ *(addr + 1));
+ printf("\t\tContained Element Maximum: 0x%02x\n",
+ *(addr + 2));
+ addr += table->element_record_length;
+ }
+ }
+ smbios_print_str("SKU Number", table, *addr);
+}
+
+static void smbios_print_type4(struct smbios_type4 *table)
+{
+ printf("Processor Information:\n");
+ smbios_print_str("Socket Designation", table, table->socket_design);
+ smbios_print_lookup_str(processor_type_strings,
+ table->processor_type,
+ ARRAY_SIZE(processor_type_strings),
+ "Processor Type");
+ smbios_print_lookup_str(processor_family_strings,
+ table->processor_family,
+ ARRAY_SIZE(processor_family_strings),
+ "Processor Family");
+ smbios_print_str("Processor Manufacturer", table,
+ table->processor_manufacturer);
+ printf("\tProcessor ID word 0: 0x%08x\n", table->processor_id[0]);
+ printf("\tProcessor ID word 1: 0x%08x\n", table->processor_id[1]);
+ smbios_print_str("Processor Version", table, table->processor_version);
+ printf("\tVoltage: 0x%02x\n", table->voltage);
+ printf("\tExternal Clock: 0x%04x\n", table->external_clock);
+ printf("\tMax Speed: 0x%04x\n", table->max_speed);
+ printf("\tCurrent Speed: 0x%04x\n", table->current_speed);
+ printf("\tStatus: 0x%02x\n", table->status);
+ smbios_print_lookup_str(processor_upgrade_strings,
+ table->processor_upgrade,
+ ARRAY_SIZE(processor_upgrade_strings),
+ "Processor Upgrade");
+ printf("\tL1 Cache Handle: 0x%04x\n", table->l1_cache_handle);
+ printf("\tL2 Cache Handle: 0x%04x\n", table->l2_cache_handle);
+ printf("\tL3 Cache Handle: 0x%04x\n", table->l3_cache_handle);
+ smbios_print_str("Serial Number", table, table->serial_number);
+ smbios_print_str("Asset Tag", table, table->asset_tag);
+ smbios_print_str("Part Number", table, table->part_number);
+ printf("\tCore Count: 0x%02x\n", table->core_count);
+ printf("\tCore Enabled: 0x%02x\n", table->core_enabled);
+ printf("\tThread Count: 0x%02x\n", table->thread_count);
+ printf("\tProcessor Characteristics: 0x%04x\n",
+ table->processor_characteristics);
+ smbios_print_lookup_str(processor_family_strings,
+ table->processor_family2,
+ ARRAY_SIZE(processor_family_strings),
+ "Processor Family 2");
+ printf("\tCore Count 2: 0x%04x\n", table->core_count2);
+ printf("\tCore Enabled 2: 0x%04x\n", table->core_enabled2);
+ printf("\tThread Count 2: 0x%04x\n", table->thread_count2);
+ printf("\tThread Enabled: 0x%04x\n", table->thread_enabled);
+}
+
+static void smbios_print_type7(struct smbios_type7 *table)
+{
+ printf("Cache Information:\n");
+ smbios_print_str("Socket Designation", table,
+ table->socket_design);
+ printf("\tCache Configuration: 0x%04x\n", table->config.data);
+ printf("\tMaximum Cache Size: 0x%04x\n", table->max_size.data);
+ printf("\tInstalled Size: 0x%04x\n", table->inst_size.data);
+ printf("\tSupported SRAM Type: 0x%04x\n", table->supp_sram_type.data);
+ printf("\tCurrent SRAM Type: 0x%04x\n", table->curr_sram_type.data);
+ printf("\tCache Speed: 0x%02x\n", table->speed);
+ smbios_print_lookup_str(err_corr_type_strings,
+ table->err_corr_type,
+ ARRAY_SIZE(err_corr_type_strings),
+ "Error Correction Type");
+ smbios_print_lookup_str(sys_cache_type_strings,
+ table->sys_cache_type,
+ ARRAY_SIZE(sys_cache_type_strings),
+ "System Cache Type");
+ smbios_print_lookup_str(associativity_strings,
+ table->associativity,
+ ARRAY_SIZE(associativity_strings),
+ "Associativity");
+ printf("\tMaximum Cache Size 2: 0x%08x\n", table->max_size2.data);
+ printf("\tInstalled Cache Size 2: 0x%08x\n", table->inst_size2.data);
+}
+
+static void smbios_print_type127(struct smbios_type127 *table)
+{
+ printf("End Of Table\n");
+}
+
+static int do_smbios(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr;
+ void *entry;
+ u32 size;
+ char version[12];
+ struct smbios_header *table;
+ static const char smbios_sig[] = "_SM_";
+ static const char smbios3_sig[] = "_SM3_";
+ size_t count = 0;
+ u32 table_maximum_size;
+
+ addr = gd_smbios_start();
+ if (!addr) {
+ log_warning("SMBIOS not available\n");
+ return CMD_RET_FAILURE;
+ }
+ entry = map_sysmem(addr, 0);
+ if (!memcmp(entry, smbios3_sig, sizeof(smbios3_sig) - 1)) {
+ struct smbios3_entry *entry3 = entry;
+
+ table = (void *)(uintptr_t)entry3->struct_table_address;
+ snprintf(version, sizeof(version), "%d.%d.%d",
+ entry3->major_ver, entry3->minor_ver, entry3->doc_rev);
+ table = (void *)(uintptr_t)entry3->struct_table_address;
+ size = entry3->length;
+ table_maximum_size = entry3->table_maximum_size;
+ } else if (!memcmp(entry, smbios_sig, sizeof(smbios_sig) - 1)) {
+ struct smbios_entry *entry2 = entry;
+
+ snprintf(version, sizeof(version), "%d.%d",
+ entry2->major_ver, entry2->minor_ver);
+ table = (void *)(uintptr_t)entry2->struct_table_address;
+ size = entry2->length;
+ table_maximum_size = entry2->struct_table_length;
+ } else {
+ log_err("Unknown SMBIOS anchor format\n");
+ return CMD_RET_FAILURE;
+ }
+ if (table_compute_checksum(entry, size)) {
+ log_err("Invalid anchor checksum\n");
+ return CMD_RET_FAILURE;
+ }
+ printf("SMBIOS %s present.\n", version);
+
+ for (struct smbios_header *pos = table; pos; pos = next_table(pos))
+ ++count;
+ printf("%zd structures occupying %d bytes\n", count, table_maximum_size);
+ printf("Table at 0x%llx\n", (unsigned long long)map_to_sysmem(table));
+
+ for (struct smbios_header *pos = table; pos; pos = next_table(pos)) {
+ printf("\nHandle 0x%04x, DMI type %d, %d bytes at 0x%llx\n",
+ pos->handle, pos->type, pos->length,
+ (unsigned long long)map_to_sysmem(pos));
+ switch (pos->type) {
+ case SMBIOS_BIOS_INFORMATION:
+ smbios_print_type0((struct smbios_type0 *)pos);
+ break;
+ case SMBIOS_SYSTEM_INFORMATION:
+ smbios_print_type1((struct smbios_type1 *)pos);
+ break;
+ case SMBIOS_BOARD_INFORMATION:
+ smbios_print_type2((struct smbios_type2 *)pos);
+ break;
+ case SMBIOS_SYSTEM_ENCLOSURE:
+ smbios_print_type3((struct smbios_type3 *)pos);
+ break;
+ case SMBIOS_PROCESSOR_INFORMATION:
+ smbios_print_type4((struct smbios_type4 *)pos);
+ break;
+ case SMBIOS_CACHE_INFORMATION:
+ smbios_print_type7((struct smbios_type7 *)pos);
+ break;
+ case SMBIOS_END_OF_TABLE:
+ smbios_print_type127((struct smbios_type127 *)pos);
+ break;
+ default:
+ smbios_print_generic(pos);
+ break;
+ }
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_LONGHELP(smbios, "- display SMBIOS information");
+
+U_BOOT_CMD(smbios, 1, 0, do_smbios, "display SMBIOS information",
+ smbios_help_text);
diff --git a/cmd/smccc.c b/cmd/smccc.c
new file mode 100644
index 00000000000..fa04bb05ca4
--- /dev/null
+++ b/cmd/smccc.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018
+ * Michalis Pappas <mpappas@fastmail.fm>
+ */
+#include <asm/psci.h>
+#include <command.h>
+#include <vsprintf.h>
+#include <linux/arm-smccc.h>
+#include <linux/compiler.h>
+#include <linux/psci.h>
+#include <linux/string.h>
+
+static int do_call(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct arm_smccc_res res;
+
+ unsigned long fid;
+
+ unsigned long a1;
+ unsigned long a2;
+ unsigned long a3;
+ unsigned long a4;
+ unsigned long a5;
+ unsigned long a6;
+ unsigned long a7;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ fid = hextoul(argv[1], NULL);
+
+ a1 = argc > 2 ? hextoul(argv[2], NULL) : 0;
+ a2 = argc > 3 ? hextoul(argv[3], NULL) : 0;
+ a3 = argc > 4 ? hextoul(argv[4], NULL) : 0;
+ a4 = argc > 5 ? hextoul(argv[5], NULL) : 0;
+ a5 = argc > 6 ? hextoul(argv[6], NULL) : 0;
+ a6 = argc > 7 ? hextoul(argv[7], NULL) : 0;
+ a7 = argc > 8 ? hextoul(argv[8], NULL) : 0;
+
+ if (!strcmp(argv[0], "smc"))
+ arm_smccc_smc(fid, a1, a2, a3, a4, a5, a6, a7, &res);
+ else
+ arm_smccc_hvc(fid, a1, a2, a3, a4, a5, a6, a7, &res);
+
+ printf("Res: 0x%lx 0x%lx 0x%lx 0x%lx\n", res.a0, res.a1, res.a2, res.a3);
+
+ return 0;
+}
+
+#ifdef CONFIG_CMD_SMC
+U_BOOT_CMD(
+ smc, 9, 2, do_call,
+ "Issue a Secure Monitor Call",
+ "<fid> [arg1 ... arg6] [id]\n"
+ " - fid Function ID\n"
+ " - arg SMC arguments, passed to X1-X6 (default to zero)\n"
+ " - id Secure OS ID / Session ID, passed to W7 (defaults to zero)\n"
+);
+#endif
+
+#ifdef CONFIG_CMD_HVC
+U_BOOT_CMD(
+ hvc, 9, 2, do_call,
+ "Issue a Hypervisor Call",
+ "<fid> [arg1...arg6] [id]\n"
+ " - fid Function ID\n"
+ " - arg HVC arguments, passed to X1-X6 (default to zero)\n"
+ " - id Session ID, passed to W7 (defaults to zero)\n"
+);
+#endif
diff --git a/cmd/sound.c b/cmd/sound.c
new file mode 100644
index 00000000000..8f67cbd96e1
--- /dev/null
+++ b/cmd/sound.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ * Rajeshwari Shinde <rajeshwari.s@samsung.com>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <sound.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Initilaise sound subsystem */
+static int do_init(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_first_device_err(UCLASS_SOUND, &dev);
+ if (!ret)
+ ret = sound_setup(dev);
+ if (ret) {
+ printf("Initialise Audio driver failed (ret=%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+/* play sound from buffer */
+static int do_play(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int ret = 0;
+ int msec = 1000;
+ int freq = 400;
+ bool first = true;
+
+ ret = uclass_first_device_err(UCLASS_SOUND, &dev);
+ if (ret)
+ goto err;
+ --argc;
+ ++argv;
+ while (argc || first) {
+ first = false;
+ if (argc) {
+ msec = dectoul(argv[0], NULL);
+ --argc;
+ ++argv;
+ }
+ if (argc) {
+ freq = dectoul(argv[0], NULL);
+ --argc;
+ ++argv;
+ }
+ ret = sound_beep(dev, msec, freq);
+ if (ret)
+ goto err;
+ }
+ return 0;
+
+err:
+ printf("Sound device failed to play (err=%d)\n", ret);
+ return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl cmd_sound_sub[] = {
+ U_BOOT_CMD_MKENT(init, 0, 1, do_init, "", ""),
+ U_BOOT_CMD_MKENT(play, INT_MAX, 1, do_play, "", ""),
+};
+
+/* process sound command */
+static int do_sound(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *c;
+
+ if (argc < 1)
+ return CMD_RET_USAGE;
+
+ /* Strip off leading 'sound' command argument */
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], &cmd_sound_sub[0], ARRAY_SIZE(cmd_sound_sub));
+
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+ else
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+ sound, INT_MAX, 1, do_sound,
+ "sound sub-system",
+ "init - initialise the sound driver\n"
+ "sound play [len [freq [len [freq ...]]]] - play sounds\n"
+ " len - duration in ms\n"
+ " freq - frequency in Hz\n"
+);
diff --git a/cmd/source.c b/cmd/source.c
new file mode 100644
index 00000000000..c9b5f8e400a
--- /dev/null
+++ b/cmd/source.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2001
+ * Kyle Harris, kharris@nexus-tech.net
+ */
+
+/*
+ * The "source" command allows to define "script images", i. e. files
+ * that contain command sequences that can be executed by the command
+ * interpreter. It returns the exit status of the last command
+ * executed from the script. This is very similar to running a shell
+ * script in a UNIX shell, hence the name for the command.
+ */
+
+/* #define DEBUG */
+
+#include <command.h>
+#include <env.h>
+#include <image.h>
+#include <log.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+static int do_source(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr;
+ int rcode;
+ const char *fit_uname = NULL, *confname = NULL;
+
+ /* Find script image */
+ if (argc < 2) {
+ addr = CONFIG_SYS_LOAD_ADDR;
+ debug("* source: default load address = 0x%08lx\n", addr);
+#if defined(CONFIG_FIT)
+ } else if (fit_parse_subimage(argv[1], image_load_addr, &addr,
+ &fit_uname)) {
+ debug("* source: subimage '%s' from FIT image at 0x%08lx\n",
+ fit_uname, addr);
+ } else if (fit_parse_conf(argv[1], image_load_addr, &addr, &confname)) {
+ debug("* source: config '%s' from FIT image at 0x%08lx\n",
+ confname, addr);
+#endif
+ } else {
+ addr = hextoul(argv[1], NULL);
+ debug("* source: cmdline image address = 0x%08lx\n", addr);
+ }
+
+ printf ("## Executing script at %08lx\n", addr);
+ rcode = cmd_source_script(addr, fit_uname, confname);
+ return rcode;
+}
+
+U_BOOT_LONGHELP(source,
+#if defined(CONFIG_FIT)
+ "[<addr>][:[<image>]|#[<config>]]\n"
+ "\t- Run script starting at addr\n"
+ "\t- A FIT config name or subimage name may be specified with : or #\n"
+ "\t (like bootm). If the image or config name is omitted, the\n"
+ "\t default is used."
+#else
+ "[<addr>]\n"
+ "\t- Run script starting at addr"
+#endif
+ );
+
+U_BOOT_CMD(
+ source, 2, 0, do_source,
+ "run script from memory", source_help_text
+);
diff --git a/cmd/spawn.c b/cmd/spawn.c
new file mode 100644
index 00000000000..8829aa9728d
--- /dev/null
+++ b/cmd/spawn.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ */
+
+#include <command.h>
+#include <console.h>
+#include <env.h>
+#include <malloc.h>
+#include <vsprintf.h>
+#include <uthread.h>
+
+/* Spawn arguments and job index */
+struct spa {
+ int argc;
+ char **argv;
+ unsigned int job_idx;
+};
+
+/*
+ * uthread group identifiers for each running job
+ * 0: job slot available, != 0: uthread group id
+ * Note that job[0] is job_id 1, job[1] is job_id 2 etc.
+ */
+static unsigned int job[CONFIG_CMD_SPAWN_NUM_JOBS];
+/* Return values of the commands run as jobs */
+static enum command_ret_t job_ret[CONFIG_CMD_SPAWN_NUM_JOBS];
+
+static void spa_free(struct spa *spa)
+{
+ int i;
+
+ if (!spa)
+ return;
+
+ for (i = 0; i < spa->argc; i++)
+ free(spa->argv[i]);
+ free(spa->argv);
+ free(spa);
+}
+
+static struct spa *spa_create(int argc, char *const argv[])
+{
+ struct spa *spa;
+ int i;
+
+ spa = calloc(1, sizeof(*spa));
+ if (!spa)
+ return NULL;
+ spa->argc = argc;
+ spa->argv = malloc(argc * sizeof(char *));
+ if (!spa->argv)
+ goto err;
+ for (i = 0; i < argc; i++) {
+ spa->argv[i] = strdup(argv[i]);
+ if (!spa->argv[i])
+ goto err;
+ }
+ return spa;
+err:
+ spa_free(spa);
+ return NULL;
+}
+
+static void spawn_thread(void *arg)
+{
+ struct spa *spa = (struct spa *)arg;
+ ulong cycles = 0;
+ int repeatable = 0;
+
+ job_ret[spa->job_idx] = cmd_process(0, spa->argc, spa->argv,
+ &repeatable, &cycles);
+ spa_free(spa);
+}
+
+static unsigned int next_job_id(void)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_CMD_SPAWN_NUM_JOBS; i++)
+ if (!job[i])
+ return i + 1;
+
+ /* No job available */
+ return 0;
+}
+
+static void refresh_jobs(void)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_CMD_SPAWN_NUM_JOBS; i++)
+ if (job[i] && uthread_grp_done(job[i]))
+ job[i] = 0;
+
+}
+
+static int do_spawn(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ unsigned int id;
+ unsigned int idx;
+ struct spa *spa;
+ int ret;
+
+ if (argc == 1)
+ return CMD_RET_USAGE;
+
+ spa = spa_create(argc - 1, argv + 1);
+ if (!spa)
+ return CMD_RET_FAILURE;
+
+ refresh_jobs();
+
+ id = next_job_id();
+ if (!id)
+ return CMD_RET_FAILURE;
+ idx = id - 1;
+
+ job[idx] = uthread_grp_new_id();
+
+ ret = uthread_create(NULL, spawn_thread, spa, 0, job[idx]);
+ if (ret) {
+ job[idx] = 0;
+ return CMD_RET_FAILURE;
+ }
+
+ ret = env_set_ulong("job_id", id);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(spawn, CONFIG_SYS_MAXARGS, 0, do_spawn,
+ "run commands and summarize execution time",
+ "command [args...]\n");
+
+static enum command_ret_t wait_job(unsigned int idx)
+{
+ int prev = disable_ctrlc(false);
+
+ while (!uthread_grp_done(job[idx])) {
+ if (ctrlc()) {
+ puts("<INTERRUPT>\n");
+ disable_ctrlc(prev);
+ return CMD_RET_FAILURE;
+ }
+ uthread_schedule();
+ }
+
+ job[idx] = 0;
+ disable_ctrlc(prev);
+
+ return job_ret[idx];
+}
+
+static int do_wait(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ enum command_ret_t ret = CMD_RET_SUCCESS;
+ unsigned long id;
+ unsigned int idx;
+ int i;
+
+ if (argc == 1) {
+ for (i = 0; i < CONFIG_CMD_SPAWN_NUM_JOBS; i++)
+ if (job[i])
+ ret = wait_job(i);
+ } else {
+ for (i = 1; i < argc; i++) {
+ id = dectoul(argv[i], NULL);
+ if (id < 1 || id > CONFIG_CMD_SPAWN_NUM_JOBS)
+ return CMD_RET_USAGE;
+ idx = (int)id - 1;
+ ret = wait_job(idx);
+ }
+ }
+
+ return ret;
+}
+
+U_BOOT_CMD(wait, CONFIG_SYS_MAXARGS, 0, do_wait,
+ "wait for one or more jobs to complete",
+ "[job_id ...]\n"
+ " - Wait until all specified jobs have exited and return the\n"
+ " exit status of the last job waited for. When no job_id is\n"
+ " given, wait for all the background jobs.\n");
diff --git a/cmd/spi.c b/cmd/spi.c
new file mode 100644
index 00000000000..ea30c854c21
--- /dev/null
+++ b/cmd/spi.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2002
+ * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
+ */
+
+/*
+ * SPI Read/Write Utilities
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <errno.h>
+#include <spi.h>
+
+/*-----------------------------------------------------------------------
+ * Definitions
+ */
+
+#ifndef MAX_SPI_BYTES
+# define MAX_SPI_BYTES 32 /* Maximum number of bytes we can handle */
+#endif
+
+/*
+ * Values from last command.
+ */
+static unsigned int bus;
+static unsigned int cs;
+static unsigned int mode;
+static unsigned int freq;
+static int bitlen;
+static uchar dout[MAX_SPI_BYTES];
+static uchar din[MAX_SPI_BYTES];
+
+static int do_spi_xfer(int bus, int cs)
+{
+ struct spi_slave *slave;
+ int ret = 0;
+
+#if CONFIG_IS_ENABLED(DM_SPI)
+ char name[30], *str;
+ struct udevice *dev;
+
+ snprintf(name, sizeof(name), "generic_%d:%d", bus, cs);
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+ ret = _spi_get_bus_and_cs(bus, cs, freq, mode, "spi_generic_drv",
+ str, &dev, &slave);
+ if (ret)
+ return ret;
+#else
+ slave = spi_setup_slave(bus, cs, freq, mode);
+ if (!slave) {
+ printf("Invalid device %d:%d\n", bus, cs);
+ return -EINVAL;
+ }
+#endif
+
+ ret = spi_claim_bus(slave);
+ if (ret)
+ goto done;
+ ret = spi_xfer(slave, bitlen, dout, din,
+ SPI_XFER_BEGIN | SPI_XFER_END);
+#if !CONFIG_IS_ENABLED(DM_SPI)
+ /* We don't get an error code in this case */
+ if (ret)
+ ret = -EIO;
+#endif
+ if (ret) {
+ printf("Error %d during SPI transaction\n", ret);
+ } else {
+ int j;
+
+ for (j = 0; j < ((bitlen + 7) / 8); j++)
+ printf("%02X", din[j]);
+ printf("\n");
+ }
+done:
+ spi_release_bus(slave);
+#if !CONFIG_IS_ENABLED(DM_SPI)
+ spi_free_slave(slave);
+#endif
+
+ return ret;
+}
+
+/*
+ * SPI read/write
+ *
+ * Syntax:
+ * spi {dev} {num_bits} {dout}
+ * {dev} is the device number for controlling chip select (see TBD)
+ * {num_bits} is the number of bits to send & receive (base 10)
+ * {dout} is a hexadecimal string of data to send
+ * The command prints out the hexadecimal string received via SPI.
+ */
+
+int do_spi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char *cp = 0;
+ uchar tmp;
+ int j;
+
+ /*
+ * We use the last specified parameters, unless new ones are
+ * entered.
+ */
+ if (freq == 0)
+ freq = 1000000;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0)
+ {
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (argc >= 2) {
+ mode = CONFIG_DEFAULT_SPI_MODE;
+ bus = dectoul(argv[1], &cp);
+ if (*cp == ':') {
+ cs = dectoul(cp + 1, &cp);
+ } else {
+ cs = bus;
+ bus = CONFIG_DEFAULT_SPI_BUS;
+ }
+ if (*cp == '.')
+ mode = dectoul(cp + 1, &cp);
+ if (*cp == '@')
+ freq = dectoul(cp + 1, &cp);
+ }
+ if (argc >= 3)
+ bitlen = dectoul(argv[2], NULL);
+ if (argc >= 4) {
+ cp = argv[3];
+ for(j = 0; *cp; j++, cp++) {
+ tmp = *cp - '0';
+ if(tmp > 9)
+ tmp -= ('A' - '0') - 10;
+ if(tmp > 15)
+ tmp -= ('a' - 'A');
+ if(tmp > 15) {
+ printf("Hex conversion error on %c\n", *cp);
+ return 1;
+ }
+ if((j % 2) == 0)
+ dout[j / 2] = (tmp << 4);
+ else
+ dout[j / 2] |= tmp;
+ }
+ }
+ }
+
+ if ((bitlen < 0) || (bitlen > (MAX_SPI_BYTES * 8))) {
+ printf("Invalid bitlen %d\n", bitlen);
+ return 1;
+ }
+
+ if (do_spi_xfer(bus, cs))
+ return 1;
+
+ return 0;
+}
+
+/***************************************************/
+
+U_BOOT_CMD(
+ sspi, 5, 1, do_spi,
+ "SPI utility command",
+ "[<bus>:]<cs>[.<mode>][@<freq>] <bit_len> <dout> - Send and receive bits\n"
+ "<bus> - Identifies the SPI bus\n"
+ "<cs> - Identifies the chip select\n"
+ "<mode> - Identifies the SPI mode to use\n"
+ "<freq> - Identifies the SPI bus frequency in Hz\n"
+ "<bit_len> - Number of bits to send (base 10)\n"
+ "<dout> - Hexadecimal string that gets sent"
+);
diff --git a/cmd/spl.c b/cmd/spl.c
new file mode 100644
index 00000000000..f591dc07fb6
--- /dev/null
+++ b/cmd/spl.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011
+ * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de>
+ */
+
+#include <command.h>
+#include <cmd_spl.h>
+#include <env.h>
+#include <image.h>
+#include <log.h>
+#include <asm/global_data.h>
+#include <linux/libfdt.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char **subcmd_list[] = {
+
+ [SPL_EXPORT_FDT] = (const char * []) {
+#ifdef CONFIG_OF_LIBFDT
+ "start",
+ "loados",
+ #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
+ "ramdisk",
+ #endif
+ "fdt",
+ "cmdline",
+ "bdt",
+ "prep",
+#endif
+ NULL,
+ },
+ NULL
+};
+
+/* Calls bootm with the parameters given */
+static int call_bootm(int argc, char *const argv[], const char *subcommand[])
+{
+ char *bootm_argv[5];
+
+ int i = 0;
+ int ret = 0;
+ int j;
+
+ /* create paramter array */
+ bootm_argv[0] = "do_bootm";
+ switch (argc) {
+ case 3:
+ bootm_argv[4] = argv[2]; /* fdt addr */
+ fallthrough;
+ case 2:
+ bootm_argv[3] = argv[1]; /* initrd addr */
+ fallthrough;
+ case 1:
+ bootm_argv[2] = argv[0]; /* kernel addr */
+ }
+
+ /*
+ * - do the work -
+ * exec subcommands of do_bootm to init the images
+ * data structure
+ */
+ while (subcommand[i] != NULL) {
+ bootm_argv[1] = (char *)subcommand[i];
+ debug("args %d: %s %s ", argc, bootm_argv[0], bootm_argv[1]);
+ for (j = 0; j < argc; j++)
+ debug("%s ", bootm_argv[j + 2]);
+ debug("\n");
+
+ ret = do_bootm(find_cmd("do_bootm"), 0, argc+2,
+ bootm_argv);
+ debug("Subcommand retcode: %d\n", ret);
+ i++;
+ }
+
+ if (ret) {
+ printf("ERROR prep subcommand failed!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct cmd_tbl cmd_spl_export_sub[] = {
+ U_BOOT_CMD_MKENT(fdt, 0, 1, (void *)SPL_EXPORT_FDT, "", ""),
+};
+
+static int spl_export(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const struct cmd_tbl *c;
+
+ if (argc < 2) /* no subcommand */
+ return cmd_usage(cmdtp);
+
+ c = find_cmd_tbl(argv[1], &cmd_spl_export_sub[0],
+ ARRAY_SIZE(cmd_spl_export_sub));
+ if ((c) && ((long)c->cmd <= SPL_EXPORT_LAST)) {
+ argc -= 2;
+ argv += 2;
+ if (call_bootm(argc, argv, subcmd_list[(long)c->cmd]))
+ return -1;
+ switch ((long)c->cmd) {
+#ifdef CONFIG_OF_LIBFDT
+ case SPL_EXPORT_FDT:
+ printf("Argument image is now in RAM: 0x%p\n",
+ (void *)images.ft_addr);
+ env_set_addr("fdtargsaddr", images.ft_addr);
+ env_set_hex("fdtargslen", fdt_totalsize(images.ft_addr));
+#ifdef CONFIG_CMD_SPL_WRITE_SIZE
+ if (fdt_totalsize(images.ft_addr) >
+ CONFIG_CMD_SPL_WRITE_SIZE)
+ puts("WARN: FDT size > CMD_SPL_WRITE_SIZE\n");
+#endif
+ break;
+#endif
+ }
+ } else {
+ /* Unrecognized command */
+ return cmd_usage(cmdtp);
+ }
+
+ return 0;
+}
+
+static struct cmd_tbl cmd_spl_sub[] = {
+ U_BOOT_CMD_MKENT(export, 0, 1, (void *)SPL_EXPORT, "", ""),
+};
+
+static int do_spl(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ const struct cmd_tbl *c;
+ int cmd;
+
+ if (argc < 2) /* no subcommand */
+ return cmd_usage(cmdtp);
+
+ c = find_cmd_tbl(argv[1], &cmd_spl_sub[0], ARRAY_SIZE(cmd_spl_sub));
+ if (c) {
+ cmd = (long)c->cmd;
+ switch (cmd) {
+ case SPL_EXPORT:
+ argc--;
+ argv++;
+ if (spl_export(cmdtp, flag, argc, argv))
+ printf("Subcommand failed\n");
+ break;
+ default:
+ /* unrecognized command */
+ return cmd_usage(cmdtp);
+ }
+ } else {
+ /* Unrecognized command */
+ return cmd_usage(cmdtp);
+ }
+ return 0;
+}
+
+U_BOOT_CMD(
+ spl, 6 , 1, do_spl, "SPL configuration",
+ "export fdt [kernel_addr] [initrd_addr] [fdt_addr]\n"
+ "\tkernel_addr\taddress where a kernel image is stored.\n"
+ "\t\t\tkernel is loaded as part of the boot process, but it is not started.\n"
+ "\tinitrd_addr\taddress of initial ramdisk\n"
+ "\t\t\tcan be set to \"-\" if fdt_addr without initrd_addr is used.\n"
+ "\tfdt_addr\tthe address of the device tree.\n"
+ );
diff --git a/cmd/sqfs.c b/cmd/sqfs.c
new file mode 100644
index 00000000000..107038c4cf2
--- /dev/null
+++ b/cmd/sqfs.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ *
+ * squashfs.c: implements SquashFS related commands
+ */
+
+#include <command.h>
+#include <fs.h>
+#include <squashfs.h>
+
+static int do_sqfs_ls(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ return do_ls(cmdtp, flag, argc, argv, FS_TYPE_SQUASHFS);
+}
+
+U_BOOT_CMD(sqfsls, 4, 1, do_sqfs_ls,
+ "List files in directory. Default: root (/).",
+ "<interface> [<dev[:part]>] [directory]\n"
+ " - list files from 'dev' on 'interface' in 'directory'\n"
+);
+
+static int do_sqfs_load(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ return do_load(cmdtp, flag, argc, argv, FS_TYPE_SQUASHFS);
+}
+
+U_BOOT_CMD(sqfsload, 7, 0, do_sqfs_load,
+ "load binary file from a SquashFS filesystem",
+ "<interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]\n"
+ " - Load binary file 'filename' from 'dev' on 'interface'\n"
+ " to address 'addr' from SquashFS filesystem.\n"
+ " 'pos' gives the file position to start loading from.\n"
+ " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
+ " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
+ " the load stops on end of file.\n"
+ " If either 'pos' or 'bytes' are not aligned to\n"
+ " ARCH_DMA_MINALIGN then a misaligned buffer warning will\n"
+ " be printed and performance will suffer for the load."
+);
diff --git a/cmd/stackprot_test.c b/cmd/stackprot_test.c
new file mode 100644
index 00000000000..78e9beba5bf
--- /dev/null
+++ b/cmd/stackprot_test.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Broadcom
+ */
+
+#include <command.h>
+#include <linux/string.h>
+
+static int do_test_stackprot_fail(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ /*
+ * In order to avoid having the compiler optimize away the stack smashing
+ * we need to do a little something here.
+ */
+ char a[128];
+
+ memset(a, 0xa5, 512);
+
+ printf("We have smashed our stack as this should not exceed 128: sizeof(a) = %zd\n",
+ strlen(a));
+
+ return 0;
+}
+
+U_BOOT_CMD(stackprot_test, 1, 1, do_test_stackprot_fail,
+ "test stack protector fail", "");
diff --git a/cmd/strings.c b/cmd/strings.c
new file mode 100644
index 00000000000..beac2a6e6b3
--- /dev/null
+++ b/cmd/strings.c
@@ -0,0 +1,47 @@
+/*
+ * cmd_strings.c - just like `strings` command
+ *
+ * Copyright (c) 2008 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <config.h>
+#include <command.h>
+#include <vsprintf.h>
+#include <linux/string.h>
+
+static char *start_addr, *last_addr;
+
+int do_strings(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ if (argc == 1)
+ return CMD_RET_USAGE;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ start_addr = (char *)hextoul(argv[1], NULL);
+ if (argc > 2)
+ last_addr = (char *)hextoul(argv[2], NULL);
+ else
+ last_addr = (char *)-1;
+ }
+
+ char *addr = start_addr;
+ do {
+ puts(addr);
+ puts("\n");
+ addr += strlen(addr) + 1;
+ } while (addr[0] && addr < last_addr);
+
+ last_addr = addr + (last_addr - start_addr);
+ start_addr = addr;
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ strings, 3, 1, do_strings,
+ "display strings",
+ "<addr> [byte count]\n"
+ " - display strings at <addr> for at least [byte count] or first double NUL"
+);
diff --git a/cmd/sysboot.c b/cmd/sysboot.c
new file mode 100644
index 00000000000..93d4a400830
--- /dev/null
+++ b/cmd/sysboot.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <command.h>
+#include <env.h>
+#include <fs.h>
+#include <pxe_utils.h>
+#include <vsprintf.h>
+
+/**
+ * struct sysboot_info - useful information for sysboot helpers
+ *
+ * @fstype: Filesystem type (FS_TYPE_...)
+ * @ifname: Interface name (e.g. "ide", "scsi")
+ * @dev_part_str is in the format:
+ * <dev>.<hw_part>:<part> where <dev> is the device number,
+ * <hw_part> is the optional hardware partition number and
+ * <part> is the partition number
+ */
+struct sysboot_info {
+ int fstype;
+ const char *ifname;
+ const char *dev_part_str;
+};
+
+static int sysboot_read_file(struct pxe_context *ctx, const char *file_path,
+ char *file_addr, enum bootflow_img_t type,
+ ulong *sizep)
+{
+ struct sysboot_info *info = ctx->userdata;
+ loff_t len_read;
+ ulong addr;
+ int ret;
+
+ addr = simple_strtoul(file_addr, NULL, 16);
+ ret = fs_set_blk_dev(info->ifname, info->dev_part_str, info->fstype);
+ if (ret)
+ return ret;
+ ret = fs_read(file_path, addr, 0, 0, &len_read);
+ if (ret)
+ return ret;
+ *sizep = len_read;
+
+ return 0;
+}
+
+/*
+ * Boots a system using a local disk syslinux/extlinux file
+ *
+ * Returns 0 on success, 1 on error.
+ */
+static int do_sysboot(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ unsigned long pxefile_addr_r;
+ struct pxe_context ctx;
+ char *pxefile_addr_str;
+ struct sysboot_info info;
+ char *filename;
+ int prompt = 0;
+ int ret;
+
+ if (argc > 1 && strstr(argv[1], "-p")) {
+ prompt = 1;
+ argc--;
+ argv++;
+ }
+
+ if (argc < 4)
+ return cmd_usage(cmdtp);
+
+ if (argc < 5) {
+ pxefile_addr_str = from_env("pxefile_addr_r");
+ if (!pxefile_addr_str)
+ return 1;
+ } else {
+ pxefile_addr_str = argv[4];
+ }
+
+ if (argc < 6) {
+ filename = env_get("bootfile");
+ if (!filename) {
+ printf("Specify a filename or set the ${bootfile} environment variable\n");
+ return 1;
+ }
+ } else {
+ filename = argv[5];
+ env_set("bootfile", filename);
+ }
+
+ if (strstr(argv[3], "ext2")) {
+ info.fstype = FS_TYPE_EXT;
+ } else if (strstr(argv[3], "fat")) {
+ info.fstype = FS_TYPE_FAT;
+ } else if (strstr(argv[3], "any")) {
+ info.fstype = FS_TYPE_ANY;
+ } else {
+ printf("Invalid filesystem: %s\n", argv[3]);
+ return 1;
+ }
+ info.ifname = argv[1];
+ info.dev_part_str = argv[2];
+
+ if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
+ printf("Invalid pxefile address: %s\n", pxefile_addr_str);
+ return 1;
+ }
+
+ if (pxe_setup_ctx(&ctx, cmdtp, sysboot_read_file, &info, true,
+ filename, false, false)) {
+ printf("Out of memory\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (get_pxe_file(&ctx, filename, pxefile_addr_r)
+ < 0) {
+ printf("Error reading config file\n");
+ pxe_destroy_ctx(&ctx);
+ return 1;
+ }
+
+ ret = pxe_process(&ctx, pxefile_addr_r, prompt);
+ pxe_destroy_ctx(&ctx);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+U_BOOT_CMD(sysboot, 7, 1, do_sysboot,
+ "command to get and boot from syslinux files",
+ "[-p] <interface> <dev[:part]> <ext2|fat|any> [addr] [filename]\n"
+ " - load and parse syslinux menu file 'filename' from ext2, fat\n"
+ " or any filesystem on 'dev' on 'interface' to address 'addr'"
+);
diff --git a/cmd/tcpm.c b/cmd/tcpm.c
new file mode 100644
index 00000000000..39578f6ccc0
--- /dev/null
+++ b/cmd/tcpm.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2024 Collabora
+ */
+
+#include <command.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/uclass-internal.h>
+#include <usb/tcpm.h>
+
+#define LIMIT_DEV 32
+#define LIMIT_PARENT 20
+
+static struct udevice *currdev;
+
+static int do_dev(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int devnum, ret;
+
+ switch (argc) {
+ case 2:
+ devnum = (int)dectoul(argv[1], NULL);
+ ret = tcpm_get(devnum, &currdev);
+ if (ret) {
+ log_err("Can't get TCPM %d: %d (%s)!\n", devnum, ret, errno_str(ret));
+ return CMD_RET_FAILURE;
+ }
+ case 1:
+ if (!currdev) {
+ log_err("TCPM device is not set!\n\n");
+ return CMD_RET_USAGE;
+ }
+
+ printf("dev: %d @ %s\n", dev_seq(currdev), currdev->name);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int ret, err = 0;
+
+ printf("| ID | %-*.*s| %-*.*s| %s @ %s\n",
+ LIMIT_DEV, LIMIT_DEV, "Name",
+ LIMIT_PARENT, LIMIT_PARENT, "Parent name",
+ "Parent uclass", "seq");
+
+ for (ret = uclass_first_device_check(UCLASS_TCPM, &dev); dev;
+ ret = uclass_next_device_check(&dev)) {
+ if (ret)
+ err = ret;
+
+ printf("| %2d | %-*.*s| %-*.*s| %s @ %d | status: %i\n",
+ dev_seq(dev),
+ LIMIT_DEV, LIMIT_DEV, dev->name,
+ LIMIT_PARENT, LIMIT_PARENT, dev->parent->name,
+ dev_get_uclass_name(dev->parent), dev_seq(dev->parent),
+ ret);
+ }
+
+ if (err)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+int do_print_info(struct udevice *dev)
+{
+ enum typec_orientation orientation = tcpm_get_orientation(dev);
+ const char *state = tcpm_get_state(dev);
+ int pd_rev = tcpm_get_pd_rev(dev);
+ int mv = tcpm_get_voltage(dev);
+ int ma = tcpm_get_current(dev);
+ enum typec_role pwr_role = tcpm_get_pwr_role(dev);
+ enum typec_data_role data_role = tcpm_get_data_role(dev);
+ bool connected = tcpm_is_connected(dev);
+
+ if (!connected) {
+ printf("TCPM State: %s\n", state);
+ return 0;
+ }
+
+ printf("Orientation: %s\n", typec_orientation_name[orientation]);
+ printf("PD Revision: %s\n", typec_pd_rev_name[pd_rev]);
+ printf("Power Role: %s\n", typec_role_name[pwr_role]);
+ printf("Data Role: %s\n", typec_data_role_name[data_role]);
+ printf("Voltage: %2d.%03d V\n", mv / 1000, mv % 1000);
+ printf("Current: %2d.%03d A\n", ma / 1000, ma % 1000);
+
+ return 0;
+}
+
+static int do_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (!currdev) {
+ printf("First, set the TCPM device!\n");
+ return CMD_RET_USAGE;
+ }
+
+ return do_print_info(currdev);
+}
+
+static struct cmd_tbl subcmd[] = {
+ U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
+ U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
+ U_BOOT_CMD_MKENT(info, 1, 1, do_info, "", ""),
+};
+
+static int do_tcpm(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *cmd;
+
+ argc--;
+ argv++;
+
+ cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
+ if (!cmd || argc > cmd->maxargs)
+ return CMD_RET_USAGE;
+
+ return cmd->cmd(cmdtp, flag, argc, argv);
+}
+
+ /**************************************************/
+
+U_BOOT_CMD(tcpm, CONFIG_SYS_MAXARGS, 1, do_tcpm,
+ "TCPM sub-system",
+ "list - list TCPM devices\n"
+ "tcpm dev [ID] - show or [set] operating TCPM device\n"
+ "tcpm info - dump information\n"
+);
diff --git a/cmd/temperature.c b/cmd/temperature.c
new file mode 100644
index 00000000000..c145d019364
--- /dev/null
+++ b/cmd/temperature.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Copyright (c) 2022 Sartura Ltd.
+ * Written by Robert Marko <robert.marko@sartura.hr>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <thermal.h>
+
+#define LIMIT_DEVNAME 30
+
+static int do_get(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int ret, temp;
+
+ if (argc < 2) {
+ printf("thermal device not selected\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = uclass_get_device_by_name(UCLASS_THERMAL, argv[1], &dev);
+ if (ret) {
+ printf("thermal device not found\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = thermal_get_temp(dev, &temp);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ printf("%s: %d mC\n", dev->name, temp);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+
+ printf("| %-*.*s| %-*.*s| %s\n",
+ LIMIT_DEVNAME, LIMIT_DEVNAME, "Device",
+ LIMIT_DEVNAME, LIMIT_DEVNAME, "Driver",
+ "Parent");
+
+ uclass_foreach_dev_probe(UCLASS_THERMAL, dev) {
+ printf("| %-*.*s| %-*.*s| %s\n",
+ LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
+ LIMIT_DEVNAME, LIMIT_DEVNAME, dev->driver->name,
+ dev->parent->name);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static struct cmd_tbl temperature_subcmd[] = {
+ U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
+ U_BOOT_CMD_MKENT(get, 2, 1, do_get, "", ""),
+};
+
+static int do_temperature(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *cmd;
+
+ argc--;
+ argv++;
+
+ cmd = find_cmd_tbl(argv[0], temperature_subcmd, ARRAY_SIZE(temperature_subcmd));
+ if (!cmd || argc > cmd->maxargs)
+ return CMD_RET_USAGE;
+
+ return cmd->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(temperature, CONFIG_SYS_MAXARGS, 1, do_temperature,
+ "thermal sensor temperature",
+ "list\t\tshow list of temperature sensors\n"
+ "get [thermal device name]\tprint temperature in degrees C"
+);
diff --git a/cmd/terminal.c b/cmd/terminal.c
new file mode 100644
index 00000000000..14610694255
--- /dev/null
+++ b/cmd/terminal.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2007 OpenMoko, Inc.
+ * Written by Harald Welte <laforge@openmoko.org>
+ */
+
+/*
+ * Boot support
+ */
+#include <command.h>
+#include <stdio_dev.h>
+#include <serial.h>
+
+int do_terminal(struct cmd_tbl *cmd, int flag, int argc, char *const argv[])
+{
+ int last_tilde = 0;
+ struct stdio_dev *dev = NULL;
+
+ if (argc < 1)
+ return -1;
+
+ /* Scan for selected output/input device */
+ dev = stdio_get_by_name(argv[1]);
+ if (!dev)
+ return -1;
+
+ if (IS_ENABLED(CONFIG_SERIAL) && !IS_ENABLED(CONFIG_DM_SERIAL))
+ serial_reinit_all();
+
+ printf("Entering terminal mode for port %s\n", dev->name);
+ puts("Use '~.' to leave the terminal and get back to u-boot\n");
+
+ while (1) {
+ int c;
+
+ /* read from console and display on serial port */
+ if (stdio_devices[0]->tstc(stdio_devices[0])) {
+ c = stdio_devices[0]->getc(stdio_devices[0]);
+ if (last_tilde == 1) {
+ if (c == '.') {
+ putc(c);
+ putc('\n');
+ break;
+ } else {
+ last_tilde = 0;
+ /* write the delayed tilde */
+ dev->putc(dev, '~');
+ /* fall-through to print current
+ * character */
+ }
+ }
+ if (c == '~') {
+ last_tilde = 1;
+ puts("[u-boot]");
+ putc(c);
+ }
+ dev->putc(dev, c);
+ }
+
+ /* read from serial port and display on console */
+ if (dev->tstc(dev)) {
+ c = dev->getc(dev);
+ putc(c);
+ }
+ }
+ return 0;
+}
+
+/***************************************************/
+
+U_BOOT_CMD(
+ terminal, 3, 1, do_terminal,
+ "start terminal emulator",
+ ""
+);
diff --git a/cmd/test.c b/cmd/test.c
new file mode 100644
index 00000000000..a9ac07e6143
--- /dev/null
+++ b/cmd/test.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2000-2009
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <fs.h>
+#include <log.h>
+#include <slre.h>
+#include <vsprintf.h>
+#include <linux/string.h>
+
+#define OP_INVALID 0
+#define OP_NOT 1
+#define OP_OR 2
+#define OP_AND 3
+#define OP_STR_EMPTY 4
+#define OP_STR_NEMPTY 5
+#define OP_STR_EQ 6
+#define OP_STR_NEQ 7
+#define OP_STR_LT 8
+#define OP_STR_GT 9
+#define OP_INT_EQ 10
+#define OP_INT_NEQ 11
+#define OP_INT_LT 12
+#define OP_INT_LE 13
+#define OP_INT_GT 14
+#define OP_INT_GE 15
+#define OP_FILE_EXISTS 16
+#define OP_REGEX 17
+
+const struct {
+ int arg;
+ const char *str;
+ int op;
+ int adv;
+} op_adv[] = {
+ {1, "=", OP_STR_EQ, 3},
+ {1, "!=", OP_STR_NEQ, 3},
+ {1, "<", OP_STR_LT, 3},
+ {1, ">", OP_STR_GT, 3},
+ {1, "-eq", OP_INT_EQ, 3},
+ {1, "-ne", OP_INT_NEQ, 3},
+ {1, "-lt", OP_INT_LT, 3},
+ {1, "-le", OP_INT_LE, 3},
+ {1, "-gt", OP_INT_GT, 3},
+ {1, "-ge", OP_INT_GE, 3},
+ {0, "!", OP_NOT, 1},
+ {0, "-o", OP_OR, 1},
+ {0, "-a", OP_AND, 1},
+ {0, "-z", OP_STR_EMPTY, 2},
+ {0, "-n", OP_STR_NEMPTY, 2},
+ {0, "-e", OP_FILE_EXISTS, 4},
+#ifdef CONFIG_REGEX
+ {1, "=~", OP_REGEX, 3},
+#endif
+};
+
+static int do_test(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char * const *ap;
+ int i, op, left, adv, expr, last_expr, last_unop, last_binop;
+
+ /* args? */
+ if (argc < 3)
+ return 1;
+
+#ifdef DEBUG
+ {
+ debug("test(%d):", argc);
+ left = 1;
+ while (argv[left])
+ debug(" '%s'", argv[left++]);
+ }
+#endif
+
+ left = argc - 1;
+ ap = argv + 1;
+ expr = 0;
+ last_unop = OP_INVALID;
+ last_binop = OP_INVALID;
+ last_expr = -1;
+ while (left > 0) {
+ for (i = 0; i < ARRAY_SIZE(op_adv); i++) {
+ if (left <= op_adv[i].arg)
+ continue;
+ if (!strcmp(ap[op_adv[i].arg], op_adv[i].str)) {
+ op = op_adv[i].op;
+ adv = op_adv[i].adv;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(op_adv)) {
+ expr = 1;
+ break;
+ }
+ if (left < adv) {
+ expr = 1;
+ break;
+ }
+
+ switch (op) {
+ case OP_STR_EMPTY:
+ expr = strlen(ap[1]) == 0 ? 1 : 0;
+ break;
+ case OP_STR_NEMPTY:
+ expr = strlen(ap[1]) == 0 ? 0 : 1;
+ break;
+ case OP_STR_EQ:
+ expr = strcmp(ap[0], ap[2]) == 0;
+ break;
+ case OP_STR_NEQ:
+ expr = strcmp(ap[0], ap[2]) != 0;
+ break;
+ case OP_STR_LT:
+ expr = strcmp(ap[0], ap[2]) < 0;
+ break;
+ case OP_STR_GT:
+ expr = strcmp(ap[0], ap[2]) > 0;
+ break;
+ case OP_INT_EQ:
+ expr = simple_strtol(ap[0], NULL, 0) ==
+ simple_strtol(ap[2], NULL, 0);
+ break;
+ case OP_INT_NEQ:
+ expr = simple_strtol(ap[0], NULL, 0) !=
+ simple_strtol(ap[2], NULL, 0);
+ break;
+ case OP_INT_LT:
+ expr = simple_strtol(ap[0], NULL, 0) <
+ simple_strtol(ap[2], NULL, 0);
+ break;
+ case OP_INT_LE:
+ expr = simple_strtol(ap[0], NULL, 0) <=
+ simple_strtol(ap[2], NULL, 0);
+ break;
+ case OP_INT_GT:
+ expr = simple_strtol(ap[0], NULL, 0) >
+ simple_strtol(ap[2], NULL, 0);
+ break;
+ case OP_INT_GE:
+ expr = simple_strtol(ap[0], NULL, 0) >=
+ simple_strtol(ap[2], NULL, 0);
+ break;
+ case OP_FILE_EXISTS:
+ expr = file_exists(ap[1], ap[2], ap[3], FS_TYPE_ANY);
+ break;
+#ifdef CONFIG_REGEX
+ case OP_REGEX: {
+ struct slre slre;
+
+ if (slre_compile(&slre, ap[2]) == 0) {
+ printf("Error compiling regex: %s\n", slre.err_str);
+ expr = 0;
+ break;
+ }
+
+ expr = slre_match(&slre, ap[0], strlen(ap[0]), NULL);
+ break;
+ }
+#endif
+ }
+
+ switch (op) {
+ case OP_OR:
+ last_expr = expr;
+ last_binop = OP_OR;
+ break;
+ case OP_AND:
+ last_expr = expr;
+ last_binop = OP_AND;
+ break;
+ case OP_NOT:
+ if (last_unop == OP_NOT)
+ last_unop = OP_INVALID;
+ else
+ last_unop = OP_NOT;
+ break;
+ default:
+ if (last_unop == OP_NOT) {
+ expr = !expr;
+ last_unop = OP_INVALID;
+ }
+
+ if (last_binop == OP_OR)
+ expr = last_expr || expr;
+ else if (last_binop == OP_AND)
+ expr = last_expr && expr;
+ last_binop = OP_INVALID;
+
+ break;
+ }
+
+ ap += adv; left -= adv;
+ }
+
+ expr = !expr;
+
+ debug (": returns %d\n", expr);
+
+ return expr;
+}
+
+#undef true
+#undef false
+
+U_BOOT_CMD(
+ test, CONFIG_SYS_MAXARGS, 1, do_test,
+ "minimal test like /bin/sh",
+ "[args..]"
+);
+
+static int do_false(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return 1;
+}
+
+U_BOOT_CMD(
+ false, CONFIG_SYS_MAXARGS, 1, do_false,
+ "do nothing, unsuccessfully",
+ NULL
+);
+
+static int do_true(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return 0;
+}
+
+U_BOOT_CMD(
+ true, CONFIG_SYS_MAXARGS, 1, do_true,
+ "do nothing, successfully",
+ NULL
+);
diff --git a/cmd/thordown.c b/cmd/thordown.c
new file mode 100644
index 00000000000..70061bf8d4c
--- /dev/null
+++ b/cmd/thordown.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * cmd_thordown.c -- USB TIZEN "THOR" Downloader gadget
+ *
+ * Copyright (C) 2013 Lukasz Majewski <l.majewski@samsung.com>
+ * All rights reserved.
+ */
+
+#include <command.h>
+#include <thor.h>
+#include <dfu.h>
+#include <g_dnl.h>
+#include <usb.h>
+#include <linux/printk.h>
+
+int do_thor_down(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char *interface, *devstring;
+ int controller_index;
+ struct udevice *udc;
+ int ret;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ puts("TIZEN \"THOR\" Downloader\n");
+
+ interface = argv[2];
+ devstring = argv[3];
+
+ ret = dfu_init_env_entities(interface, devstring);
+ if (ret)
+ goto done;
+
+ controller_index = simple_strtoul(argv[1], NULL, 0);
+ ret = udc_device_get_by_index(controller_index, &udc);
+ if (ret) {
+ pr_err("USB init failed: %d\n", ret);
+ ret = CMD_RET_FAILURE;
+ goto exit;
+ }
+
+ ret = g_dnl_register("usb_dnl_thor");
+ if (ret) {
+ pr_err("g_dnl_register failed %d\n", ret);
+ ret = CMD_RET_FAILURE;
+ goto exit;
+ }
+
+ ret = thor_init(udc);
+ if (ret) {
+ pr_err("THOR DOWNLOAD failed: %d\n", ret);
+ ret = CMD_RET_FAILURE;
+ goto exit;
+ }
+
+ do {
+ ret = thor_handle(udc);
+ if (ret == THOR_DFU_REINIT_NEEDED) {
+ dfu_free_entities();
+ ret = dfu_init_env_entities(interface, devstring);
+ }
+ if (ret) {
+ pr_err("THOR failed: %d\n", ret);
+ ret = CMD_RET_FAILURE;
+ goto exit;
+ }
+ } while (ret == 0);
+exit:
+ g_dnl_unregister();
+ udc_device_put(udc);
+done:
+ dfu_free_entities();
+
+ return ret;
+}
+
+U_BOOT_CMD(thordown, CONFIG_SYS_MAXARGS, 1, do_thor_down,
+ "TIZEN \"THOR\" downloader",
+ "<USB_controller> <interface> <dev>\n"
+ " - device software upgrade via LTHOR TIZEN download\n"
+ " program via <USB_controller> on device <dev>,\n"
+ " attached to interface <interface>\n"
+);
diff --git a/cmd/ti/Kconfig b/cmd/ti/Kconfig
new file mode 100644
index 00000000000..9442c9993c1
--- /dev/null
+++ b/cmd/ti/Kconfig
@@ -0,0 +1,19 @@
+menu "TI specific command line interface"
+
+config CMD_DDR3
+ bool "command for verifying DDR features"
+ depends on ARCH_KEYSTONE || DRA7XX
+ help
+ Support for testing ddr3 on TI platforms. This command
+ supports memory verification, memory comapre and ecc
+ verification if supported.
+
+config CMD_PD
+ bool "command for verifying power domains"
+ depends on TI_POWER_DOMAIN
+ help
+ Debug command for K3 power domains. For this to work, the
+ K3 power domain driver must be enabled for the u-boot; by
+ default it is only enabled for SPL.
+
+endmenu
diff --git a/cmd/ti/Makefile b/cmd/ti/Makefile
new file mode 100644
index 00000000000..5f9c64f598a
--- /dev/null
+++ b/cmd/ti/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/
+
+obj-$(CONFIG_CMD_DDR3) += ddr3.o
+obj-$(CONFIG_CMD_PD) += pd.o
diff --git a/cmd/ti/ddr3.c b/cmd/ti/ddr3.c
new file mode 100644
index 00000000000..70ce53d01e8
--- /dev/null
+++ b/cmd/ti/ddr3.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EMIF: DDR3 test commands
+ *
+ * Copyright (C) 2012-2017 Texas Instruments Incorporated, <www.ti.com>
+ */
+
+#include <cpu_func.h>
+#include <env.h>
+#include <init.h>
+#include <log.h>
+#include <asm/arch/hardware.h>
+#include <asm/cache.h>
+#include <asm/emif.h>
+#include <command.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifdef CONFIG_ARCH_KEYSTONE
+#include <asm/arch/ddr3.h>
+#define DDR_MIN_ADDR CFG_SYS_SDRAM_BASE
+#define STACKSIZE (512 << 10) /* 512 KiB */
+
+#define DDR_REMAP_ADDR 0x80000000
+#define ECC_START_ADDR1 ((DDR_MIN_ADDR - DDR_REMAP_ADDR) >> 17)
+
+#define ECC_END_ADDR1 (((gd->start_addr_sp - DDR_REMAP_ADDR - \
+ STACKSIZE) >> 17) - 2)
+#endif
+
+#define DDR_TEST_BURST_SIZE 1024
+
+static int ddr_memory_test(u32 start_address, u32 end_address, int quick)
+{
+ u32 index_start, value, index;
+
+ index_start = start_address;
+
+ while (1) {
+ /* Write a pattern */
+ for (index = index_start;
+ index < index_start + DDR_TEST_BURST_SIZE;
+ index += 4)
+ __raw_writel(index, index);
+
+ /* Read and check the pattern */
+ for (index = index_start;
+ index < index_start + DDR_TEST_BURST_SIZE;
+ index += 4) {
+ value = __raw_readl(index);
+ if (value != index) {
+ printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n",
+ index, value, __raw_readl(index));
+
+ return -1;
+ }
+ }
+
+ index_start += DDR_TEST_BURST_SIZE;
+ if (index_start >= end_address)
+ break;
+
+ if (quick)
+ continue;
+
+ /* Write a pattern for complementary values */
+ for (index = index_start;
+ index < index_start + DDR_TEST_BURST_SIZE;
+ index += 4)
+ __raw_writel((u32)~index, index);
+
+ /* Read and check the pattern */
+ for (index = index_start;
+ index < index_start + DDR_TEST_BURST_SIZE;
+ index += 4) {
+ value = __raw_readl(index);
+ if (value != ~index) {
+ printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n",
+ index, value, __raw_readl(index));
+
+ return -1;
+ }
+ }
+
+ index_start += DDR_TEST_BURST_SIZE;
+ if (index_start >= end_address)
+ break;
+
+ /* Write a pattern */
+ for (index = index_start;
+ index < index_start + DDR_TEST_BURST_SIZE;
+ index += 2)
+ __raw_writew((u16)index, index);
+
+ /* Read and check the pattern */
+ for (index = index_start;
+ index < index_start + DDR_TEST_BURST_SIZE;
+ index += 2) {
+ value = __raw_readw(index);
+ if (value != (u16)index) {
+ printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n",
+ index, value, __raw_readw(index));
+
+ return -1;
+ }
+ }
+
+ index_start += DDR_TEST_BURST_SIZE;
+ if (index_start >= end_address)
+ break;
+
+ /* Write a pattern */
+ for (index = index_start;
+ index < index_start + DDR_TEST_BURST_SIZE;
+ index += 1)
+ __raw_writeb((u8)index, index);
+
+ /* Read and check the pattern */
+ for (index = index_start;
+ index < index_start + DDR_TEST_BURST_SIZE;
+ index += 1) {
+ value = __raw_readb(index);
+ if (value != (u8)index) {
+ printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n",
+ index, value, __raw_readb(index));
+
+ return -1;
+ }
+ }
+
+ index_start += DDR_TEST_BURST_SIZE;
+ if (index_start >= end_address)
+ break;
+ }
+
+ puts("ddr memory test PASSED!\n");
+ return 0;
+}
+
+static int ddr_memory_compare(u32 address1, u32 address2, u32 size)
+{
+ u32 index, value, index2, value2;
+
+ for (index = address1, index2 = address2;
+ index < address1 + size;
+ index += 4, index2 += 4) {
+ value = __raw_readl(index);
+ value2 = __raw_readl(index2);
+
+ if (value != value2) {
+ printf("ddr_memory_test: Compare failed at address = 0x%x value = 0x%x, address2 = 0x%x value2 = 0x%x\n",
+ index, value, index2, value2);
+
+ return -1;
+ }
+ }
+
+ puts("ddr memory compare PASSED!\n");
+ return 0;
+}
+
+static void ddr_check_ecc_status(void)
+{
+ struct emif_reg_struct *emif = (struct emif_reg_struct *)EMIF1_BASE;
+ u32 err_1b = readl(&emif->emif_1b_ecc_err_cnt);
+ u32 int_status = readl(&emif->emif_irqstatus_raw_sys);
+ int ecc_test = 0;
+ char *env;
+
+ env = env_get("ecc_test");
+ if (env)
+ ecc_test = simple_strtol(env, NULL, 0);
+
+ puts("ECC test Status:\n");
+ if (int_status & EMIF_INT_WR_ECC_ERR_SYS_MASK)
+ puts("\tECC test: DDR ECC write error interrupted\n");
+
+ if (int_status & EMIF_INT_TWOBIT_ECC_ERR_SYS_MASK)
+ if (!ecc_test)
+ panic("\tECC test: DDR ECC 2-bit error interrupted");
+
+ if (int_status & EMIF_INT_ONEBIT_ECC_ERR_SYS_MASK)
+ puts("\tECC test: DDR ECC 1-bit error interrupted\n");
+
+ if (err_1b)
+ printf("\tECC test: 1-bit ECC err count: 0x%x\n", err_1b);
+}
+
+static int ddr_memory_ecc_err(u32 addr, u32 ecc_err)
+{
+ struct emif_reg_struct *emif = (struct emif_reg_struct *)EMIF1_BASE;
+ u32 ecc_ctrl = readl(&emif->emif_ecc_ctrl_reg);
+ u32 val1, val2, val3;
+
+ debug("Disabling D-Cache before ECC test\n");
+ dcache_disable();
+ invalidate_dcache_all();
+
+ puts("Testing DDR ECC:\n");
+ puts("\tECC test: Disabling DDR ECC ...\n");
+ writel(0, &emif->emif_ecc_ctrl_reg);
+
+ val1 = readl(addr);
+ val2 = val1 ^ ecc_err;
+ writel(val2, addr);
+
+ val3 = readl(addr);
+#ifdef CONFIG_ARCH_KEYSTONE
+ ecc_ctrl = ECC_START_ADDR1 | (ECC_END_ADDR1 << 16);
+ writel(ecc_ctrl, EMIF1_BASE + KS2_DDR3_ECC_ADDR_RANGE1_OFFSET);
+ ddr3_enable_ecc(EMIF1_BASE, 1);
+#else
+ writel(ecc_ctrl, &emif->emif_ecc_ctrl_reg);
+#endif
+
+ printf("\tECC test: addr 0x%x, read data 0x%x, written data 0x%x, err pattern: 0x%x, read after write data 0x%x\n",
+ addr, val1, val2, ecc_err, val3);
+
+ puts("\tECC test: Enabled DDR ECC ...\n");
+
+ val1 = readl(addr);
+ printf("\tECC test: addr 0x%x, read data 0x%x\n", addr, val1);
+
+ ddr_check_ecc_status();
+
+ debug("Enabling D-cache back after ECC test\n");
+ enable_caches();
+
+ return 0;
+}
+
+static int is_addr_valid(u32 addr)
+{
+ struct emif_reg_struct *emif = (struct emif_reg_struct *)EMIF1_BASE;
+ u32 start_addr, end_addr, range, ecc_ctrl;
+
+#ifdef CONFIG_ARCH_KEYSTONE
+ ecc_ctrl = EMIF_ECC_REG_ECC_ADDR_RGN_1_EN_MASK;
+ range = ECC_START_ADDR1 | (ECC_END_ADDR1 << 16);
+#else
+ ecc_ctrl = readl(&emif->emif_ecc_ctrl_reg);
+ range = readl(&emif->emif_ecc_address_range_1);
+#endif
+
+ /* Check in ecc address range 1 */
+ if (ecc_ctrl & EMIF_ECC_REG_ECC_ADDR_RGN_1_EN_MASK) {
+ start_addr = ((range & EMIF_ECC_REG_ECC_START_ADDR_MASK) << 16)
+ + CFG_SYS_SDRAM_BASE;
+ end_addr = (range & EMIF_ECC_REG_ECC_END_ADDR_MASK) + 0xFFFF +
+ CFG_SYS_SDRAM_BASE;
+ if ((addr >= start_addr) && (addr <= end_addr))
+ /* addr within ecc address range 1 */
+ return 1;
+ }
+
+ /* Check in ecc address range 2 */
+ if (ecc_ctrl & EMIF_ECC_REG_ECC_ADDR_RGN_2_EN_MASK) {
+ range = readl(&emif->emif_ecc_address_range_2);
+ start_addr = ((range & EMIF_ECC_REG_ECC_START_ADDR_MASK) << 16)
+ + CFG_SYS_SDRAM_BASE;
+ end_addr = (range & EMIF_ECC_REG_ECC_END_ADDR_MASK) + 0xFFFF +
+ CFG_SYS_SDRAM_BASE;
+ if ((addr >= start_addr) && (addr <= end_addr))
+ /* addr within ecc address range 2 */
+ return 1;
+ }
+
+ return 0;
+}
+
+static int is_ecc_enabled(void)
+{
+ struct emif_reg_struct *emif = (struct emif_reg_struct *)EMIF1_BASE;
+ u32 ecc_ctrl = readl(&emif->emif_ecc_ctrl_reg);
+
+ return (ecc_ctrl & EMIF_ECC_CTRL_REG_ECC_EN_MASK) &&
+ (ecc_ctrl & EMIF_ECC_REG_RMW_EN_MASK);
+}
+
+static int do_ddr_test(struct cmd_tbl *cmdtp,
+ int flag, int argc, char *const argv[])
+{
+ u32 start_addr, end_addr, size, ecc_err;
+
+ if ((argc == 4) && (strncmp(argv[1], "ecc_err", 8) == 0)) {
+ if (!is_ecc_enabled()) {
+ puts("ECC not enabled. Please Enable ECC and try again\n");
+ return CMD_RET_FAILURE;
+ }
+
+ start_addr = hextoul(argv[2], NULL);
+ ecc_err = hextoul(argv[3], NULL);
+
+ if (!is_addr_valid(start_addr)) {
+ puts("Invalid address. Please enter ECC supported address!\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ddr_memory_ecc_err(start_addr, ecc_err);
+ return 0;
+ }
+
+ if (!(((argc == 4) && (strncmp(argv[1], "test", 5) == 0)) ||
+ ((argc == 5) && (strncmp(argv[1], "compare", 8) == 0))))
+ return cmd_usage(cmdtp);
+
+ start_addr = hextoul(argv[2], NULL);
+ end_addr = hextoul(argv[3], NULL);
+
+ if ((start_addr < CFG_SYS_SDRAM_BASE) ||
+ (start_addr > (CFG_SYS_SDRAM_BASE +
+ get_effective_memsize() - 1)) ||
+ (end_addr < CFG_SYS_SDRAM_BASE) ||
+ (end_addr > (CFG_SYS_SDRAM_BASE +
+ get_effective_memsize() - 1)) || (start_addr >= end_addr)) {
+ puts("Invalid start or end address!\n");
+ return cmd_usage(cmdtp);
+ }
+
+ puts("Please wait ...\n");
+ if (argc == 5) {
+ size = hextoul(argv[4], NULL);
+ ddr_memory_compare(start_addr, end_addr, size);
+ } else {
+ ddr_memory_test(start_addr, end_addr, 0);
+ }
+
+ return 0;
+}
+
+U_BOOT_CMD(ddr, 5, 1, do_ddr_test,
+ "DDR3 test",
+ "test <start_addr in hex> <end_addr in hex> - test DDR from start\n"
+ " address to end address\n"
+ "ddr compare <start_addr in hex> <end_addr in hex> <size in hex> -\n"
+ " compare DDR data of (size) bytes from start address to end\n"
+ " address\n"
+ "ddr ecc_err <addr in hex> <bit_err in hex> - generate bit errors\n"
+ " in DDR data at <addr>, the command will read a 32-bit data\n"
+ " from <addr>, and write (data ^ bit_err) back to <addr>\n"
+);
diff --git a/cmd/ti/pd.c b/cmd/ti/pd.c
new file mode 100644
index 00000000000..305023af1e7
--- /dev/null
+++ b/cmd/ti/pd.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Power Domain test commands
+ *
+ * Copyright (C) 2020 Texas Instruments Incorporated, <www.ti.com>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <k3-dev.h>
+
+static const struct udevice_id ti_pd_of_match[] = {
+ { .compatible = "ti,sci-pm-domain" },
+ { /* sentinel */ }
+};
+
+static struct ti_k3_pd_platdata *ti_pd_find_data(void)
+{
+ struct udevice *dev;
+ int i = 0;
+
+ while (1) {
+ uclass_get_device(UCLASS_POWER_DOMAIN, i++, &dev);
+ if (!dev)
+ return NULL;
+
+ if (device_is_compatible(dev,
+ ti_pd_of_match[0].compatible))
+ return dev_get_priv(dev);
+ }
+
+ return NULL;
+}
+
+static void dump_lpsc(struct ti_k3_pd_platdata *data, struct ti_pd *pd)
+{
+ int i;
+ struct ti_lpsc *lpsc;
+ u8 state;
+ static const char * const lpsc_states[] = {
+ "swrstdis", "syncrst", "disable", "enable", "autosleep",
+ "autowake", "unknown",
+ };
+
+ for (i = 0; i < data->num_lpsc; i++) {
+ lpsc = &data->lpsc[i];
+ if (lpsc->pd != pd)
+ continue;
+ state = lpsc_get_state(lpsc);
+ if (state > ARRAY_SIZE(lpsc_states))
+ state = ARRAY_SIZE(lpsc_states) - 1;
+ printf(" LPSC%d: state=%s, usecount=%d\n",
+ lpsc->id, lpsc_states[state], lpsc->usecount);
+ }
+}
+
+static void dump_pd(struct ti_k3_pd_platdata *data, struct ti_psc *psc)
+{
+ int i;
+ struct ti_pd *pd;
+ u8 state;
+ static const char * const pd_states[] = {
+ "off", "on", "unknown"
+ };
+
+ for (i = 0; i < data->num_pd; i++) {
+ pd = &data->pd[i];
+ if (pd->psc != psc)
+ continue;
+ state = ti_pd_state(pd);
+ if (state > ARRAY_SIZE(pd_states))
+ state = ARRAY_SIZE(pd_states) - 1;
+ printf(" PD%d: state=%s, usecount=%d:\n",
+ pd->id, pd_states[state], pd->usecount);
+ dump_lpsc(data, pd);
+ }
+}
+
+static void dump_psc(struct ti_k3_pd_platdata *data)
+{
+ int i;
+ struct ti_psc *psc;
+
+ for (i = 0; i < data->num_psc; i++) {
+ psc = &data->psc[i];
+ printf("PSC%d [%p]:\n", psc->id, psc->base);
+ dump_pd(data, psc);
+ }
+}
+
+static int do_pd_dump(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct ti_k3_pd_platdata *data;
+
+ data = ti_pd_find_data();
+ if (!data)
+ return CMD_RET_FAILURE;
+
+ dump_psc(data);
+
+ return 0;
+}
+
+static int do_pd_endis(int argc, char *const argv[], u8 state)
+{
+ u32 psc_id;
+ u32 lpsc_id;
+ int i;
+ struct ti_k3_pd_platdata *data;
+ struct ti_lpsc *lpsc;
+ int ret;
+
+ if (argc < 3)
+ return CMD_RET_FAILURE;
+
+ data = ti_pd_find_data();
+ if (!data)
+ return CMD_RET_FAILURE;
+
+ psc_id = dectoul(argv[1], NULL);
+ lpsc_id = dectoul(argv[2], NULL);
+
+ for (i = 0; i < data->num_lpsc; i++) {
+ lpsc = &data->lpsc[i];
+ if (lpsc->pd->psc->id != psc_id)
+ continue;
+ if (lpsc->id != lpsc_id)
+ continue;
+ printf("%s pd [PSC:%d,LPSC:%d]...\n",
+ state == MDSTAT_STATE_ENABLE ? "Enabling" : "Disabling",
+ psc_id, lpsc_id);
+ ret = ti_lpsc_transition(lpsc, state);
+ if (ret)
+ return CMD_RET_FAILURE;
+ else
+ return 0;
+ }
+
+ printf("No matching psc/lpsc found.\n");
+
+ return CMD_RET_FAILURE;
+}
+
+static int do_pd_enable(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_pd_endis(argc, argv, MDSTAT_STATE_ENABLE);
+}
+
+static int do_pd_disable(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return do_pd_endis(argc, argv, MDSTAT_STATE_SWRSTDISABLE);
+}
+
+static struct cmd_tbl cmd_pd[] = {
+ U_BOOT_CMD_MKENT(dump, 1, 0, do_pd_dump, "", ""),
+ U_BOOT_CMD_MKENT(enable, 3, 0, do_pd_enable, "", ""),
+ U_BOOT_CMD_MKENT(disable, 3, 0, do_pd_disable, "", ""),
+};
+
+static int ti_do_pd(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ struct cmd_tbl *c;
+
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], cmd_pd, ARRAY_SIZE(cmd_pd));
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+ else
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_LONGHELP(pd,
+ "dump - show power domain status\n"
+ "enable [psc] [lpsc] - enable power domain\n"
+ "disable [psc] [lpsc] - disable power domain\n");
+
+U_BOOT_CMD(pd, 4, 1, ti_do_pd,
+ "TI power domain control", pd_help_text
+);
diff --git a/cmd/time.c b/cmd/time.c
new file mode 100644
index 00000000000..eee6084e968
--- /dev/null
+++ b/cmd/time.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ */
+
+#include <command.h>
+
+static void report_time(ulong cycles)
+{
+ ulong minutes, seconds, milliseconds;
+ ulong total_seconds, remainder;
+
+ total_seconds = cycles / CONFIG_SYS_HZ;
+ remainder = cycles % CONFIG_SYS_HZ;
+ minutes = total_seconds / 60;
+ seconds = total_seconds % 60;
+ /* approximate millisecond value */
+ milliseconds = (remainder * 1000 + CONFIG_SYS_HZ / 2) / CONFIG_SYS_HZ;
+
+ printf("\ntime:");
+ if (minutes)
+ printf(" %lu minutes,", minutes);
+ printf(" %lu.%03lu seconds\n", seconds, milliseconds);
+}
+
+static int do_time(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong cycles = 0;
+ int retval = 0;
+ int repeatable = 0;
+
+ if (argc == 1)
+ return CMD_RET_USAGE;
+
+ retval = cmd_process(0, argc - 1, argv + 1, &repeatable, &cycles);
+ report_time(cycles);
+
+ return retval;
+}
+
+U_BOOT_CMD(time, CONFIG_SYS_MAXARGS, 0, do_time,
+ "run commands and summarize execution time",
+ "command [args...]\n");
diff --git a/cmd/timer.c b/cmd/timer.c
new file mode 100644
index 00000000000..427309e108d
--- /dev/null
+++ b/cmd/timer.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2001
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <time.h>
+#include <linux/string.h>
+
+static int do_timer(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ static ulong start;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ if (!strcmp(argv[1], "start"))
+ start = get_timer(0);
+
+ if (!strcmp(argv[1], "get")) {
+ ulong msecs = get_timer(start) * 1000 / CONFIG_SYS_HZ;
+ printf("%ld.%03d\n", msecs / 1000, (int)(msecs % 1000));
+ }
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ timer, 2, 1, do_timer,
+ "access the system timer",
+ "start - Reset the timer reference.\n"
+ "timer get - Print the time since 'start'."
+);
diff --git a/cmd/tlv_eeprom.c b/cmd/tlv_eeprom.c
new file mode 100644
index 00000000000..3127660dd9e
--- /dev/null
+++ b/cmd/tlv_eeprom.c
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * Copyright (C) 2013 Curt Brune <curt@cumulusnetworks.com>
+ * Copyright (C) 2014 Srideep <srideep_devireddy@dell.com>
+ * Copyright (C) 2013 Miles Tseng <miles_tseng@accton.com>
+ * Copyright (C) 2014,2016 david_yang <david_yang@accton.com>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <i2c.h>
+#include <i2c_eeprom.h>
+#include <env.h>
+#include <init.h>
+#include <net.h>
+#include <asm/global_data.h>
+#include <linux/ctype.h>
+#include <u-boot/crc.h>
+
+#include "tlv_eeprom.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define MAX_TLV_DEVICES 2
+
+/* File scope function prototypes */
+static bool is_checksum_valid(u8 *eeprom);
+static int read_eeprom(int devnum, u8 *eeprom);
+static void show_eeprom(int devnum, u8 *eeprom);
+static void decode_tlv(struct tlvinfo_tlv *tlv);
+static void update_crc(u8 *eeprom);
+static int prog_eeprom(int devnum, u8 *eeprom);
+static bool tlvinfo_find_tlv(u8 *eeprom, u8 tcode, int *eeprom_index);
+static bool tlvinfo_delete_tlv(u8 *eeprom, u8 code);
+static bool tlvinfo_add_tlv(u8 *eeprom, int tcode, char *strval);
+static int set_mac(char *buf, const char *string);
+static int set_date(char *buf, const char *string);
+static int set_bytes(char *buf, const char *string, int *converted_accum);
+static void show_tlv_devices(int current_dev);
+
+/* The EEPROM contents after being read into memory */
+static u8 eeprom[TLV_INFO_MAX_LEN];
+
+static struct udevice *tlv_devices[MAX_TLV_DEVICES];
+
+#define to_header(p) ((struct tlvinfo_header *)p)
+#define to_entry(p) ((struct tlvinfo_tlv *)p)
+
+#define HDR_SIZE sizeof(struct tlvinfo_header)
+#define ENT_SIZE sizeof(struct tlvinfo_tlv)
+
+static inline bool is_digit(char c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+/**
+ * is_valid_tlv
+ *
+ * Perform basic sanity checks on a TLV field. The TLV is pointed to
+ * by the parameter provided.
+ * 1. The type code is not reserved (0x00 or 0xFF)
+ */
+static inline bool is_valid_tlv(struct tlvinfo_tlv *tlv)
+{
+ return((tlv->type != 0x00) && (tlv->type != 0xFF));
+}
+
+/**
+ * is_hex
+ *
+ * Tests if character is an ASCII hex digit
+ */
+static inline u8 is_hex(char p)
+{
+ return (((p >= '0') && (p <= '9')) ||
+ ((p >= 'A') && (p <= 'F')) ||
+ ((p >= 'a') && (p <= 'f')));
+}
+
+/**
+ * is_checksum_valid
+ *
+ * Validate the checksum in the provided TlvInfo EEPROM data. First,
+ * verify that the TlvInfo header is valid, then make sure the last
+ * TLV is a CRC-32 TLV. Then calculate the CRC over the EEPROM data
+ * and compare it to the value stored in the EEPROM CRC-32 TLV.
+ */
+static bool is_checksum_valid(u8 *eeprom)
+{
+ struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+ struct tlvinfo_tlv *eeprom_crc;
+ unsigned int calc_crc;
+ unsigned int stored_crc;
+
+ // Is the eeprom header valid?
+ if (!is_valid_tlvinfo_header(eeprom_hdr))
+ return false;
+
+ // Is the last TLV a CRC?
+ eeprom_crc = to_entry(&eeprom[HDR_SIZE +
+ be16_to_cpu(eeprom_hdr->totallen) - (ENT_SIZE + 4)]);
+ if (eeprom_crc->type != TLV_CODE_CRC_32 || eeprom_crc->length != 4)
+ return false;
+
+ // Calculate the checksum
+ calc_crc = crc32(0, (void *)eeprom,
+ HDR_SIZE + be16_to_cpu(eeprom_hdr->totallen) - 4);
+ stored_crc = (eeprom_crc->value[0] << 24) |
+ (eeprom_crc->value[1] << 16) |
+ (eeprom_crc->value[2] << 8) |
+ eeprom_crc->value[3];
+ return calc_crc == stored_crc;
+}
+
+/**
+ * read_eeprom
+ *
+ * Read the EEPROM into memory, if it hasn't already been read.
+ */
+static int read_eeprom(int devnum, u8 *eeprom)
+{
+ int ret;
+ struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+ struct tlvinfo_tlv *eeprom_tlv = to_entry(&eeprom[HDR_SIZE]);
+
+ /* Read the header */
+ ret = read_tlv_eeprom((void *)eeprom_hdr, 0, HDR_SIZE, devnum);
+ /* If the header was successfully read, read the TLVs */
+ if (ret == 0 && is_valid_tlvinfo_header(eeprom_hdr))
+ ret = read_tlv_eeprom((void *)eeprom_tlv, HDR_SIZE,
+ be16_to_cpu(eeprom_hdr->totallen), devnum);
+ else if (ret == -ENODEV)
+ return ret;
+
+ // If the contents are invalid, start over with default contents
+ if (!is_valid_tlvinfo_header(eeprom_hdr) ||
+ !is_checksum_valid(eeprom)) {
+ strcpy(eeprom_hdr->signature, TLV_INFO_ID_STRING);
+ eeprom_hdr->version = TLV_INFO_VERSION;
+ eeprom_hdr->totallen = cpu_to_be16(0);
+ update_crc(eeprom);
+ }
+
+#ifdef DEBUG
+ show_eeprom(devnum, eeprom);
+#endif
+
+ return ret;
+}
+
+/**
+ * show_eeprom
+ *
+ * Display the contents of the EEPROM
+ */
+static void show_eeprom(int devnum, u8 *eeprom)
+{
+ int tlv_end;
+ int curr_tlv;
+#ifdef DEBUG
+ int i;
+#endif
+ struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+ struct tlvinfo_tlv *eeprom_tlv;
+
+ if (!is_valid_tlvinfo_header(eeprom_hdr)) {
+ printf("EEPROM does not contain data in a valid TlvInfo format.\n");
+ return;
+ }
+
+ printf("TLV: %u\n", devnum);
+ printf("TlvInfo Header:\n");
+ printf(" Id String: %s\n", eeprom_hdr->signature);
+ printf(" Version: %d\n", eeprom_hdr->version);
+ printf(" Total Length: %d\n", be16_to_cpu(eeprom_hdr->totallen));
+
+ printf("TLV Name Code Len Value\n");
+ printf("-------------------- ---- --- -----\n");
+ curr_tlv = HDR_SIZE;
+ tlv_end = HDR_SIZE + be16_to_cpu(eeprom_hdr->totallen);
+ while (curr_tlv < tlv_end) {
+ eeprom_tlv = to_entry(&eeprom[curr_tlv]);
+ if (!is_valid_tlv(eeprom_tlv)) {
+ printf("Invalid TLV field starting at EEPROM offset %d\n",
+ curr_tlv);
+ return;
+ }
+ decode_tlv(eeprom_tlv);
+ curr_tlv += ENT_SIZE + eeprom_tlv->length;
+ }
+
+ printf("Checksum is %s.\n",
+ is_checksum_valid(eeprom) ? "valid" : "invalid");
+
+#ifdef DEBUG
+ printf("EEPROM dump: (0x%x bytes)", TLV_INFO_MAX_LEN);
+ for (i = 0; i < TLV_INFO_MAX_LEN; i++) {
+ if ((i % 16) == 0)
+ printf("\n%02X: ", i);
+ printf("%02X ", eeprom[i]);
+ }
+ printf("\n");
+#endif
+}
+
+/**
+ * Struct for displaying the TLV codes and names.
+ */
+struct tlv_code_desc {
+ u8 m_code;
+ char *m_name;
+};
+
+/**
+ * List of TLV codes and names.
+ */
+static struct tlv_code_desc tlv_code_list[] = {
+ { TLV_CODE_PRODUCT_NAME, "Product Name"},
+ { TLV_CODE_PART_NUMBER, "Part Number"},
+ { TLV_CODE_SERIAL_NUMBER, "Serial Number"},
+ { TLV_CODE_MAC_BASE, "Base MAC Address"},
+ { TLV_CODE_MANUF_DATE, "Manufacture Date"},
+ { TLV_CODE_DEVICE_VERSION, "Device Version"},
+ { TLV_CODE_LABEL_REVISION, "Label Revision"},
+ { TLV_CODE_PLATFORM_NAME, "Platform Name"},
+ { TLV_CODE_ONIE_VERSION, "ONIE Version"},
+ { TLV_CODE_MAC_SIZE, "MAC Addresses"},
+ { TLV_CODE_MANUF_NAME, "Manufacturer"},
+ { TLV_CODE_MANUF_COUNTRY, "Country Code"},
+ { TLV_CODE_VENDOR_NAME, "Vendor Name"},
+ { TLV_CODE_DIAG_VERSION, "Diag Version"},
+ { TLV_CODE_SERVICE_TAG, "Service Tag"},
+ { TLV_CODE_VENDOR_EXT, "Vendor Extension"},
+ { TLV_CODE_CRC_32, "CRC-32"},
+};
+
+/**
+ * Look up a TLV name by its type.
+ */
+static inline const char *tlv_type2name(u8 type)
+{
+ char *name = "Unknown";
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tlv_code_list); i++) {
+ if (tlv_code_list[i].m_code == type) {
+ name = tlv_code_list[i].m_name;
+ break;
+ }
+ }
+
+ return name;
+}
+
+/*
+ * decode_tlv
+ *
+ * Print a string representing the contents of the TLV field. The format of
+ * the string is:
+ * 1. The name of the field left justified in 20 characters
+ * 2. The type code in hex right justified in 5 characters
+ * 3. The length in decimal right justified in 4 characters
+ * 4. The value, left justified in however many characters it takes
+ * The validity of EEPROM contents and the TLV field have been verified
+ * prior to calling this function.
+ */
+#define DECODE_NAME_MAX 20
+
+/*
+ * The max decode value is currently for the 'raw' type or the 'vendor
+ * extension' type, both of which have the same decode format. The
+ * max decode string size is computed as follows:
+ *
+ * strlen(" 0xFF") * TLV_VALUE_MAX_LEN + 1
+ *
+ */
+#define DECODE_VALUE_MAX ((5 * TLV_VALUE_MAX_LEN) + 1)
+
+static void decode_tlv(struct tlvinfo_tlv *tlv)
+{
+ char name[DECODE_NAME_MAX];
+ char value[DECODE_VALUE_MAX];
+ int i;
+
+ strncpy(name, tlv_type2name(tlv->type), DECODE_NAME_MAX);
+
+ switch (tlv->type) {
+ case TLV_CODE_PRODUCT_NAME:
+ case TLV_CODE_PART_NUMBER:
+ case TLV_CODE_SERIAL_NUMBER:
+ case TLV_CODE_MANUF_DATE:
+ case TLV_CODE_LABEL_REVISION:
+ case TLV_CODE_PLATFORM_NAME:
+ case TLV_CODE_ONIE_VERSION:
+ case TLV_CODE_MANUF_NAME:
+ case TLV_CODE_MANUF_COUNTRY:
+ case TLV_CODE_VENDOR_NAME:
+ case TLV_CODE_DIAG_VERSION:
+ case TLV_CODE_SERVICE_TAG:
+ memcpy(value, tlv->value, tlv->length);
+ value[tlv->length] = 0;
+ break;
+ case TLV_CODE_MAC_BASE:
+ sprintf(value, "%02X:%02X:%02X:%02X:%02X:%02X",
+ tlv->value[0], tlv->value[1], tlv->value[2],
+ tlv->value[3], tlv->value[4], tlv->value[5]);
+ break;
+ case TLV_CODE_DEVICE_VERSION:
+ sprintf(value, "%u", tlv->value[0]);
+ break;
+ case TLV_CODE_MAC_SIZE:
+ sprintf(value, "%u", (tlv->value[0] << 8) | tlv->value[1]);
+ break;
+ case TLV_CODE_VENDOR_EXT:
+ value[0] = 0;
+ for (i = 0; (i < (DECODE_VALUE_MAX / 5)) && (i < tlv->length);
+ i++) {
+ sprintf(value, "%s 0x%02X", value, tlv->value[i]);
+ }
+ break;
+ case TLV_CODE_CRC_32:
+ sprintf(value, "0x%02X%02X%02X%02X",
+ tlv->value[0], tlv->value[1],
+ tlv->value[2], tlv->value[3]);
+ break;
+ default:
+ value[0] = 0;
+ for (i = 0; (i < (DECODE_VALUE_MAX / 5)) && (i < tlv->length);
+ i++) {
+ sprintf(value, "%s 0x%02X", value, tlv->value[i]);
+ }
+ break;
+ }
+
+ name[DECODE_NAME_MAX - 1] = 0;
+ printf("%-20s 0x%02X %3d %s\n", name, tlv->type, tlv->length, value);
+}
+
+/**
+ * update_crc
+ *
+ * This function updates the CRC-32 TLV. If there is no CRC-32 TLV, then
+ * one is added. This function should be called after each update to the
+ * EEPROM structure, to make sure the CRC is always correct.
+ */
+static void update_crc(u8 *eeprom)
+{
+ struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+ struct tlvinfo_tlv *eeprom_crc;
+ unsigned int calc_crc;
+ int eeprom_index;
+
+ // Discover the CRC TLV
+ if (!tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index)) {
+ unsigned int totallen = be16_to_cpu(eeprom_hdr->totallen);
+
+ if ((totallen + ENT_SIZE + 4) > TLV_TOTAL_LEN_MAX)
+ return;
+ eeprom_index = HDR_SIZE + totallen;
+ eeprom_hdr->totallen = cpu_to_be16(totallen + ENT_SIZE + 4);
+ }
+ eeprom_crc = to_entry(&eeprom[eeprom_index]);
+ eeprom_crc->type = TLV_CODE_CRC_32;
+ eeprom_crc->length = 4;
+
+ // Calculate the checksum
+ calc_crc = crc32(0, (void *)eeprom,
+ HDR_SIZE + be16_to_cpu(eeprom_hdr->totallen) - 4);
+ eeprom_crc->value[0] = (calc_crc >> 24) & 0xFF;
+ eeprom_crc->value[1] = (calc_crc >> 16) & 0xFF;
+ eeprom_crc->value[2] = (calc_crc >> 8) & 0xFF;
+ eeprom_crc->value[3] = (calc_crc >> 0) & 0xFF;
+}
+
+/**
+ * prog_eeprom
+ *
+ * Write the EEPROM data from CPU memory to the hardware.
+ */
+static int prog_eeprom(int devnum, u8 *eeprom)
+{
+ int ret = 0;
+ struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+ int eeprom_len;
+
+ update_crc(eeprom);
+
+ eeprom_len = HDR_SIZE + be16_to_cpu(eeprom_hdr->totallen);
+ ret = write_tlv_eeprom(eeprom, eeprom_len, devnum);
+ if (ret) {
+ printf("Programming failed.\n");
+ return -1;
+ }
+
+ printf("Programming passed.\n");
+ return 0;
+}
+
+/**
+ * show_tlv_code_list - Display the list of TLV codes and names
+ */
+void show_tlv_code_list(void)
+{
+ int i;
+
+ printf("TLV Code TLV Name\n");
+ printf("======== =================\n");
+ for (i = 0; i < ARRAY_SIZE(tlv_code_list); i++) {
+ printf("0x%02X %s\n",
+ tlv_code_list[i].m_code,
+ tlv_code_list[i].m_name);
+ }
+}
+
+/**
+ * do_tlv_eeprom
+ *
+ * This function implements the tlv_eeprom command.
+ */
+int do_tlv_eeprom(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char cmd;
+ struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+ static unsigned int current_dev;
+ /* Set to 1 if we've read EEPROM into memory */
+ static int has_been_read;
+ int ret;
+
+ // If no arguments, read the EEPROM and display its contents
+ if (argc == 1) {
+ if (!has_been_read) {
+ ret = read_eeprom(current_dev, eeprom);
+ if (ret) {
+ printf("Failed to read EEPROM data from device.\n");
+ return 0;
+ }
+
+ has_been_read = 1;
+ }
+ show_eeprom(current_dev, eeprom);
+ return 0;
+ }
+
+ // We only look at the first character to the command, so "read" and
+ // "reset" will both be treated as "read".
+ cmd = argv[1][0];
+
+ // select device
+ if (cmd == 'd') {
+ /* 'dev' command */
+ unsigned int devnum;
+
+ devnum = simple_strtoul(argv[2], NULL, 0);
+ if (devnum >= MAX_TLV_DEVICES) {
+ printf("Invalid device number\n");
+ return 0;
+ }
+ current_dev = devnum;
+ has_been_read = 0;
+
+ return 0;
+ }
+
+ // Read the EEPROM contents
+ if (cmd == 'r') {
+ has_been_read = 0;
+ ret = read_eeprom(current_dev, eeprom);
+ if (ret) {
+ printf("Failed to read EEPROM data from device.\n");
+ return 0;
+ }
+
+ printf("EEPROM data loaded from device to memory.\n");
+ has_been_read = 1;
+ return 0;
+ }
+
+ // Subsequent commands require that the EEPROM has already been read.
+ if (!has_been_read) {
+ printf("Please read the EEPROM data first, using the 'tlv_eeprom read' command.\n");
+ return 0;
+ }
+
+ // Handle the commands that don't take parameters
+ if (argc == 2) {
+ switch (cmd) {
+ case 'w': /* write */
+ prog_eeprom(current_dev, eeprom);
+ break;
+ case 'e': /* erase */
+ strcpy(eeprom_hdr->signature, TLV_INFO_ID_STRING);
+ eeprom_hdr->version = TLV_INFO_VERSION;
+ eeprom_hdr->totallen = cpu_to_be16(0);
+ update_crc(eeprom);
+ printf("EEPROM data in memory reset.\n");
+ break;
+ case 'l': /* list */
+ show_tlv_code_list();
+ break;
+ case 'd': /* dev */
+ show_tlv_devices(current_dev);
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ return 0;
+ }
+
+ // The set command takes one or two args.
+ if (argc > 4)
+ return CMD_RET_USAGE;
+
+ // Set command. If the TLV exists in the EEPROM, delete it. Then if
+ // data was supplied for this TLV add the TLV with the new contents at
+ // the end.
+ if (cmd == 's') {
+ int tcode;
+
+ tcode = simple_strtoul(argv[2], NULL, 0);
+ tlvinfo_delete_tlv(eeprom, tcode);
+ if (argc == 4)
+ tlvinfo_add_tlv(eeprom, tcode, argv[3]);
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ return 0;
+}
+
+/**
+ * This macro defines the tlv_eeprom command line command.
+ */
+U_BOOT_CMD(tlv_eeprom, 4, 1, do_tlv_eeprom,
+ "Display and program the system EEPROM data block.",
+ "[read|write|set <type_code> <string_value>|erase|list]\n"
+ "tlv_eeprom\n"
+ " - With no arguments display the current contents.\n"
+ "tlv_eeprom dev [dev]\n"
+ " - List devices or set current EEPROM device.\n"
+ "tlv_eeprom read\n"
+ " - Load EEPROM data from device to memory.\n"
+ "tlv_eeprom write\n"
+ " - Write the EEPROM data to persistent storage.\n"
+ "tlv_eeprom set <type_code> <string_value>\n"
+ " - Set a field to a value.\n"
+ " - If no string_value, field is deleted.\n"
+ " - Use 'tlv_eeprom write' to make changes permanent.\n"
+ "tlv_eeprom erase\n"
+ " - Reset the in memory EEPROM data.\n"
+ " - Use 'tlv_eeprom read' to refresh the in memory EEPROM data.\n"
+ " - Use 'tlv_eeprom write' to make changes permanent.\n"
+ "tlv_eeprom list\n"
+ " - List the understood TLV codes and names.\n"
+ );
+
+/**
+ * tlvinfo_find_tlv
+ *
+ * This function finds the TLV with the supplied code in the EEPROM.
+ * An offset from the beginning of the EEPROM is returned in the
+ * eeprom_index parameter if the TLV is found.
+ */
+static bool tlvinfo_find_tlv(u8 *eeprom, u8 tcode, int *eeprom_index)
+{
+ struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+ struct tlvinfo_tlv *eeprom_tlv;
+ int eeprom_end;
+
+ // Search through the TLVs, looking for the first one which matches the
+ // supplied type code.
+ *eeprom_index = HDR_SIZE;
+ eeprom_end = HDR_SIZE + be16_to_cpu(eeprom_hdr->totallen);
+ while (*eeprom_index < eeprom_end) {
+ eeprom_tlv = to_entry(&eeprom[*eeprom_index]);
+ if (!is_valid_tlv(eeprom_tlv))
+ return false;
+ if (eeprom_tlv->type == tcode)
+ return true;
+ *eeprom_index += ENT_SIZE + eeprom_tlv->length;
+ }
+ return(false);
+}
+
+/**
+ * tlvinfo_delete_tlv
+ *
+ * This function deletes the TLV with the specified type code from the
+ * EEPROM.
+ */
+static bool tlvinfo_delete_tlv(u8 *eeprom, u8 code)
+{
+ int eeprom_index;
+ int tlength;
+ struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+ struct tlvinfo_tlv *eeprom_tlv;
+
+ // Find the TLV and then move all following TLVs "forward"
+ if (tlvinfo_find_tlv(eeprom, code, &eeprom_index)) {
+ eeprom_tlv = to_entry(&eeprom[eeprom_index]);
+ tlength = ENT_SIZE + eeprom_tlv->length;
+ memcpy(&eeprom[eeprom_index], &eeprom[eeprom_index + tlength],
+ HDR_SIZE +
+ be16_to_cpu(eeprom_hdr->totallen) - eeprom_index -
+ tlength);
+ eeprom_hdr->totallen =
+ cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) -
+ tlength);
+ update_crc(eeprom);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * tlvinfo_add_tlv
+ *
+ * This function adds a TLV to the EEPROM, converting the value (a string) to
+ * the format in which it will be stored in the EEPROM.
+ */
+#define MAX_TLV_VALUE_LEN 256
+static bool tlvinfo_add_tlv(u8 *eeprom, int tcode, char *strval)
+{
+ struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+ struct tlvinfo_tlv *eeprom_tlv;
+ int new_tlv_len = 0;
+ u32 value;
+ char data[MAX_TLV_VALUE_LEN];
+ int eeprom_index;
+
+ // Encode each TLV type into the format to be stored in the EEPROM
+ switch (tcode) {
+ case TLV_CODE_PRODUCT_NAME:
+ case TLV_CODE_PART_NUMBER:
+ case TLV_CODE_SERIAL_NUMBER:
+ case TLV_CODE_LABEL_REVISION:
+ case TLV_CODE_PLATFORM_NAME:
+ case TLV_CODE_ONIE_VERSION:
+ case TLV_CODE_MANUF_NAME:
+ case TLV_CODE_MANUF_COUNTRY:
+ case TLV_CODE_VENDOR_NAME:
+ case TLV_CODE_DIAG_VERSION:
+ case TLV_CODE_SERVICE_TAG:
+ strncpy(data, strval, MAX_TLV_VALUE_LEN);
+ new_tlv_len = min_t(size_t, MAX_TLV_VALUE_LEN, strlen(strval));
+ break;
+ case TLV_CODE_DEVICE_VERSION:
+ value = simple_strtoul(strval, NULL, 0);
+ if (value >= 256) {
+ printf("ERROR: Device version must be 255 or less. Value supplied: %u",
+ value);
+ return false;
+ }
+ data[0] = value & 0xFF;
+ new_tlv_len = 1;
+ break;
+ case TLV_CODE_MAC_SIZE:
+ value = simple_strtoul(strval, NULL, 0);
+ if (value >= 65536) {
+ printf("ERROR: MAC Size must be 65535 or less. Value supplied: %u",
+ value);
+ return false;
+ }
+ data[0] = (value >> 8) & 0xFF;
+ data[1] = value & 0xFF;
+ new_tlv_len = 2;
+ break;
+ case TLV_CODE_MANUF_DATE:
+ if (set_date(data, strval) != 0)
+ return false;
+ new_tlv_len = 19;
+ break;
+ case TLV_CODE_MAC_BASE:
+ if (set_mac(data, strval) != 0)
+ return false;
+ new_tlv_len = 6;
+ break;
+ case TLV_CODE_CRC_32:
+ printf("WARNING: The CRC TLV is set automatically and cannot be set manually.\n");
+ return false;
+ case TLV_CODE_VENDOR_EXT:
+ default:
+ if (set_bytes(data, strval, &new_tlv_len) != 0)
+ return false;
+ break;
+ }
+
+ // Is there room for this TLV?
+ if ((be16_to_cpu(eeprom_hdr->totallen) + ENT_SIZE + new_tlv_len) >
+ TLV_TOTAL_LEN_MAX) {
+ printf("ERROR: There is not enough room in the EEPROM to save data.\n");
+ return false;
+ }
+
+ // Add TLV at the end, overwriting CRC TLV if it exists
+ if (tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index))
+ eeprom_hdr->totallen =
+ cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) -
+ ENT_SIZE - 4);
+ else
+ eeprom_index = HDR_SIZE + be16_to_cpu(eeprom_hdr->totallen);
+ eeprom_tlv = to_entry(&eeprom[eeprom_index]);
+ eeprom_tlv->type = tcode;
+ eeprom_tlv->length = new_tlv_len;
+ memcpy(eeprom_tlv->value, data, new_tlv_len);
+
+ // Update the total length and calculate (add) a new CRC-32 TLV
+ eeprom_hdr->totallen = cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) +
+ ENT_SIZE + new_tlv_len);
+ update_crc(eeprom);
+
+ return true;
+}
+
+/**
+ * set_mac
+ *
+ * Converts a string MAC address into a binary buffer.
+ *
+ * This function takes a pointer to a MAC address string
+ * (i.e."XX:XX:XX:XX:XX:XX", where "XX" is a two-digit hex number).
+ * The string format is verified and then converted to binary and
+ * stored in a buffer.
+ */
+static int set_mac(char *buf, const char *string)
+{
+ char *p = (char *)string;
+ int i;
+ int err = 0;
+ char *end;
+
+ if (!p) {
+ printf("ERROR: NULL mac addr string passed in.\n");
+ return -1;
+ }
+
+ if (strlen(p) != 17) {
+ printf("ERROR: MAC address strlen() != 17 -- %zu\n", strlen(p));
+ printf("ERROR: Bad MAC address format: %s\n", string);
+ return -1;
+ }
+
+ for (i = 0; i < 17; i++) {
+ if ((i % 3) == 2) {
+ if (p[i] != ':') {
+ err++;
+ printf("ERROR: mac: p[%i] != :, found: `%c'\n",
+ i, p[i]);
+ break;
+ }
+ continue;
+ } else if (!is_hex(p[i])) {
+ err++;
+ printf("ERROR: mac: p[%i] != hex digit, found: `%c'\n",
+ i, p[i]);
+ break;
+ }
+ }
+
+ if (err != 0) {
+ printf("ERROR: Bad MAC address format: %s\n", string);
+ return -1;
+ }
+
+ /* Convert string to binary */
+ for (i = 0, p = (char *)string; i < 6; i++) {
+ buf[i] = p ? hextoul(p, &end) : 0;
+ if (p)
+ p = (*end) ? end + 1 : end;
+ }
+
+ if (!is_valid_ethaddr((u8 *)buf)) {
+ printf("ERROR: MAC address must not be 00:00:00:00:00:00, a multicast address or FF:FF:FF:FF:FF:FF.\n");
+ printf("ERROR: Bad MAC address format: %s\n", string);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * set_date
+ *
+ * Validates the format of the data string
+ *
+ * This function takes a pointer to a date string (i.e. MM/DD/YYYY hh:mm:ss)
+ * and validates that the format is correct. If so the string is copied
+ * to the supplied buffer.
+ */
+static int set_date(char *buf, const char *string)
+{
+ int i;
+
+ if (!string) {
+ printf("ERROR: NULL date string passed in.\n");
+ return -1;
+ }
+
+ if (strlen(string) != 19) {
+ printf("ERROR: Date strlen() != 19 -- %zu\n", strlen(string));
+ printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n",
+ string);
+ return -1;
+ }
+
+ for (i = 0; string[i] != 0; i++) {
+ switch (i) {
+ case 2:
+ case 5:
+ if (string[i] != '/') {
+ printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n",
+ string);
+ return -1;
+ }
+ break;
+ case 10:
+ if (string[i] != ' ') {
+ printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n",
+ string);
+ return -1;
+ }
+ break;
+ case 13:
+ case 16:
+ if (string[i] != ':') {
+ printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n",
+ string);
+ return -1;
+ }
+ break;
+ default:
+ if (!is_digit(string[i])) {
+ printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n",
+ string);
+ return -1;
+ }
+ break;
+ }
+ }
+
+ strcpy(buf, string);
+ return 0;
+}
+
+/**
+ * set_bytes
+ *
+ * Converts a space-separated string of decimal numbers into a
+ * buffer of bytes.
+ *
+ * This function takes a pointer to a space-separated string of decimal
+ * numbers (i.e. "128 0x55 0321") with "C" standard radix specifiers
+ * and converts them to an array of bytes.
+ */
+static int set_bytes(char *buf, const char *string, int *converted_accum)
+{
+ char *p = (char *)string;
+ int i;
+ uint byte;
+
+ if (!p) {
+ printf("ERROR: NULL string passed in.\n");
+ return -1;
+ }
+
+ /* Convert string to bytes */
+ for (i = 0, p = (char *)string; (i < TLV_VALUE_MAX_LEN) && (*p != 0);
+ i++) {
+ while ((*p == ' ') || (*p == '\t') || (*p == ',') ||
+ (*p == ';')) {
+ p++;
+ }
+ if (*p != 0) {
+ if (!is_digit(*p)) {
+ printf("ERROR: Non-digit found in byte string: (%s)\n",
+ string);
+ return -1;
+ }
+ byte = simple_strtoul(p, &p, 0);
+ if (byte >= 256) {
+ printf("ERROR: The value specified is greater than 255: (%u) in string: %s\n",
+ byte, string);
+ return -1;
+ }
+ buf[i] = byte & 0xFF;
+ }
+ }
+
+ if (i == TLV_VALUE_MAX_LEN && (*p != 0)) {
+ printf("ERROR: Trying to assign too many bytes (max: %d) in string: %s\n",
+ TLV_VALUE_MAX_LEN, string);
+ return -1;
+ }
+
+ *converted_accum = i;
+ return 0;
+}
+
+static void show_tlv_devices(int current_dev)
+{
+ unsigned int dev;
+
+ for (dev = 0; dev < MAX_TLV_DEVICES; dev++)
+ if (tlv_devices[dev])
+ printf("TLV: %u%s\n", dev,
+ (dev == current_dev) ? " (*)" : "");
+}
+
+static int find_tlv_devices(struct udevice **tlv_devices_p)
+{
+ int ret;
+ int count_dev = 0;
+ struct udevice *dev;
+
+ for (ret = uclass_first_device_check(UCLASS_I2C_EEPROM, &dev);
+ dev;
+ ret = uclass_next_device_check(&dev)) {
+ if (ret == 0)
+ tlv_devices_p[count_dev++] = dev;
+ if (count_dev >= MAX_TLV_DEVICES)
+ break;
+ }
+
+ return (count_dev == 0) ? -ENODEV : 0;
+}
+
+static struct udevice *find_tlv_device_by_index(int dev_num)
+{
+ struct udevice *local_tlv_devices[MAX_TLV_DEVICES] = {};
+ struct udevice **tlv_devices_p;
+ int ret;
+
+ if (gd->flags & (GD_FLG_RELOC | GD_FLG_SPL_INIT)) {
+ /* Assume BSS is initialized; use static data */
+ if (tlv_devices[dev_num])
+ return tlv_devices[dev_num];
+ tlv_devices_p = tlv_devices;
+ } else {
+ tlv_devices_p = local_tlv_devices;
+ }
+
+ ret = find_tlv_devices(tlv_devices_p);
+ if (ret == 0 && tlv_devices_p[dev_num])
+ return tlv_devices_p[dev_num];
+
+ return NULL;
+}
+
+/**
+ * read_tlv_eeprom - read the hwinfo from i2c EEPROM
+ */
+int read_tlv_eeprom(void *eeprom, int offset, int len, int dev_num)
+{
+ struct udevice *dev;
+
+ if (dev_num >= MAX_TLV_DEVICES)
+ return -EINVAL;
+
+ dev = find_tlv_device_by_index(dev_num);
+ if (!dev)
+ return -ENODEV;
+
+ return i2c_eeprom_read(dev, offset, eeprom, len);
+}
+
+/**
+ * write_tlv_eeprom - write the hwinfo to i2c EEPROM
+ */
+int write_tlv_eeprom(void *eeprom, int len, int dev)
+{
+ if (!(gd->flags & GD_FLG_RELOC))
+ return -ENODEV;
+ if (!tlv_devices[dev])
+ return -ENODEV;
+
+ return i2c_eeprom_write(tlv_devices[dev], 0, eeprom, len);
+}
+
+int read_tlvinfo_tlv_eeprom(void *eeprom, struct tlvinfo_header **hdr,
+ struct tlvinfo_tlv **first_entry, int dev_num)
+{
+ int ret;
+ struct tlvinfo_header *tlv_hdr;
+ struct tlvinfo_tlv *tlv_ent;
+
+ /* Read TLV header */
+ ret = read_tlv_eeprom(eeprom, 0, HDR_SIZE, dev_num);
+ if (ret < 0)
+ return ret;
+
+ tlv_hdr = eeprom;
+ if (!is_valid_tlvinfo_header(tlv_hdr))
+ return -EINVAL;
+
+ /* Read TLV entries */
+ tlv_ent = to_entry(&tlv_hdr[1]);
+ ret = read_tlv_eeprom(tlv_ent, HDR_SIZE,
+ be16_to_cpu(tlv_hdr->totallen), dev_num);
+ if (ret < 0)
+ return ret;
+ if (!is_checksum_valid(eeprom))
+ return -EINVAL;
+
+ *hdr = tlv_hdr;
+ *first_entry = tlv_ent;
+
+ return 0;
+}
+
+/**
+ * mac_read_from_eeprom
+ *
+ * Read the MAC addresses from EEPROM
+ *
+ * This function reads the MAC addresses from EEPROM and sets the
+ * appropriate environment variables for each one read.
+ *
+ * The environment variables are only set if they haven't been set already.
+ * This ensures that any user-saved variables are never overwritten.
+ *
+ * This function must be called after relocation.
+ */
+int mac_read_from_eeprom(void)
+{
+ unsigned int i;
+ int eeprom_index;
+ struct tlvinfo_tlv *eeprom_tlv;
+ int maccount;
+ u8 macbase[6];
+ struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+ int devnum = 0; // TODO: support multiple EEPROMs
+
+ if (read_eeprom(devnum, eeprom)) {
+ log_err("EEPROM: read failed\n");
+ return -1;
+ }
+
+ maccount = 1;
+ if (tlvinfo_find_tlv(eeprom, TLV_CODE_MAC_SIZE, &eeprom_index)) {
+ eeprom_tlv = to_entry(&eeprom[eeprom_index]);
+ maccount = (eeprom_tlv->value[0] << 8) | eeprom_tlv->value[1];
+ }
+
+ memcpy(macbase, "\0\0\0\0\0\0", 6);
+ if (tlvinfo_find_tlv(eeprom, TLV_CODE_MAC_BASE, &eeprom_index)) {
+ eeprom_tlv = to_entry(&eeprom[eeprom_index]);
+ memcpy(macbase, eeprom_tlv->value, 6);
+ }
+
+ for (i = 0; i < maccount; i++) {
+ if (is_valid_ethaddr(macbase)) {
+ char ethaddr[18];
+ char enetvar[11];
+
+ sprintf(ethaddr, "%02X:%02X:%02X:%02X:%02X:%02X",
+ macbase[0], macbase[1], macbase[2],
+ macbase[3], macbase[4], macbase[5]);
+ sprintf(enetvar, i ? "eth%daddr" : "ethaddr", i);
+ /* Only initialize environment variables that are blank
+ * (i.e. have not yet been set)
+ */
+ if (!env_get(enetvar))
+ env_set(enetvar, ethaddr);
+
+ macbase[5]++;
+ if (macbase[5] == 0) {
+ macbase[4]++;
+ if (macbase[4] == 0) {
+ macbase[3]++;
+ if (macbase[3] == 0) {
+ macbase[0] = 0;
+ macbase[1] = 0;
+ macbase[2] = 0;
+ }
+ }
+ }
+ }
+ }
+
+ log_debug("EEPROM: %s v%u len=%u\n", eeprom_hdr->signature, eeprom_hdr->version,
+ be16_to_cpu(eeprom_hdr->totallen));
+
+ return 0;
+}
+
+int serial_read_from_eeprom(int devnum)
+{
+ char serialstr[257];
+ int eeprom_index;
+ struct tlvinfo_tlv *eeprom_tlv;
+
+ if (env_get("serial#"))
+ return 0;
+
+ if (read_eeprom(devnum, eeprom)) {
+ printf("Read failed.\n");
+ return -1;
+ }
+
+ if (tlvinfo_find_tlv(eeprom, TLV_CODE_SERIAL_NUMBER, &eeprom_index)) {
+ eeprom_tlv = to_entry(&eeprom[eeprom_index]);
+ memcpy(serialstr, eeprom_tlv->value, eeprom_tlv->length);
+ serialstr[eeprom_tlv->length] = 0;
+ env_set("serial#", serialstr);
+ }
+
+ return 0;
+}
diff --git a/cmd/tpm-common.c b/cmd/tpm-common.c
new file mode 100644
index 00000000000..1cd57f901b6
--- /dev/null
+++ b/cmd/tpm-common.c
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013 The Chromium OS Authors.
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <env.h>
+#include <malloc.h>
+#include <asm/unaligned.h>
+#include <linux/string.h>
+#include <tpm-common.h>
+#include <tpm_api.h>
+#include "tpm-user-utils.h"
+
+static struct udevice *tpm_dev;
+
+/**
+ * Print a byte string in hexdecimal format, 16-bytes per line.
+ *
+ * @param data byte string to be printed
+ * @param count number of bytes to be printed
+ */
+void print_byte_string(u8 *data, size_t count)
+{
+ int i, print_newline = 0;
+
+ for (i = 0; i < count; i++) {
+ printf(" %02x", data[i]);
+ print_newline = (i % 16 == 15);
+ if (print_newline)
+ putc('\n');
+ }
+ /* Avoid duplicated newline at the end */
+ if (!print_newline)
+ putc('\n');
+}
+
+/**
+ * Convert a text string of hexdecimal values into a byte string.
+ *
+ * @param bytes text string of hexdecimal values with no space
+ * between them
+ * @param data output buffer for byte string. The caller has to make
+ * sure it is large enough for storing the output. If
+ * NULL is passed, a large enough buffer will be allocated,
+ * and the caller must free it.
+ * @param count_ptr output variable for the length of byte string
+ * Return: pointer to output buffer
+ */
+void *parse_byte_string(char *bytes, u8 *data, size_t *count_ptr)
+{
+ char byte[3];
+ size_t count, length;
+ int i;
+
+ if (!bytes)
+ return NULL;
+ length = strlen(bytes);
+ count = length / 2;
+
+ if (!data)
+ data = malloc(count);
+ if (!data)
+ return NULL;
+
+ byte[2] = '\0';
+ for (i = 0; i < length; i += 2) {
+ byte[0] = bytes[i];
+ byte[1] = bytes[i + 1];
+ data[i / 2] = (u8)hextoul(byte, NULL);
+ }
+
+ if (count_ptr)
+ *count_ptr = count;
+
+ return data;
+}
+
+/**
+ * report_return_code() - Report any error and return failure or success
+ *
+ * @param return_code TPM command return code
+ * Return: value of enum command_ret_t
+ */
+int report_return_code(int return_code)
+{
+ if (return_code) {
+ printf("Error: %d\n", return_code);
+ return CMD_RET_FAILURE;
+ } else {
+ return CMD_RET_SUCCESS;
+ }
+}
+
+/**
+ * Return number of values defined by a type string.
+ *
+ * @param type_str type string
+ * Return: number of values of type string
+ */
+int type_string_get_num_values(const char *type_str)
+{
+ return strlen(type_str);
+}
+
+/**
+ * Return total size of values defined by a type string.
+ *
+ * @param type_str type string
+ * Return: total size of values of type string, or 0 if type string
+ * contains illegal type character.
+ */
+size_t type_string_get_space_size(const char *type_str)
+{
+ size_t size;
+
+ for (size = 0; *type_str; type_str++) {
+ switch (*type_str) {
+ case 'b':
+ size += 1;
+ break;
+ case 'w':
+ size += 2;
+ break;
+ case 'd':
+ size += 4;
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ return size;
+}
+
+/**
+ * Allocate a buffer large enough to hold values defined by a type
+ * string. The caller has to free the buffer.
+ *
+ * @param type_str type string
+ * @param count pointer for storing size of buffer
+ * Return: pointer to buffer or NULL on error
+ */
+void *type_string_alloc(const char *type_str, u32 *count)
+{
+ void *data;
+ size_t size;
+
+ size = type_string_get_space_size(type_str);
+ if (!size)
+ return NULL;
+ data = malloc(size);
+ if (data)
+ *count = size;
+
+ return data;
+}
+
+/**
+ * Pack values defined by a type string into a buffer. The buffer must have
+ * large enough space.
+ *
+ * @param type_str type string
+ * @param values text strings of values to be packed
+ * @param data output buffer of values
+ * Return: 0 on success, non-0 on error
+ */
+int type_string_pack(const char *type_str, char * const values[],
+ u8 *data)
+{
+ size_t offset;
+ u32 value;
+
+ for (offset = 0; *type_str; type_str++, values++) {
+ value = simple_strtoul(values[0], NULL, 0);
+ switch (*type_str) {
+ case 'b':
+ data[offset] = value;
+ offset += 1;
+ break;
+ case 'w':
+ put_unaligned_be16(value, data + offset);
+ offset += 2;
+ break;
+ case 'd':
+ put_unaligned_be32(value, data + offset);
+ offset += 4;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Read values defined by a type string from a buffer, and write these values
+ * to environment variables.
+ *
+ * @param type_str type string
+ * @param data input buffer of values
+ * @param vars names of environment variables
+ * Return: 0 on success, non-0 on error
+ */
+int type_string_write_vars(const char *type_str, u8 *data,
+ char * const vars[])
+{
+ size_t offset;
+ u32 value;
+
+ for (offset = 0; *type_str; type_str++, vars++) {
+ switch (*type_str) {
+ case 'b':
+ value = data[offset];
+ offset += 1;
+ break;
+ case 'w':
+ value = get_unaligned_be16(data + offset);
+ offset += 2;
+ break;
+ case 'd':
+ value = get_unaligned_be32(data + offset);
+ offset += 4;
+ break;
+ default:
+ return -1;
+ }
+ if (env_set_ulong(*vars, value))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int tpm_show_device(void)
+{
+ struct udevice *dev;
+ char buf[80];
+ int n = 0, rc;
+
+ for_each_tpm_device(dev) {
+ rc = tpm_get_desc(dev, buf, sizeof(buf));
+ if (rc < 0)
+ printf("device %d: can't get info\n", n);
+ else
+ printf("device %d: %s\n", n, buf);
+
+ n++;
+ };
+
+ return 0;
+}
+
+static int tpm_set_device(unsigned long num)
+{
+ struct udevice *dev;
+ unsigned long n = 0;
+ int rc = CMD_RET_FAILURE;
+
+ for_each_tpm_device(dev) {
+ if (n == num) {
+ rc = 0;
+ break;
+ }
+
+ n++;
+ }
+
+ if (!rc)
+ tpm_dev = dev;
+
+ return rc;
+}
+
+int get_tpm(struct udevice **devp)
+{
+ int rc;
+
+ /*
+ * To keep a backward compatibility with previous code,
+ * if a tpm device is not explicitly set, we set the first one.
+ */
+ if (!tpm_dev) {
+ rc = tpm_set_device(0);
+ if (rc) {
+ printf("Couldn't set TPM 0 (rc = %d)\n", rc);
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ if (devp)
+ *devp = tpm_dev;
+
+ return 0;
+}
+
+int do_tpm_device(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ unsigned long num;
+ int rc;
+
+ if (argc == 2) {
+ num = dectoul(argv[1], NULL);
+
+ rc = tpm_set_device(num);
+ if (rc)
+ printf("Couldn't set TPM %lu (rc = %d)\n", num, rc);
+ } else {
+ rc = tpm_show_device();
+ }
+
+ return rc;
+}
+
+int do_tpm_info(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct udevice *dev;
+ char buf[80];
+ int rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+ rc = tpm_get_desc(dev, buf, sizeof(buf));
+ if (rc < 0) {
+ printf("Couldn't get TPM info (%d)\n", rc);
+ return CMD_RET_FAILURE;
+ }
+ printf("%s\n", buf);
+
+ return 0;
+}
+
+int do_tpm_report_state(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ char buf[80];
+ int rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+ rc = tpm_report_state(dev, buf, sizeof(buf));
+ if (rc < 0) {
+ printf("Couldn't get TPM state (%d)\n", rc);
+ return CMD_RET_FAILURE;
+ }
+ printf("%s\n", buf);
+
+ return 0;
+}
+
+int do_tpm_init(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct udevice *dev;
+ int rc;
+
+ if (argc != 1)
+ return CMD_RET_USAGE;
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ return report_return_code(tpm_init(dev));
+}
+
+int do_tpm_autostart(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int rc;
+
+ if (argc != 1)
+ return CMD_RET_USAGE;
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ return report_return_code(tpm_auto_start(dev));
+}
+
+int do_tpm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct cmd_tbl *tpm_commands, *cmd;
+ struct tpm_chip_priv *priv;
+ struct udevice *dev;
+ unsigned int size;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+
+ priv = dev_get_uclass_priv(dev);
+
+ /* Below getters return NULL if the desired stack is not built */
+ switch (priv->version) {
+ case TPM_V1:
+ tpm_commands = get_tpm1_commands(&size);
+ break;
+ case TPM_V2:
+ tpm_commands = get_tpm2_commands(&size);
+ break;
+ default:
+ tpm_commands = NULL;
+ }
+
+ if (!tpm_commands)
+ return CMD_RET_USAGE;
+
+ cmd = find_cmd_tbl(argv[1], tpm_commands, size);
+ if (!cmd)
+ return CMD_RET_USAGE;
+
+ return cmd->cmd(cmdtp, flag, argc - 1, argv + 1);
+}
diff --git a/cmd/tpm-user-utils.h b/cmd/tpm-user-utils.h
new file mode 100644
index 00000000000..dfa11353e12
--- /dev/null
+++ b/cmd/tpm-user-utils.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2013 The Chromium OS Authors.
+ * Coypright (c) 2013 Guntermann & Drunck GmbH
+ */
+
+#ifndef __TPM_USER_UTILS_H
+#define __TPM_USER_UTILS_H
+
+void print_byte_string(u8 *data, size_t count);
+void *parse_byte_string(char *bytes, u8 *data, size_t *count_ptr);
+int report_return_code(int return_code);
+int type_string_get_num_values(const char *type_str);
+size_t type_string_get_space_size(const char *type_str);
+void *type_string_alloc(const char *type_str, u32 *count);
+int type_string_pack(const char *type_str, char * const values[], u8 *data);
+int type_string_write_vars(const char *type_str, u8 *data, char * const vars[]);
+int get_tpm(struct udevice **devp);
+
+int do_tpm_device(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[]);
+int do_tpm_init(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
+int do_tpm_autostart(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
+int do_tpm_info(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
+int do_tpm_report_state(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[]);
+int do_tpm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
+
+#endif /* __TPM_USER_UTILS_H */
diff --git a/cmd/tpm-v1.c b/cmd/tpm-v1.c
new file mode 100644
index 00000000000..6e019d1c729
--- /dev/null
+++ b/cmd/tpm-v1.c
@@ -0,0 +1,839 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013 The Chromium OS Authors.
+ */
+
+#include <command.h>
+#include <env.h>
+#include <malloc.h>
+#include <vsprintf.h>
+#include <asm/unaligned.h>
+#include <tpm-common.h>
+#include <tpm-v1.h>
+#include "tpm-user-utils.h"
+#include <tpm_api.h>
+
+static int do_tpm_startup(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ enum tpm_startup_type mode;
+ struct udevice *dev;
+ int rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+ if (argc != 2)
+ return CMD_RET_USAGE;
+ if (!strcasecmp("TPM_ST_CLEAR", argv[1])) {
+ mode = TPM_ST_CLEAR;
+ } else if (!strcasecmp("TPM_ST_STATE", argv[1])) {
+ mode = TPM_ST_STATE;
+ } else if (!strcasecmp("TPM_ST_DEACTIVATED", argv[1])) {
+ mode = TPM_ST_DEACTIVATED;
+ } else {
+ printf("Couldn't recognize mode string: %s\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ return report_return_code(tpm_startup(dev, mode));
+}
+
+static int do_tpm_nv_define_space(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 index, perm, size;
+ struct udevice *dev;
+ int rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+ index = simple_strtoul(argv[1], NULL, 0);
+ perm = simple_strtoul(argv[2], NULL, 0);
+ size = simple_strtoul(argv[3], NULL, 0);
+
+ return report_return_code(tpm1_nv_define_space(dev, index, perm, size));
+}
+
+static int do_tpm_nv_read_value(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 index, count, rc;
+ struct udevice *dev;
+ void *data;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+ index = simple_strtoul(argv[1], NULL, 0);
+ data = (void *)simple_strtoul(argv[2], NULL, 0);
+ count = simple_strtoul(argv[3], NULL, 0);
+
+ rc = tpm_nv_read_value(dev, index, data, count);
+ if (!rc) {
+ puts("area content:\n");
+ print_byte_string(data, count);
+ }
+
+ return report_return_code(rc);
+}
+
+static int do_tpm_nv_write_value(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ u32 index, rc;
+ size_t count;
+ void *data;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+ index = simple_strtoul(argv[1], NULL, 0);
+ data = parse_byte_string(argv[2], NULL, &count);
+ if (!data) {
+ printf("Couldn't parse byte string %s\n", argv[2]);
+ return CMD_RET_FAILURE;
+ }
+
+ rc = tpm_nv_write_value(dev, index, data, count);
+ free(data);
+
+ return report_return_code(rc);
+}
+
+static int do_tpm_extend(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u8 in_digest[20], out_digest[20];
+ struct udevice *dev;
+ u32 index, rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+ index = simple_strtoul(argv[1], NULL, 0);
+ if (!parse_byte_string(argv[2], in_digest, NULL)) {
+ printf("Couldn't parse byte string %s\n", argv[2]);
+ return CMD_RET_FAILURE;
+ }
+
+ rc = tpm_pcr_extend(dev, index, in_digest, sizeof(in_digest),
+ out_digest, "cmd");
+ if (!rc) {
+ puts("PCR value after execution of the command:\n");
+ print_byte_string(out_digest, sizeof(out_digest));
+ }
+
+ return report_return_code(rc);
+}
+
+static int do_tpm_pcr_read(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 index, count, rc;
+ struct udevice *dev;
+ void *data;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+ index = simple_strtoul(argv[1], NULL, 0);
+ data = (void *)simple_strtoul(argv[2], NULL, 0);
+ count = simple_strtoul(argv[3], NULL, 0);
+
+ rc = tpm_pcr_read(dev, index, data, count);
+ if (!rc) {
+ puts("Named PCR content:\n");
+ print_byte_string(data, count);
+ }
+
+ return report_return_code(rc);
+}
+
+static int do_tpm_tsc_physical_presence(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct udevice *dev;
+ u16 presence;
+ int rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+ presence = (u16)simple_strtoul(argv[1], NULL, 0);
+
+ return report_return_code(tpm_tsc_physical_presence(dev, presence));
+}
+
+static int do_tpm_read_pubek(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ u32 count, rc;
+ void *data;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+ data = (void *)simple_strtoul(argv[1], NULL, 0);
+ count = simple_strtoul(argv[2], NULL, 0);
+
+ rc = tpm_read_pubek(dev, data, count);
+ if (!rc) {
+ puts("pubek value:\n");
+ print_byte_string(data, count);
+ }
+
+ return report_return_code(rc);
+}
+
+static int do_tpm_physical_set_deactivated(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct udevice *dev;
+ u8 state;
+ int rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+ state = (u8)simple_strtoul(argv[1], NULL, 0);
+
+ return report_return_code(tpm_physical_set_deactivated(dev, state));
+}
+
+static int do_tpm_get_capability(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 cap_area, sub_cap, rc;
+ void *cap;
+ size_t count;
+ struct udevice *dev;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc != 5)
+ return CMD_RET_USAGE;
+ cap_area = simple_strtoul(argv[1], NULL, 0);
+ sub_cap = simple_strtoul(argv[2], NULL, 0);
+ cap = (void *)simple_strtoul(argv[3], NULL, 0);
+ count = simple_strtoul(argv[4], NULL, 0);
+
+ rc = tpm_get_capability(dev, cap_area, sub_cap, cap, count);
+ if (!rc) {
+ puts("capability information:\n");
+ print_byte_string(cap, count);
+ }
+
+ return report_return_code(rc);
+}
+
+static int do_tpm_raw_transfer(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ void *command;
+ u8 response[1024];
+ size_t count, response_length = sizeof(response);
+ u32 rc;
+
+ command = parse_byte_string(argv[1], NULL, &count);
+ if (!command) {
+ printf("Couldn't parse byte string %s\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ rc = tpm_xfer(dev, command, count, response, &response_length);
+ free(command);
+ if (!rc) {
+ puts("tpm response:\n");
+ print_byte_string(response, response_length);
+ }
+
+ return report_return_code(rc);
+}
+
+static int do_tpm_nv_define(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 index, perm, size;
+ struct udevice *dev;
+ int rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+ size = type_string_get_space_size(argv[1]);
+ if (!size) {
+ printf("Couldn't parse arguments\n");
+ return CMD_RET_USAGE;
+ }
+ index = simple_strtoul(argv[2], NULL, 0);
+ perm = simple_strtoul(argv[3], NULL, 0);
+
+ return report_return_code(tpm1_nv_define_space(dev, index, perm, size));
+}
+
+static int do_tpm_nv_read(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 index, count, err;
+ struct udevice *dev;
+ void *data;
+ int rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+ if (argc != 3 + type_string_get_num_values(argv[1]))
+ return CMD_RET_USAGE;
+ index = simple_strtoul(argv[2], NULL, 0);
+ data = type_string_alloc(argv[1], &count);
+ if (!data) {
+ printf("Couldn't parse arguments\n");
+ return CMD_RET_USAGE;
+ }
+
+ err = tpm_nv_read_value(dev, index, data, count);
+ if (!err) {
+ if (type_string_write_vars(argv[1], data, argv + 3)) {
+ printf("Couldn't write to variables\n");
+ err = ~0;
+ }
+ }
+ free(data);
+
+ return report_return_code(err);
+}
+
+static int do_tpm_nv_write(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 index, count, err;
+ struct udevice *dev;
+ void *data;
+ int rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+ if (argc != 3 + type_string_get_num_values(argv[1]))
+ return CMD_RET_USAGE;
+ index = simple_strtoul(argv[2], NULL, 0);
+ data = type_string_alloc(argv[1], &count);
+ if (!data) {
+ printf("Couldn't parse arguments\n");
+ return CMD_RET_USAGE;
+ }
+ if (type_string_pack(argv[1], argv + 3, data)) {
+ printf("Couldn't parse arguments\n");
+ free(data);
+ return CMD_RET_USAGE;
+ }
+
+ err = tpm_nv_write_value(dev, index, data, count);
+ free(data);
+
+ return report_return_code(err);
+}
+
+#ifdef CONFIG_TPM_AUTH_SESSIONS
+
+static int do_tpm_oiap(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 auth_handle, err;
+ struct udevice *dev;
+ int rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ err = tpm1_oiap(dev, &auth_handle);
+
+ return report_return_code(err);
+}
+
+#ifdef CONFIG_TPM_LOAD_KEY_BY_SHA1
+static int do_tpm_load_key_by_sha1(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 parent_handle = 0;
+ u32 key_len, key_handle, err;
+ u8 usage_auth[DIGEST_LENGTH];
+ u8 parent_hash[DIGEST_LENGTH];
+ void *key;
+ struct udevice *dev;
+
+ err = get_tpm(&dev);
+ if (err)
+ return err;
+
+ if (argc < 5)
+ return CMD_RET_USAGE;
+
+ parse_byte_string(argv[1], parent_hash, NULL);
+ key = (void *)simple_strtoul(argv[2], NULL, 0);
+ key_len = simple_strtoul(argv[3], NULL, 0);
+ if (strlen(argv[4]) != 2 * DIGEST_LENGTH)
+ return CMD_RET_FAILURE;
+ parse_byte_string(argv[4], usage_auth, NULL);
+
+ err = tpm1_find_key_sha1(dev, usage_auth, parent_hash, &parent_handle);
+ if (err) {
+ printf("Could not find matching parent key (err = %d)\n", err);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Found parent key %08x\n", parent_handle);
+
+ err = tpm1_load_key2_oiap(dev, parent_handle, key, key_len, usage_auth,
+ &key_handle);
+ if (!err) {
+ printf("Key handle is 0x%x\n", key_handle);
+ env_set_hex("key_handle", key_handle);
+ }
+
+ return report_return_code(err);
+}
+#endif /* CONFIG_TPM_LOAD_KEY_BY_SHA1 */
+
+static int do_tpm_load_key2_oiap(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 parent_handle, key_len, key_handle, err;
+ u8 usage_auth[DIGEST_LENGTH];
+ void *key;
+ struct udevice *dev;
+ int rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc < 5)
+ return CMD_RET_USAGE;
+
+ parent_handle = simple_strtoul(argv[1], NULL, 0);
+ key = (void *)simple_strtoul(argv[2], NULL, 0);
+ key_len = simple_strtoul(argv[3], NULL, 0);
+ if (strlen(argv[4]) != 2 * DIGEST_LENGTH)
+ return CMD_RET_FAILURE;
+ parse_byte_string(argv[4], usage_auth, NULL);
+
+ err = tpm1_load_key2_oiap(dev, parent_handle, key, key_len, usage_auth,
+ &key_handle);
+ if (!err)
+ printf("Key handle is 0x%x\n", key_handle);
+
+ return report_return_code(err);
+}
+
+static int do_tpm_get_pub_key_oiap(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 key_handle, err;
+ u8 usage_auth[DIGEST_LENGTH];
+ u8 pub_key_buffer[TPM_PUBKEY_MAX_LENGTH];
+ size_t pub_key_len = sizeof(pub_key_buffer);
+ struct udevice *dev;
+ int rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ key_handle = simple_strtoul(argv[1], NULL, 0);
+ if (strlen(argv[2]) != 2 * DIGEST_LENGTH)
+ return CMD_RET_FAILURE;
+ parse_byte_string(argv[2], usage_auth, NULL);
+
+ err = tpm1_get_pub_key_oiap(dev, key_handle, usage_auth, pub_key_buffer,
+ &pub_key_len);
+ if (!err) {
+ printf("dump of received pub key structure:\n");
+ print_byte_string(pub_key_buffer, pub_key_len);
+ }
+ return report_return_code(err);
+}
+
+TPM_COMMAND_NO_ARG(tpm1_end_oiap)
+
+#endif /* CONFIG_TPM_AUTH_SESSIONS */
+
+#ifdef CONFIG_TPM_FLUSH_RESOURCES
+static int do_tpm_flush(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int type = 0;
+ int rc;
+
+ rc = get_tpm(&dev);
+ if (rc)
+ return rc;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ if (!strcasecmp(argv[1], "key"))
+ type = TPM_RT_KEY;
+ else if (!strcasecmp(argv[1], "auth"))
+ type = TPM_RT_AUTH;
+ else if (!strcasecmp(argv[1], "hash"))
+ type = TPM_RT_HASH;
+ else if (!strcasecmp(argv[1], "trans"))
+ type = TPM_RT_TRANS;
+ else if (!strcasecmp(argv[1], "context"))
+ type = TPM_RT_CONTEXT;
+ else if (!strcasecmp(argv[1], "counter"))
+ type = TPM_RT_COUNTER;
+ else if (!strcasecmp(argv[1], "delegate"))
+ type = TPM_RT_DELEGATE;
+ else if (!strcasecmp(argv[1], "daa_tpm"))
+ type = TPM_RT_DAA_TPM;
+ else if (!strcasecmp(argv[1], "daa_v0"))
+ type = TPM_RT_DAA_V0;
+ else if (!strcasecmp(argv[1], "daa_v1"))
+ type = TPM_RT_DAA_V1;
+
+ if (!type) {
+ printf("Resource type %s unknown.\n", argv[1]);
+ return -1;
+ }
+
+ if (!strcasecmp(argv[2], "all")) {
+ u16 res_count;
+ u8 buf[288];
+ u8 *ptr;
+ int err;
+ uint i;
+
+ /* fetch list of already loaded resources in the TPM */
+ err = tpm_get_capability(dev, TPM_CAP_HANDLE, type, buf,
+ sizeof(buf));
+ if (err) {
+ printf("tpm_get_capability returned error %d.\n", err);
+ return -1;
+ }
+ res_count = get_unaligned_be16(buf);
+ ptr = buf + 2;
+ for (i = 0; i < res_count; ++i, ptr += 4)
+ tpm1_flush_specific(dev, get_unaligned_be32(ptr), type);
+ } else {
+ u32 handle = simple_strtoul(argv[2], NULL, 0);
+
+ if (!handle) {
+ printf("Illegal resource handle %s\n", argv[2]);
+ return -1;
+ }
+ tpm1_flush_specific(dev, cpu_to_be32(handle), type);
+ }
+
+ return 0;
+}
+#endif /* CONFIG_TPM_FLUSH_RESOURCES */
+
+#ifdef CONFIG_TPM_LIST_RESOURCES
+static int do_tpm_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int type = 0;
+ u16 res_count;
+ u8 buf[288];
+ u8 *ptr;
+ int err;
+ uint i;
+
+ err = get_tpm(&dev);
+ if (err)
+ return err;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ if (!strcasecmp(argv[1], "key"))
+ type = TPM_RT_KEY;
+ else if (!strcasecmp(argv[1], "auth"))
+ type = TPM_RT_AUTH;
+ else if (!strcasecmp(argv[1], "hash"))
+ type = TPM_RT_HASH;
+ else if (!strcasecmp(argv[1], "trans"))
+ type = TPM_RT_TRANS;
+ else if (!strcasecmp(argv[1], "context"))
+ type = TPM_RT_CONTEXT;
+ else if (!strcasecmp(argv[1], "counter"))
+ type = TPM_RT_COUNTER;
+ else if (!strcasecmp(argv[1], "delegate"))
+ type = TPM_RT_DELEGATE;
+ else if (!strcasecmp(argv[1], "daa_tpm"))
+ type = TPM_RT_DAA_TPM;
+ else if (!strcasecmp(argv[1], "daa_v0"))
+ type = TPM_RT_DAA_V0;
+ else if (!strcasecmp(argv[1], "daa_v1"))
+ type = TPM_RT_DAA_V1;
+
+ if (!type) {
+ printf("Resource type %s unknown.\n", argv[1]);
+ return -1;
+ }
+
+ /* fetch list of already loaded resources in the TPM */
+ err = tpm_get_capability(dev, TPM_CAP_HANDLE, type, buf,
+ sizeof(buf));
+ if (err) {
+ printf("tpm_get_capability returned error %d.\n", err);
+ return -1;
+ }
+ res_count = get_unaligned_be16(buf);
+ ptr = buf + 2;
+
+ printf("Resources of type %s (%02x):\n", argv[1], type);
+ if (!res_count) {
+ puts("None\n");
+ } else {
+ for (i = 0; i < res_count; ++i, ptr += 4)
+ printf("Index %d: %08x\n", i, get_unaligned_be32(ptr));
+ }
+
+ return 0;
+}
+#endif /* CONFIG_TPM_LIST_RESOURCES */
+
+TPM_COMMAND_NO_ARG(tpm_self_test_full)
+TPM_COMMAND_NO_ARG(tpm_continue_self_test)
+TPM_COMMAND_NO_ARG(tpm_force_clear)
+TPM_COMMAND_NO_ARG(tpm_physical_enable)
+TPM_COMMAND_NO_ARG(tpm_physical_disable)
+
+static struct cmd_tbl tpm1_commands[] = {
+ U_BOOT_CMD_MKENT(device, 0, 1, do_tpm_device, "", ""),
+ U_BOOT_CMD_MKENT(info, 0, 1, do_tpm_info, "", ""),
+ U_BOOT_CMD_MKENT(autostart, 0, 1, do_tpm_autostart, "", ""),
+ U_BOOT_CMD_MKENT(init, 0, 1, do_tpm_init, "", ""),
+ U_BOOT_CMD_MKENT(startup, 0, 1,
+ do_tpm_startup, "", ""),
+ U_BOOT_CMD_MKENT(self_test_full, 0, 1,
+ do_tpm_self_test_full, "", ""),
+ U_BOOT_CMD_MKENT(continue_self_test, 0, 1,
+ do_tpm_continue_self_test, "", ""),
+ U_BOOT_CMD_MKENT(force_clear, 0, 1,
+ do_tpm_force_clear, "", ""),
+ U_BOOT_CMD_MKENT(physical_enable, 0, 1,
+ do_tpm_physical_enable, "", ""),
+ U_BOOT_CMD_MKENT(physical_disable, 0, 1,
+ do_tpm_physical_disable, "", ""),
+ U_BOOT_CMD_MKENT(nv_define_space, 0, 1,
+ do_tpm_nv_define_space, "", ""),
+ U_BOOT_CMD_MKENT(nv_read_value, 0, 1,
+ do_tpm_nv_read_value, "", ""),
+ U_BOOT_CMD_MKENT(nv_write_value, 0, 1,
+ do_tpm_nv_write_value, "", ""),
+ U_BOOT_CMD_MKENT(extend, 0, 1,
+ do_tpm_extend, "", ""),
+ U_BOOT_CMD_MKENT(pcr_read, 0, 1,
+ do_tpm_pcr_read, "", ""),
+ U_BOOT_CMD_MKENT(tsc_physical_presence, 0, 1,
+ do_tpm_tsc_physical_presence, "", ""),
+ U_BOOT_CMD_MKENT(read_pubek, 0, 1,
+ do_tpm_read_pubek, "", ""),
+ U_BOOT_CMD_MKENT(physical_set_deactivated, 0, 1,
+ do_tpm_physical_set_deactivated, "", ""),
+ U_BOOT_CMD_MKENT(get_capability, 0, 1,
+ do_tpm_get_capability, "", ""),
+ U_BOOT_CMD_MKENT(raw_transfer, 0, 1,
+ do_tpm_raw_transfer, "", ""),
+ U_BOOT_CMD_MKENT(nv_define, 0, 1,
+ do_tpm_nv_define, "", ""),
+ U_BOOT_CMD_MKENT(nv_read, 0, 1,
+ do_tpm_nv_read, "", ""),
+ U_BOOT_CMD_MKENT(nv_write, 0, 1,
+ do_tpm_nv_write, "", ""),
+#ifdef CONFIG_TPM_AUTH_SESSIONS
+ U_BOOT_CMD_MKENT(oiap, 0, 1,
+ do_tpm_oiap, "", ""),
+ U_BOOT_CMD_MKENT(end_oiap, 0, 1,
+ do_tpm1_end_oiap, "", ""),
+ U_BOOT_CMD_MKENT(load_key2_oiap, 0, 1,
+ do_tpm_load_key2_oiap, "", ""),
+#ifdef CONFIG_TPM_LOAD_KEY_BY_SHA1
+ U_BOOT_CMD_MKENT(load_key_by_sha1, 0, 1,
+ do_tpm_load_key_by_sha1, "", ""),
+#endif /* CONFIG_TPM_LOAD_KEY_BY_SHA1 */
+ U_BOOT_CMD_MKENT(get_pub_key_oiap, 0, 1,
+ do_tpm_get_pub_key_oiap, "", ""),
+#endif /* CONFIG_TPM_AUTH_SESSIONS */
+#ifdef CONFIG_TPM_FLUSH_RESOURCES
+ U_BOOT_CMD_MKENT(flush, 0, 1,
+ do_tpm_flush, "", ""),
+#endif /* CONFIG_TPM_FLUSH_RESOURCES */
+#ifdef CONFIG_TPM_LIST_RESOURCES
+ U_BOOT_CMD_MKENT(list, 0, 1,
+ do_tpm_list, "", ""),
+#endif /* CONFIG_TPM_LIST_RESOURCES */
+};
+
+struct cmd_tbl *get_tpm1_commands(unsigned int *size)
+{
+ *size = ARRAY_SIZE(tpm1_commands);
+
+ return tpm1_commands;
+}
+
+U_BOOT_CMD(tpm, CONFIG_SYS_MAXARGS, 1, do_tpm,
+"Issue a TPMv1.x command",
+"cmd args...\n"
+" - Issue TPM command <cmd> with arguments <args...>.\n"
+"Admin Startup and State Commands:\n"
+" device [num device]\n"
+" - Show all devices or set the specified device\n"
+" info - Show information about the TPM\n"
+" autostart\n"
+" - Initalize the tpm, perform a Startup(clear) and run a full selftest\n"
+" sequence\n"
+" init\n"
+" - Put TPM into a state where it waits for 'startup' command.\n"
+" startup mode\n"
+" - Issue TPM_Starup command. <mode> is one of TPM_ST_CLEAR,\n"
+" TPM_ST_STATE, and TPM_ST_DEACTIVATED.\n"
+"Admin Testing Commands:\n"
+" self_test_full\n"
+" - Test all of the TPM capabilities.\n"
+" continue_self_test\n"
+" - Inform TPM that it should complete the self-test.\n"
+"Admin Opt-in Commands:\n"
+" physical_enable\n"
+" - Set the PERMANENT disable flag to FALSE using physical presence as\n"
+" authorization.\n"
+" physical_disable\n"
+" - Set the PERMANENT disable flag to TRUE using physical presence as\n"
+" authorization.\n"
+" physical_set_deactivated 0|1\n"
+" - Set deactivated flag.\n"
+"Admin Ownership Commands:\n"
+" force_clear\n"
+" - Issue TPM_ForceClear command.\n"
+" tsc_physical_presence flags\n"
+" - Set TPM device's Physical Presence flags to <flags>.\n"
+"The Capability Commands:\n"
+" get_capability cap_area sub_cap addr count\n"
+" - Read <count> bytes of TPM capability indexed by <cap_area> and\n"
+" <sub_cap> to memory address <addr>.\n"
+#if defined(CONFIG_TPM_FLUSH_RESOURCES) || defined(CONFIG_TPM_LIST_RESOURCES)
+"Resource management functions\n"
+#endif
+#ifdef CONFIG_TPM_FLUSH_RESOURCES
+" flush resource_type id\n"
+" - flushes a resource of type <resource_type> (may be one of key, auth,\n"
+" hash, trans, context, counter, delegate, daa_tpm, daa_v0, daa_v1),\n"
+" and id <id> from the TPM. Use an <id> of \"all\" to flush all\n"
+" resources of that type.\n"
+#endif /* CONFIG_TPM_FLUSH_RESOURCES */
+#ifdef CONFIG_TPM_LIST_RESOURCES
+" list resource_type\n"
+" - lists resources of type <resource_type> (may be one of key, auth,\n"
+" hash, trans, context, counter, delegate, daa_tpm, daa_v0, daa_v1),\n"
+" contained in the TPM.\n"
+#endif /* CONFIG_TPM_LIST_RESOURCES */
+#ifdef CONFIG_TPM_AUTH_SESSIONS
+"Storage functions\n"
+" loadkey2_oiap parent_handle key_addr key_len usage_auth\n"
+" - loads a key data from memory address <key_addr>, <key_len> bytes\n"
+" into TPM using the parent key <parent_handle> with authorization\n"
+" <usage_auth> (20 bytes hex string).\n"
+#ifdef CONFIG_TPM_LOAD_KEY_BY_SHA1
+" load_key_by_sha1 parent_hash key_addr key_len usage_auth\n"
+" - loads a key data from memory address <key_addr>, <key_len> bytes\n"
+" into TPM using the parent hash <parent_hash> (20 bytes hex string)\n"
+" with authorization <usage_auth> (20 bytes hex string).\n"
+#endif /* CONFIG_TPM_LOAD_KEY_BY_SHA1 */
+" get_pub_key_oiap key_handle usage_auth\n"
+" - get the public key portion of a loaded key <key_handle> using\n"
+" authorization <usage auth> (20 bytes hex string)\n"
+#endif /* CONFIG_TPM_AUTH_SESSIONS */
+"Endorsement Key Handling Commands:\n"
+" read_pubek addr count\n"
+" - Read <count> bytes of the public endorsement key to memory\n"
+" address <addr>\n"
+"Integrity Collection and Reporting Commands:\n"
+" extend index digest_hex_string\n"
+" - Add a new measurement to a PCR. Update PCR <index> with the 20-bytes\n"
+" <digest_hex_string>\n"
+" pcr_read index addr count\n"
+" - Read <count> bytes from PCR <index> to memory address <addr>.\n"
+#ifdef CONFIG_TPM_AUTH_SESSIONS
+"Authorization Sessions\n"
+" oiap\n"
+" - setup an OIAP session\n"
+" end_oiap\n"
+" - terminates an active OIAP session\n"
+#endif /* CONFIG_TPM_AUTH_SESSIONS */
+"Non-volatile Storage Commands:\n"
+" nv_define_space index permission size\n"
+" - Establish a space at index <index> with <permission> of <size> bytes.\n"
+" nv_read_value index addr count\n"
+" - Read <count> bytes from space <index> to memory address <addr>.\n"
+" nv_write_value index addr count\n"
+" - Write <count> bytes from memory address <addr> to space <index>.\n"
+"Miscellaneous helper functions:\n"
+" raw_transfer byte_string\n"
+" - Send a byte string <byte_string> to TPM and print the response.\n"
+" Non-volatile storage helper functions:\n"
+" These helper functions treat a non-volatile space as a non-padded\n"
+" sequence of integer values. These integer values are defined by a type\n"
+" string, which is a text string of 'bwd' characters: 'b' means a 8-bit\n"
+" value, 'w' 16-bit value, 'd' 32-bit value. All helper functions take\n"
+" a type string as their first argument.\n"
+" nv_define type_string index perm\n"
+" - Define a space <index> with permission <perm>.\n"
+" nv_read types_string index vars...\n"
+" - Read from space <index> to environment variables <vars...>.\n"
+" nv_write types_string index values...\n"
+" - Write to space <index> from values <values...>.\n"
+);
diff --git a/cmd/tpm-v2.c b/cmd/tpm-v2.c
new file mode 100644
index 00000000000..346e21d27bb
--- /dev/null
+++ b/cmd/tpm-v2.c
@@ -0,0 +1,598 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2018 Bootlin
+ * Author: Miquel Raynal <miquel.raynal@bootlin.com>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <log.h>
+#include <mapmem.h>
+#include <tpm-common.h>
+#include <tpm-v2.h>
+#include "tpm-user-utils.h"
+
+static int do_tpm2_startup(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ enum tpm2_startup_types mode;
+ struct udevice *dev;
+ int ret;
+ bool bon = true;
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+
+ /* argv[2] is optional to perform a TPM2_CC_SHUTDOWN */
+ if (argc > 3 || (argc == 3 && strcasecmp("off", argv[2])))
+ return CMD_RET_USAGE;
+
+ if (!strcasecmp("TPM2_SU_CLEAR", argv[1])) {
+ mode = TPM2_SU_CLEAR;
+ } else if (!strcasecmp("TPM2_SU_STATE", argv[1])) {
+ mode = TPM2_SU_STATE;
+ } else {
+ printf("Couldn't recognize mode string: %s\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ if (argv[2])
+ bon = false;
+
+ return report_return_code(tpm2_startup(dev, bon, mode));
+}
+
+static int do_tpm2_self_test(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ enum tpm2_yes_no full_test;
+ struct udevice *dev;
+ int ret;
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ if (!strcasecmp("full", argv[1])) {
+ full_test = TPMI_YES;
+ } else if (!strcasecmp("continue", argv[1])) {
+ full_test = TPMI_NO;
+ } else {
+ printf("Couldn't recognize test mode: %s\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ return report_return_code(tpm2_self_test(dev, full_test));
+}
+
+static int do_tpm2_clear(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 handle = 0;
+ const char *pw = (argc < 3) ? NULL : argv[2];
+ const ssize_t pw_sz = pw ? strlen(pw) : 0;
+ struct udevice *dev;
+ int ret;
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+
+ if (argc < 2 || argc > 3)
+ return CMD_RET_USAGE;
+
+ if (pw_sz > TPM2_DIGEST_LEN)
+ return -EINVAL;
+
+ if (!strcasecmp("TPM2_RH_LOCKOUT", argv[1]))
+ handle = TPM2_RH_LOCKOUT;
+ else if (!strcasecmp("TPM2_RH_PLATFORM", argv[1]))
+ handle = TPM2_RH_PLATFORM;
+ else
+ return CMD_RET_USAGE;
+
+ return report_return_code(tpm2_clear(dev, handle, pw, pw_sz));
+}
+
+static int do_tpm2_pcr_extend(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ struct tpm_chip_priv *priv;
+ u32 index = simple_strtoul(argv[1], NULL, 0);
+ void *digest = map_sysmem(simple_strtoul(argv[2], NULL, 0), 0);
+ int algo = TPM2_ALG_SHA256;
+ int algo_len;
+ int ret;
+ u32 rc;
+
+ if (argc < 3 || argc > 4)
+ return CMD_RET_USAGE;
+ if (argc == 4) {
+ algo = tpm2_name_to_algorithm(argv[3]);
+ if (algo == TPM2_ALG_INVAL)
+ return CMD_RET_FAILURE;
+ }
+ algo_len = tpm2_algorithm_to_len(algo);
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+
+ priv = dev_get_uclass_priv(dev);
+ if (!priv)
+ return -EINVAL;
+
+ if (index >= priv->pcr_count)
+ return -EINVAL;
+
+ rc = tpm2_pcr_extend(dev, index, algo, digest, algo_len);
+ if (!rc) {
+ printf("PCR #%u extended with %d byte %s digest\n", index,
+ algo_len, tpm2_algorithm_name(algo));
+ print_byte_string(digest, algo_len);
+ }
+
+ unmap_sysmem(digest);
+
+ return report_return_code(rc);
+}
+
+static int do_tpm_pcr_read(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ enum tpm2_algorithms algo = TPM2_ALG_SHA256;
+ struct udevice *dev;
+ struct tpm_chip_priv *priv;
+ u32 index, rc;
+ int algo_len;
+ unsigned int updates;
+ void *data;
+ int ret;
+
+ if (argc < 3 || argc > 4)
+ return CMD_RET_USAGE;
+ if (argc == 4) {
+ algo = tpm2_name_to_algorithm(argv[3]);
+ if (algo == TPM2_ALG_INVAL)
+ return CMD_RET_FAILURE;
+ }
+ algo_len = tpm2_algorithm_to_len(algo);
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+
+ priv = dev_get_uclass_priv(dev);
+ if (!priv)
+ return -EINVAL;
+
+ index = simple_strtoul(argv[1], NULL, 0);
+ if (index >= priv->pcr_count)
+ return -EINVAL;
+
+ data = map_sysmem(simple_strtoul(argv[2], NULL, 0), 0);
+
+ rc = tpm2_pcr_read(dev, index, priv->pcr_select_min, algo,
+ data, algo_len, &updates);
+ if (!rc) {
+ printf("PCR #%u %s %d byte content (%u known updates):\n", index,
+ tpm2_algorithm_name(algo), algo_len, updates);
+ print_byte_string(data, algo_len);
+ }
+
+ unmap_sysmem(data);
+
+ return report_return_code(rc);
+}
+
+static int do_tpm_get_capability(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 capability, property, rc;
+ u8 *data;
+ size_t count;
+ int i, j;
+ struct udevice *dev;
+ int ret;
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+
+ if (argc != 5)
+ return CMD_RET_USAGE;
+
+ capability = simple_strtoul(argv[1], NULL, 0);
+ property = simple_strtoul(argv[2], NULL, 0);
+ data = map_sysmem(simple_strtoul(argv[3], NULL, 0), 0);
+ count = simple_strtoul(argv[4], NULL, 0);
+
+ rc = tpm2_get_capability(dev, capability, property, data, count);
+ if (rc)
+ goto unmap_data;
+
+ printf("Capabilities read from TPM:\n");
+ for (i = 0; i < count; i++) {
+ printf("Property 0x");
+ for (j = 0; j < 4; j++)
+ printf("%02x", data[(i * 8) + j + sizeof(u32)]);
+ printf(": 0x");
+ for (j = 4; j < 8; j++)
+ printf("%02x", data[(i * 8) + j + sizeof(u32)]);
+ printf("\n");
+ }
+
+unmap_data:
+ unmap_sysmem(data);
+
+ return report_return_code(rc);
+}
+
+static u32 select_mask(u32 mask, enum tpm2_algorithms algo, bool select)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(hash_algo_list); i++) {
+ if (hash_algo_list[i].hash_alg != algo)
+ continue;
+
+ if (select)
+ mask |= hash_algo_list[i].hash_mask;
+ else
+ mask &= ~hash_algo_list[i].hash_mask;
+
+ break;
+ }
+
+ return mask;
+}
+
+static bool
+is_algo_in_pcrs(enum tpm2_algorithms algo, struct tpml_pcr_selection *pcrs)
+{
+ size_t i;
+
+ for (i = 0; i < pcrs->count; i++) {
+ if (algo == pcrs->selection[i].hash)
+ return true;
+ }
+
+ return false;
+}
+
+static int do_tpm2_pcrallocate(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int ret;
+ enum tpm2_algorithms algo;
+ const char *pw = (argc < 4) ? NULL : argv[3];
+ const ssize_t pw_sz = pw ? strlen(pw) : 0;
+ static struct tpml_pcr_selection pcr = { 0 };
+ u32 pcr_len = 0;
+ bool bon = false;
+ static u32 mask;
+ int i;
+
+ /* argv[1]: algorithm (bank), argv[2]: on/off */
+ if (argc < 3 || argc > 4)
+ return CMD_RET_USAGE;
+
+ if (!strcasecmp("on", argv[2]))
+ bon = true;
+ else if (strcasecmp("off", argv[2]))
+ return CMD_RET_USAGE;
+
+ algo = tpm2_name_to_algorithm(argv[1]);
+ if (algo == TPM2_ALG_INVAL)
+ return CMD_RET_USAGE;
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+
+ if (!pcr.count) {
+ /*
+ * Get current active algorithms (banks), PCRs and mask via the
+ * first call
+ */
+ ret = tpm2_get_pcr_info(dev, &pcr);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < pcr.count; i++) {
+ struct tpms_pcr_selection *sel = &pcr.selection[i];
+ const char *name;
+
+ if (!tpm2_is_active_bank(sel))
+ continue;
+
+ mask = select_mask(mask, sel->hash, true);
+ name = tpm2_algorithm_name(sel->hash);
+ if (name)
+ printf("Active bank[%d]: %s\n", i, name);
+ }
+ }
+
+ if (!is_algo_in_pcrs(algo, &pcr)) {
+ printf("%s is not supported by the tpm device\n", argv[1]);
+ return CMD_RET_USAGE;
+ }
+
+ mask = select_mask(mask, algo, bon);
+ ret = tpm2_pcr_config_algo(dev, mask, &pcr, &pcr_len);
+ if (ret)
+ return ret;
+
+ return report_return_code(tpm2_send_pcr_allocate(dev, pw, pw_sz, &pcr,
+ pcr_len));
+}
+
+static int do_tpm_dam_reset(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *pw = (argc < 2) ? NULL : argv[1];
+ const ssize_t pw_sz = pw ? strlen(pw) : 0;
+ struct udevice *dev;
+ int ret;
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+
+ if (argc > 2)
+ return CMD_RET_USAGE;
+
+ if (pw_sz > TPM2_DIGEST_LEN)
+ return -EINVAL;
+
+ return report_return_code(tpm2_dam_reset(dev, pw, pw_sz));
+}
+
+static int do_tpm_dam_parameters(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *pw = (argc < 5) ? NULL : argv[4];
+ const ssize_t pw_sz = pw ? strlen(pw) : 0;
+ /*
+ * No Dictionary Attack Mitigation (DAM) means:
+ * maxtries = 0xFFFFFFFF, recovery_time = 1, lockout_recovery = 0
+ */
+ unsigned long int max_tries;
+ unsigned long int recovery_time;
+ unsigned long int lockout_recovery;
+ struct udevice *dev;
+ int ret;
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+
+ if (argc < 4 || argc > 5)
+ return CMD_RET_USAGE;
+
+ if (pw_sz > TPM2_DIGEST_LEN)
+ return -EINVAL;
+
+ if (strict_strtoul(argv[1], 0, &max_tries))
+ return CMD_RET_USAGE;
+
+ if (strict_strtoul(argv[2], 0, &recovery_time))
+ return CMD_RET_USAGE;
+
+ if (strict_strtoul(argv[3], 0, &lockout_recovery))
+ return CMD_RET_USAGE;
+
+ log(LOGC_NONE, LOGL_INFO, "Changing dictionary attack parameters:\n");
+ log(LOGC_NONE, LOGL_INFO, "- maxTries: %lu", max_tries);
+ log(LOGC_NONE, LOGL_INFO, "- recoveryTime: %lu\n", recovery_time);
+ log(LOGC_NONE, LOGL_INFO, "- lockoutRecovery: %lu\n", lockout_recovery);
+
+ return report_return_code(tpm2_dam_parameters(dev, pw, pw_sz, max_tries,
+ recovery_time,
+ lockout_recovery));
+}
+
+static int do_tpm_change_auth(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 handle;
+ const char *newpw = argv[2];
+ const char *oldpw = (argc == 3) ? NULL : argv[3];
+ const ssize_t newpw_sz = strlen(newpw);
+ const ssize_t oldpw_sz = oldpw ? strlen(oldpw) : 0;
+ struct udevice *dev;
+ int ret;
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+
+ if (argc < 3 || argc > 4)
+ return CMD_RET_USAGE;
+
+ if (newpw_sz > TPM2_DIGEST_LEN || oldpw_sz > TPM2_DIGEST_LEN)
+ return -EINVAL;
+
+ if (!strcasecmp("TPM2_RH_LOCKOUT", argv[1]))
+ handle = TPM2_RH_LOCKOUT;
+ else if (!strcasecmp("TPM2_RH_ENDORSEMENT", argv[1]))
+ handle = TPM2_RH_ENDORSEMENT;
+ else if (!strcasecmp("TPM2_RH_OWNER", argv[1]))
+ handle = TPM2_RH_OWNER;
+ else if (!strcasecmp("TPM2_RH_PLATFORM", argv[1]))
+ handle = TPM2_RH_PLATFORM;
+ else
+ return CMD_RET_USAGE;
+
+ return report_return_code(tpm2_change_auth(dev, handle, newpw, newpw_sz,
+ oldpw, oldpw_sz));
+}
+
+static int do_tpm_pcr_setauthpolicy(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u32 index = simple_strtoul(argv[1], NULL, 0);
+ char *key = argv[2];
+ const char *pw = (argc < 4) ? NULL : argv[3];
+ const ssize_t pw_sz = pw ? strlen(pw) : 0;
+ struct udevice *dev;
+ int ret;
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+
+ if (strlen(key) != TPM2_DIGEST_LEN)
+ return -EINVAL;
+
+ if (argc < 3 || argc > 4)
+ return CMD_RET_USAGE;
+
+ return report_return_code(tpm2_pcr_setauthpolicy(dev, pw, pw_sz, index,
+ key));
+}
+
+static int do_tpm_pcr_setauthvalue(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ u32 index = simple_strtoul(argv[1], NULL, 0);
+ char *key = argv[2];
+ const ssize_t key_sz = strlen(key);
+ const char *pw = (argc < 4) ? NULL : argv[3];
+ const ssize_t pw_sz = pw ? strlen(pw) : 0;
+ struct udevice *dev;
+ int ret;
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+
+ if (strlen(key) != TPM2_DIGEST_LEN)
+ return -EINVAL;
+
+ if (argc < 3 || argc > 4)
+ return CMD_RET_USAGE;
+
+ return report_return_code(tpm2_pcr_setauthvalue(dev, pw, pw_sz, index,
+ key, key_sz));
+}
+
+static struct cmd_tbl tpm2_commands[] = {
+ U_BOOT_CMD_MKENT(device, 0, 1, do_tpm_device, "", ""),
+ U_BOOT_CMD_MKENT(info, 0, 1, do_tpm_info, "", ""),
+ U_BOOT_CMD_MKENT(state, 0, 1, do_tpm_report_state, "", ""),
+ U_BOOT_CMD_MKENT(init, 0, 1, do_tpm_init, "", ""),
+ U_BOOT_CMD_MKENT(startup, 0, 1, do_tpm2_startup, "", ""),
+ U_BOOT_CMD_MKENT(self_test, 0, 1, do_tpm2_self_test, "", ""),
+ U_BOOT_CMD_MKENT(clear, 0, 1, do_tpm2_clear, "", ""),
+ U_BOOT_CMD_MKENT(pcr_extend, 0, 1, do_tpm2_pcr_extend, "", ""),
+ U_BOOT_CMD_MKENT(pcr_read, 0, 1, do_tpm_pcr_read, "", ""),
+ U_BOOT_CMD_MKENT(get_capability, 0, 1, do_tpm_get_capability, "", ""),
+ U_BOOT_CMD_MKENT(dam_reset, 0, 1, do_tpm_dam_reset, "", ""),
+ U_BOOT_CMD_MKENT(dam_parameters, 0, 1, do_tpm_dam_parameters, "", ""),
+ U_BOOT_CMD_MKENT(change_auth, 0, 1, do_tpm_change_auth, "", ""),
+ U_BOOT_CMD_MKENT(autostart, 0, 1, do_tpm_autostart, "", ""),
+ U_BOOT_CMD_MKENT(pcr_setauthpolicy, 0, 1,
+ do_tpm_pcr_setauthpolicy, "", ""),
+ U_BOOT_CMD_MKENT(pcr_setauthvalue, 0, 1,
+ do_tpm_pcr_setauthvalue, "", ""),
+ U_BOOT_CMD_MKENT(pcr_allocate, 0, 1, do_tpm2_pcrallocate, "", ""),
+};
+
+struct cmd_tbl *get_tpm2_commands(unsigned int *size)
+{
+ *size = ARRAY_SIZE(tpm2_commands);
+
+ return tpm2_commands;
+}
+
+U_BOOT_CMD(tpm2, CONFIG_SYS_MAXARGS, 1, do_tpm, "Issue a TPMv2.x command",
+"<command> [<arguments>]\n"
+"\n"
+"device [num device]\n"
+" Show all devices or set the specified device\n"
+"info\n"
+" Show information about the TPM.\n"
+"state\n"
+" Show internal state from the TPM (if available)\n"
+"autostart\n"
+" Initalize the tpm, perform a Startup(clear) and run a full selftest\n"
+" sequence\n"
+"init\n"
+" Initialize the software stack. Always the first command to issue.\n"
+" 'tpm startup' is the only acceptable command after a 'tpm init' has been\n"
+" issued\n"
+"startup <mode> [<op>]\n"
+" Issue a TPM2_Startup command.\n"
+" <mode> is one of:\n"
+" * TPM2_SU_CLEAR (reset state)\n"
+" * TPM2_SU_STATE (preserved state)\n"
+" <op>:\n"
+" * off - To shutdown the TPM\n"
+"self_test <type>\n"
+" Test the TPM capabilities.\n"
+" <type> is one of:\n"
+" * full (perform all tests)\n"
+" * continue (only check untested tests)\n"
+"clear <hierarchy>\n"
+" Issue a TPM2_Clear command.\n"
+" <hierarchy> is one of:\n"
+" * TPM2_RH_LOCKOUT\n"
+" * TPM2_RH_PLATFORM\n"
+"pcr_extend <pcr> <digest_addr> [<digest_algo>]\n"
+" Extend PCR #<pcr> with digest at <digest_addr> with digest_algo.\n"
+" <pcr>: index of the PCR\n"
+" <digest_addr>: address of digest of digest_algo type (defaults to SHA256)\n"
+"pcr_read <pcr> <digest_addr> [<digest_algo>]\n"
+" Read PCR #<pcr> to memory address <digest_addr> with <digest_algo>.\n"
+" <pcr>: index of the PCR\n"
+" <digest_addr>: address of digest of digest_algo type (defaults to SHA256)\n"
+"get_capability <capability> <property> <addr> <count>\n"
+" Read and display <count> entries indexed by <capability>/<property>.\n"
+" Values are 4 bytes long and are written at <addr>.\n"
+" <capability>: capability\n"
+" <property>: property\n"
+" <addr>: address to store <count> entries of 4 bytes\n"
+" <count>: number of entries to retrieve\n"
+"dam_reset [<password>]\n"
+" If the TPM is not in a LOCKOUT state, reset the internal error counter.\n"
+" <password>: optional password\n"
+"dam_parameters <max_tries> <recovery_time> <lockout_recovery> [<password>]\n"
+" If the TPM is not in a LOCKOUT state, set the DAM parameters\n"
+" <maxTries>: maximum number of failures before lockout,\n"
+" 0 means always locking\n"
+" <recoveryTime>: time before decrement of the error counter,\n"
+" 0 means no lockout\n"
+" <lockoutRecovery>: time of a lockout (before the next try),\n"
+" 0 means a reboot is needed\n"
+" <password>: optional password of the LOCKOUT hierarchy\n"
+"change_auth <hierarchy> <new_pw> [<old_pw>]\n"
+" <hierarchy>: the hierarchy\n"
+" <new_pw>: new password for <hierarchy>\n"
+" <old_pw>: optional previous password of <hierarchy>\n"
+"pcr_setauthpolicy|pcr_setauthvalue <pcr> <key> [<password>]\n"
+" Change the <key> to access PCR #<pcr>.\n"
+" hierarchy and may be empty.\n"
+" /!\\WARNING: untested function, use at your own risks !\n"
+" <pcr>: index of the PCR\n"
+" <key>: secret to protect the access of PCR #<pcr>\n"
+" <password>: optional password of the PLATFORM hierarchy\n"
+"pcr_allocate <algorithm> <on/off> [<password>]\n"
+" Issue a TPM2_PCR_Allocate Command to reconfig PCR bank algorithm.\n"
+" <algorithm> is one of:\n"
+" * sha1\n"
+" * sha256\n"
+" * sha384\n"
+" * sha512\n"
+" <on|off> is one of:\n"
+" * on - Select all available PCRs associated with the specified\n"
+" algorithm (bank)\n"
+" * off - Clear all available PCRs associated with the specified\n"
+" algorithm (bank)\n"
+" <password>: optional password\n"
+);
diff --git a/cmd/tpm_test.c b/cmd/tpm_test.c
new file mode 100644
index 00000000000..af83d78c3fe
--- /dev/null
+++ b/cmd/tpm_test.c
@@ -0,0 +1,573 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2015 Google, Inc
+ */
+
+#include <command.h>
+#include <cpu_func.h>
+#include <log.h>
+#include <time.h>
+#include <tpm-v1.h>
+#include <linux/printk.h>
+#include "tpm-user-utils.h"
+#include <tpm_api.h>
+
+/* Prints error and returns on failure */
+#define TPM_CHECK(tpm_command) do { \
+ uint32_t result; \
+ \
+ result = (tpm_command); \
+ if (result != TPM_SUCCESS) { \
+ printf("TEST FAILED: line %d: " #tpm_command ": 0x%x\n", \
+ __LINE__, result); \
+ return result; \
+ } \
+} while (0)
+
+#define INDEX0 0xda70
+#define INDEX1 0xda71
+#define INDEX2 0xda72
+#define INDEX3 0xda73
+#define INDEX_INITIALISED 0xda80
+#define PHYS_PRESENCE 4
+#define PRESENCE 8
+
+static uint32_t TlclStartupIfNeeded(struct udevice *dev)
+{
+ uint32_t result = tpm_startup(dev, TPM_ST_CLEAR);
+
+ return result == TPM_INVALID_POSTINIT ? TPM_SUCCESS : result;
+}
+
+static int test_timer(struct udevice *dev)
+{
+ printf("get_timer(0) = %lu\n", get_timer(0));
+ return 0;
+}
+
+static uint32_t tpm_get_flags(struct udevice *dev, uint8_t *disable,
+ uint8_t *deactivated, uint8_t *nvlocked)
+{
+ struct tpm_permanent_flags pflags;
+ uint32_t result;
+
+ result = tpm1_get_permanent_flags(dev, &pflags);
+ if (result)
+ return result;
+ if (disable)
+ *disable = pflags.disable;
+ if (deactivated)
+ *deactivated = pflags.deactivated;
+ if (nvlocked)
+ *nvlocked = pflags.nv_locked;
+ debug("TPM: Got flags disable=%d, deactivated=%d, nvlocked=%d\n",
+ pflags.disable, pflags.deactivated, pflags.nv_locked);
+
+ return 0;
+}
+
+static uint32_t tpm_nv_write_value_lock(struct udevice *dev, uint32_t index)
+{
+ debug("TPM: Write lock 0x%x\n", index);
+
+ return tpm_nv_write_value(dev, index, NULL, 0);
+}
+
+static int tpm_is_owned(struct udevice *dev)
+{
+ uint8_t response[TPM_PUBEK_SIZE];
+ uint32_t result;
+
+ result = tpm_read_pubek(dev, response, sizeof(response));
+
+ return result != TPM_SUCCESS;
+}
+
+static int test_early_extend(struct udevice *dev)
+{
+ uint8_t value_in[20];
+ uint8_t value_out[20];
+
+ printf("Testing earlyextend ...");
+ tpm_init(dev);
+ TPM_CHECK(tpm_startup(dev, TPM_ST_CLEAR));
+ TPM_CHECK(tpm_continue_self_test(dev));
+ TPM_CHECK(tpm_pcr_extend(dev, 1, value_in, sizeof(value_in), value_out,
+ "test"));
+ printf("done\n");
+ return 0;
+}
+
+static int test_early_nvram(struct udevice *dev)
+{
+ uint32_t x;
+
+ printf("Testing earlynvram ...");
+ tpm_init(dev);
+ TPM_CHECK(tpm_startup(dev, TPM_ST_CLEAR));
+ TPM_CHECK(tpm_continue_self_test(dev));
+ TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
+ TPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)));
+ printf("done\n");
+ return 0;
+}
+
+static int test_early_nvram2(struct udevice *dev)
+{
+ uint32_t x;
+
+ printf("Testing earlynvram2 ...");
+ tpm_init(dev);
+ TPM_CHECK(tpm_startup(dev, TPM_ST_CLEAR));
+ TPM_CHECK(tpm_continue_self_test(dev));
+ TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
+ TPM_CHECK(tpm_nv_write_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)));
+ printf("done\n");
+ return 0;
+}
+
+static int test_enable(struct udevice *dev)
+{
+ uint8_t disable = 0, deactivated = 0;
+
+ printf("Testing enable ...\n");
+ tpm_init(dev);
+ TPM_CHECK(TlclStartupIfNeeded(dev));
+ TPM_CHECK(tpm_self_test_full(dev));
+ TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
+ TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL));
+ printf("\tdisable is %d, deactivated is %d\n", disable, deactivated);
+ TPM_CHECK(tpm_physical_enable(dev));
+ TPM_CHECK(tpm_physical_set_deactivated(dev, 0));
+ TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL));
+ printf("\tdisable is %d, deactivated is %d\n", disable, deactivated);
+ if (disable == 1 || deactivated == 1)
+ printf("\tfailed to enable or activate\n");
+ printf("\tdone\n");
+ return 0;
+}
+
+#define reboot() do { \
+ printf("\trebooting...\n"); \
+ reset_cpu(); \
+} while (0)
+
+static int test_fast_enable(struct udevice *dev)
+{
+ uint8_t disable = 0, deactivated = 0;
+ int i;
+
+ printf("Testing fastenable ...\n");
+ tpm_init(dev);
+ TPM_CHECK(TlclStartupIfNeeded(dev));
+ TPM_CHECK(tpm_self_test_full(dev));
+ TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
+ TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL));
+ printf("\tdisable is %d, deactivated is %d\n", disable, deactivated);
+ for (i = 0; i < 2; i++) {
+ TPM_CHECK(tpm_force_clear(dev));
+ TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL));
+ printf("\tdisable is %d, deactivated is %d\n", disable,
+ deactivated);
+ assert(disable == 1 && deactivated == 1);
+ TPM_CHECK(tpm_physical_enable(dev));
+ TPM_CHECK(tpm_physical_set_deactivated(dev, 0));
+ TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL));
+ printf("\tdisable is %d, deactivated is %d\n", disable,
+ deactivated);
+ assert(disable == 0 && deactivated == 0);
+ }
+ printf("\tdone\n");
+ return 0;
+}
+
+static int test_global_lock(struct udevice *dev)
+{
+ uint32_t zero = 0;
+ uint32_t result;
+ uint32_t x;
+
+ printf("Testing globallock ...\n");
+ tpm_init(dev);
+ TPM_CHECK(TlclStartupIfNeeded(dev));
+ TPM_CHECK(tpm_self_test_full(dev));
+ TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
+ TPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)));
+ TPM_CHECK(tpm_nv_write_value(dev, INDEX0, (uint8_t *)&zero,
+ sizeof(uint32_t)));
+ TPM_CHECK(tpm_nv_read_value(dev, INDEX1, (uint8_t *)&x, sizeof(x)));
+ TPM_CHECK(tpm_nv_write_value(dev, INDEX1, (uint8_t *)&zero,
+ sizeof(uint32_t)));
+ TPM_CHECK(tpm_set_global_lock(dev));
+ /* Verifies that write to index0 fails */
+ x = 1;
+ result = tpm_nv_write_value(dev, INDEX0, (uint8_t *)&x, sizeof(x));
+ assert(result == TPM_AREA_LOCKED);
+ TPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)));
+ assert(x == 0);
+ /* Verifies that write to index1 is still possible */
+ x = 2;
+ TPM_CHECK(tpm_nv_write_value(dev, INDEX1, (uint8_t *)&x, sizeof(x)));
+ TPM_CHECK(tpm_nv_read_value(dev, INDEX1, (uint8_t *)&x, sizeof(x)));
+ assert(x == 2);
+ /* Turns off PP */
+ tpm_tsc_physical_presence(dev, PHYS_PRESENCE);
+ /* Verifies that write to index1 fails */
+ x = 3;
+ result = tpm_nv_write_value(dev, INDEX1, (uint8_t *)&x, sizeof(x));
+ assert(result == TPM_BAD_PRESENCE);
+ TPM_CHECK(tpm_nv_read_value(dev, INDEX1, (uint8_t *)&x, sizeof(x)));
+ assert(x == 2);
+ printf("\tdone\n");
+ return 0;
+}
+
+static int test_lock(struct udevice *dev)
+{
+ printf("Testing lock ...\n");
+ tpm_init(dev);
+ tpm_startup(dev, TPM_ST_CLEAR);
+ tpm_self_test_full(dev);
+ tpm_tsc_physical_presence(dev, PRESENCE);
+ tpm_nv_write_value_lock(dev, INDEX0);
+ printf("\tLocked 0x%x\n", INDEX0);
+ printf("\tdone\n");
+ return 0;
+}
+
+static void initialise_spaces(struct udevice *dev)
+{
+ uint32_t zero = 0;
+ uint32_t perm = TPM_NV_PER_WRITE_STCLEAR | TPM_NV_PER_PPWRITE;
+
+ printf("\tInitialising spaces\n");
+ tpm1_nv_set_locked(dev); /* useful only the first time */
+ tpm1_nv_define_space(dev, INDEX0, perm, 4);
+ tpm_nv_write_value(dev, INDEX0, (uint8_t *)&zero, 4);
+ tpm1_nv_define_space(dev, INDEX1, perm, 4);
+ tpm_nv_write_value(dev, INDEX1, (uint8_t *)&zero, 4);
+ tpm1_nv_define_space(dev, INDEX2, perm, 4);
+ tpm_nv_write_value(dev, INDEX2, (uint8_t *)&zero, 4);
+ tpm1_nv_define_space(dev, INDEX3, perm, 4);
+ tpm_nv_write_value(dev, INDEX3, (uint8_t *)&zero, 4);
+ perm = TPM_NV_PER_READ_STCLEAR | TPM_NV_PER_WRITE_STCLEAR |
+ TPM_NV_PER_PPWRITE;
+ tpm1_nv_define_space(dev, INDEX_INITIALISED, perm, 1);
+}
+
+static int test_readonly(struct udevice *dev)
+{
+ uint8_t c;
+ uint32_t index_0, index_1, index_2, index_3;
+ int read0, read1, read2, read3;
+
+ printf("Testing readonly ...\n");
+ tpm_init(dev);
+ tpm_startup(dev, TPM_ST_CLEAR);
+ tpm_self_test_full(dev);
+ tpm_tsc_physical_presence(dev, PRESENCE);
+ /*
+ * Checks if initialisation has completed by trying to read-lock a
+ * space that's created at the end of initialisation
+ */
+ if (tpm_nv_read_value(dev, INDEX_INITIALISED, &c, 0) == TPM_BADINDEX) {
+ /* The initialisation did not complete */
+ initialise_spaces(dev);
+ }
+
+ /* Checks if spaces are OK or messed up */
+ read0 = tpm_nv_read_value(dev, INDEX0, (uint8_t *)&index_0,
+ sizeof(index_0));
+ read1 = tpm_nv_read_value(dev, INDEX1, (uint8_t *)&index_1,
+ sizeof(index_1));
+ read2 = tpm_nv_read_value(dev, INDEX2, (uint8_t *)&index_2,
+ sizeof(index_2));
+ read3 = tpm_nv_read_value(dev, INDEX3, (uint8_t *)&index_3,
+ sizeof(index_3));
+ if (read0 || read1 || read2 || read3) {
+ printf("Invalid contents\n");
+ return 0;
+ }
+
+ /*
+ * Writes space, and locks it. Then attempts to write again.
+ * I really wish I could use the imperative.
+ */
+ index_0 += 1;
+ if (tpm_nv_write_value(dev, INDEX0, (uint8_t *)&index_0,
+ sizeof(index_0)) !=
+ TPM_SUCCESS) {
+ pr_err("\tcould not write index 0\n");
+ }
+ tpm_nv_write_value_lock(dev, INDEX0);
+ if (tpm_nv_write_value(dev, INDEX0, (uint8_t *)&index_0,
+ sizeof(index_0)) ==
+ TPM_SUCCESS)
+ pr_err("\tindex 0 is not locked\n");
+
+ printf("\tdone\n");
+ return 0;
+}
+
+static int test_redefine_unowned(struct udevice *dev)
+{
+ uint32_t perm;
+ uint32_t result;
+ uint32_t x;
+
+ printf("Testing redefine_unowned ...");
+ tpm_init(dev);
+ TPM_CHECK(TlclStartupIfNeeded(dev));
+ TPM_CHECK(tpm_self_test_full(dev));
+ TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
+ assert(!tpm_is_owned(dev));
+
+ /* Ensures spaces exist. */
+ TPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)));
+ TPM_CHECK(tpm_nv_read_value(dev, INDEX1, (uint8_t *)&x, sizeof(x)));
+
+ /* Redefines spaces a couple of times. */
+ perm = TPM_NV_PER_PPWRITE | TPM_NV_PER_GLOBALLOCK;
+ TPM_CHECK(tpm1_nv_define_space(dev, INDEX0, perm,
+ 2 * sizeof(uint32_t)));
+ TPM_CHECK(tpm1_nv_define_space(dev, INDEX0, perm, sizeof(uint32_t)));
+ perm = TPM_NV_PER_PPWRITE;
+ TPM_CHECK(tpm1_nv_define_space(dev, INDEX1, perm,
+ 2 * sizeof(uint32_t)));
+ TPM_CHECK(tpm1_nv_define_space(dev, INDEX1, perm, sizeof(uint32_t)));
+
+ /* Sets the global lock */
+ tpm_set_global_lock(dev);
+
+ /* Verifies that index0 cannot be redefined */
+ result = tpm1_nv_define_space(dev, INDEX0, perm, sizeof(uint32_t));
+ assert(result == TPM_AREA_LOCKED);
+
+ /* Checks that index1 can */
+ TPM_CHECK(tpm1_nv_define_space(dev, INDEX1, perm,
+ 2 * sizeof(uint32_t)));
+ TPM_CHECK(tpm1_nv_define_space(dev, INDEX1, perm, sizeof(uint32_t)));
+
+ /* Turns off PP */
+ tpm_tsc_physical_presence(dev, PHYS_PRESENCE);
+
+ /* Verifies that neither index0 nor index1 can be redefined */
+ result = tpm1_nv_define_space(dev, INDEX0, perm, sizeof(uint32_t));
+ assert(result == TPM_BAD_PRESENCE);
+ result = tpm1_nv_define_space(dev, INDEX1, perm, sizeof(uint32_t));
+ assert(result == TPM_BAD_PRESENCE);
+
+ printf("done\n");
+ return 0;
+}
+
+#define PERMPPGL (TPM_NV_PER_PPWRITE | TPM_NV_PER_GLOBALLOCK)
+#define PERMPP TPM_NV_PER_PPWRITE
+
+static int test_space_perm(struct udevice *dev)
+{
+ uint32_t perm;
+
+ printf("Testing spaceperm ...");
+ tpm_init(dev);
+ TPM_CHECK(TlclStartupIfNeeded(dev));
+ TPM_CHECK(tpm_continue_self_test(dev));
+ TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
+ TPM_CHECK(tpm_get_permissions(dev, INDEX0, &perm));
+ assert((perm & PERMPPGL) == PERMPPGL);
+ TPM_CHECK(tpm_get_permissions(dev, INDEX1, &perm));
+ assert((perm & PERMPP) == PERMPP);
+ printf("done\n");
+ return 0;
+}
+
+static int test_startup(struct udevice *dev)
+{
+ uint32_t result;
+
+ printf("Testing startup ...\n");
+
+ tpm_init(dev);
+ result = tpm_startup(dev, TPM_ST_CLEAR);
+ if (result != 0 && result != TPM_INVALID_POSTINIT)
+ printf("\ttpm startup failed with 0x%x\n", result);
+ result = tpm_get_flags(dev, NULL, NULL, NULL);
+ if (result != 0)
+ printf("\ttpm getflags failed with 0x%x\n", result);
+ printf("\texecuting SelfTestFull\n");
+ tpm_self_test_full(dev);
+ result = tpm_get_flags(dev, NULL, NULL, NULL);
+ if (result != 0)
+ printf("\ttpm getflags failed with 0x%x\n", result);
+ printf("\tdone\n");
+ return 0;
+}
+
+/*
+ * Runs [op] and ensures it returns success and doesn't run longer than
+ * [time_limit] in milliseconds.
+ */
+#define TTPM_CHECK(op, time_limit) do { \
+ ulong start, time; \
+ uint32_t __result; \
+ \
+ start = get_timer(0); \
+ __result = op; \
+ if (__result != TPM_SUCCESS) { \
+ printf("\t" #op ": error 0x%x\n", __result); \
+ return -1; \
+ } \
+ time = get_timer(start); \
+ printf("\t" #op ": %lu ms\n", time); \
+ if (time > (ulong)time_limit) { \
+ printf("\t" #op " exceeded " #time_limit " ms\n"); \
+ } \
+} while (0)
+
+static int test_timing(struct udevice *dev)
+{
+ uint8_t in[20], out[20];
+ uint32_t x;
+
+ printf("Testing timing ...");
+ tpm_init(dev);
+ TTPM_CHECK(TlclStartupIfNeeded(dev), 50);
+ TTPM_CHECK(tpm_continue_self_test(dev), 100);
+ TTPM_CHECK(tpm_self_test_full(dev), 1000);
+ TTPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE), 100);
+ TTPM_CHECK(tpm_nv_write_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)),
+ 100);
+ TTPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)),
+ 100);
+ TTPM_CHECK(tpm_pcr_extend(dev, 0, in, sizeof(in), out, "test"), 200);
+ TTPM_CHECK(tpm_set_global_lock(dev), 50);
+ TTPM_CHECK(tpm_tsc_physical_presence(dev, PHYS_PRESENCE), 100);
+ printf("done\n");
+ return 0;
+}
+
+#define TPM_MAX_NV_WRITES_NOOWNER 64
+
+static int test_write_limit(struct udevice *dev)
+{
+ uint32_t result;
+ int i;
+
+ printf("Testing writelimit ...\n");
+ tpm_init(dev);
+ TPM_CHECK(TlclStartupIfNeeded(dev));
+ TPM_CHECK(tpm_self_test_full(dev));
+ TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
+ TPM_CHECK(tpm_force_clear(dev));
+ TPM_CHECK(tpm_physical_enable(dev));
+ TPM_CHECK(tpm_physical_set_deactivated(dev, 0));
+
+ for (i = 0; i < TPM_MAX_NV_WRITES_NOOWNER + 2; i++) {
+ printf("\twriting %d\n", i);
+ result = tpm_nv_write_value(dev, INDEX0, (uint8_t *)&i,
+ sizeof(i));
+ switch (result) {
+ case TPM_SUCCESS:
+ break;
+ case TPM_MAXNVWRITES:
+ assert(i >= TPM_MAX_NV_WRITES_NOOWNER);
+ break;
+ default:
+ pr_err("\tunexpected error code %d (0x%x)\n",
+ result, result);
+ }
+ }
+
+ /* Reset write count */
+ TPM_CHECK(tpm_force_clear(dev));
+ TPM_CHECK(tpm_physical_enable(dev));
+ TPM_CHECK(tpm_physical_set_deactivated(dev, 0));
+
+ /* Try writing again. */
+ TPM_CHECK(tpm_nv_write_value(dev, INDEX0, (uint8_t *)&i, sizeof(i)));
+ printf("\tdone\n");
+ return 0;
+}
+
+#define VOIDTEST(XFUNC) \
+ int do_test_##XFUNC(struct cmd_tbl *cmd_tbl, int flag, int argc, \
+ char *const argv[]) \
+ { \
+ struct udevice *dev; \
+ int ret; \
+\
+ ret = get_tpm(&dev); \
+ if (ret) \
+ return ret; \
+ return test_##XFUNC(dev); \
+ }
+
+#define VOIDENT(XNAME) \
+ U_BOOT_CMD_MKENT(XNAME, 0, 1, do_test_##XNAME, "", ""),
+
+VOIDTEST(early_extend)
+VOIDTEST(early_nvram)
+VOIDTEST(early_nvram2)
+VOIDTEST(enable)
+VOIDTEST(fast_enable)
+VOIDTEST(global_lock)
+VOIDTEST(lock)
+VOIDTEST(readonly)
+VOIDTEST(redefine_unowned)
+VOIDTEST(space_perm)
+VOIDTEST(startup)
+VOIDTEST(timing)
+VOIDTEST(write_limit)
+VOIDTEST(timer)
+
+static struct cmd_tbl cmd_cros_tpm_sub[] = {
+ VOIDENT(early_extend)
+ VOIDENT(early_nvram)
+ VOIDENT(early_nvram2)
+ VOIDENT(enable)
+ VOIDENT(fast_enable)
+ VOIDENT(global_lock)
+ VOIDENT(lock)
+ VOIDENT(readonly)
+ VOIDENT(redefine_unowned)
+ VOIDENT(space_perm)
+ VOIDENT(startup)
+ VOIDENT(timing)
+ VOIDENT(write_limit)
+ VOIDENT(timer)
+};
+
+static int do_tpmtest(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *c;
+ int i;
+
+ printf("argc = %d, argv = ", argc);
+
+ for (i = 0; i < argc; i++)
+ printf(" %s", argv[i]);
+
+ printf("\n------\n");
+
+ argc--;
+ argv++;
+ c = find_cmd_tbl(argv[0], cmd_cros_tpm_sub,
+ ARRAY_SIZE(cmd_cros_tpm_sub));
+ return c ? c->cmd(cmdtp, flag, argc, argv) : cmd_usage(cmdtp);
+}
+
+U_BOOT_CMD(tpmtest, 2, 1, do_tpmtest, "TPM tests",
+ "\n\tearly_extend\n"
+ "\tearly_nvram\n"
+ "\tearly_nvram2\n"
+ "\tenable\n"
+ "\tfast_enable\n"
+ "\tglobal_lock\n"
+ "\tlock\n"
+ "\treadonly\n"
+ "\tredefine_unowned\n"
+ "\tspace_perm\n"
+ "\tstartup\n"
+ "\ttiming\n"
+ "\twrite_limit\n");
diff --git a/cmd/trace.c b/cmd/trace.c
new file mode 100644
index 00000000000..d36008720db
--- /dev/null
+++ b/cmd/trace.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ */
+
+#include <command.h>
+#include <env.h>
+#include <mapmem.h>
+#include <trace.h>
+#include <vsprintf.h>
+#include <asm/io.h>
+
+static int get_args(int argc, char *const argv[], char **buff,
+ size_t *buff_ptr, size_t *buff_size)
+{
+ if (argc < 2)
+ return -1;
+ if (argc < 4) {
+ *buff_size = env_get_ulong("profsize", 16, 0);
+ *buff = map_sysmem(env_get_ulong("profbase", 16, 0),
+ *buff_size);
+ *buff_ptr = env_get_ulong("profoffset", 16, 0);
+ } else {
+ *buff_size = hextoul(argv[3], NULL);
+ *buff = map_sysmem(hextoul(argv[2], NULL),
+ *buff_size);
+ *buff_ptr = 0;
+ };
+ return 0;
+}
+
+static int create_func_list(int argc, char *const argv[])
+{
+ size_t buff_size, avail, buff_ptr, needed, used;
+ char *buff;
+ int err;
+
+ if (get_args(argc, argv, &buff, &buff_ptr, &buff_size))
+ return -1;
+
+ avail = buff_size - buff_ptr;
+ err = trace_list_functions(buff + buff_ptr, avail, &needed);
+ if (err)
+ printf("Error: truncated (%#zx bytes needed)\n", needed);
+ used = min(avail, (size_t)needed);
+ printf("Function trace dumped to %08lx, size %#zx\n",
+ (ulong)map_to_sysmem(buff + buff_ptr), used);
+ env_set_hex("profbase", map_to_sysmem(buff));
+ env_set_hex("profsize", buff_size);
+ env_set_hex("profoffset", buff_ptr + used);
+
+ return 0;
+}
+
+static int create_call_list(int argc, char *const argv[])
+{
+ size_t buff_size, avail, buff_ptr, needed, used;
+ char *buff;
+ int err;
+
+ if (get_args(argc, argv, &buff, &buff_ptr, &buff_size))
+ return -1;
+
+ avail = buff_size - buff_ptr;
+ err = trace_list_calls(buff + buff_ptr, avail, &needed);
+ if (err)
+ printf("Error: truncated (%#zx bytes needed)\n", needed);
+ used = min(avail, (size_t)needed);
+ printf("Call list dumped to %08lx, size %#zx\n",
+ (ulong)map_to_sysmem(buff + buff_ptr), used);
+
+ env_set_hex("profbase", map_to_sysmem(buff));
+ env_set_hex("profsize", buff_size);
+ env_set_hex("profoffset", buff_ptr + used);
+
+ return 0;
+}
+
+int do_trace(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ const char *cmd = argc < 2 ? NULL : argv[1];
+
+ if (!cmd)
+ return cmd_usage(cmdtp);
+ switch (*cmd) {
+ case 'p':
+ trace_set_enabled(0);
+ break;
+ case 'c':
+ if (create_call_list(argc, argv))
+ return cmd_usage(cmdtp);
+ break;
+ case 'r':
+ trace_set_enabled(1);
+ break;
+ case 'f':
+ if (create_func_list(argc, argv))
+ return cmd_usage(cmdtp);
+ break;
+ case 's':
+ trace_print_stats();
+ break;
+ case 'w':
+ if (trace_wipe())
+ return CMD_RET_FAILURE;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ trace, 4, 1, do_trace,
+ "trace utility commands",
+ "stats - display tracing statistics\n"
+ "trace pause - pause tracing\n"
+ "trace resume - resume tracing\n"
+ "trace wipe - wipe traces\n"
+ "trace funclist [<addr> <size>] - dump function list into buffer\n"
+ "trace calls [<addr> <size>] "
+ "- dump function call trace into buffer"
+);
diff --git a/cmd/ubi.c b/cmd/ubi.c
new file mode 100644
index 00000000000..93de6f3aea2
--- /dev/null
+++ b/cmd/ubi.c
@@ -0,0 +1,921 @@
+/*
+ * Unsorted Block Image commands
+ *
+ * Copyright (C) 2008 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * Copyright 2008-2009 Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <command.h>
+#include <env.h>
+#include <exports.h>
+#include <led.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <mtd.h>
+#include <nand.h>
+#include <onenand_uboot.h>
+#include <dm/devres.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/err.h>
+#include <ubi_uboot.h>
+#include <linux/errno.h>
+#include <jffs2/load_kernel.h>
+#include <linux/log2.h>
+
+#undef ubi_msg
+#define ubi_msg(fmt, ...) printf("UBI: " fmt "\n", ##__VA_ARGS__)
+
+/* Private own data */
+static struct ubi_device *ubi;
+
+#ifdef CONFIG_CMD_UBIFS
+#include <ubifs_uboot.h>
+#endif
+
+static void display_volume_info(struct ubi_device *ubi)
+{
+ int i;
+
+ for (i = 0; i < (ubi->vtbl_slots + 1); i++) {
+ if (!ubi->volumes[i])
+ continue; /* Empty record */
+ ubi_dump_vol_info(ubi->volumes[i]);
+ }
+}
+
+static void display_ubi_info(struct ubi_device *ubi)
+{
+ ubi_msg("MTD device name: \"%s\"", ubi->mtd->name);
+ ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20);
+ ubi_msg("physical eraseblock size: %d bytes (%d KiB)",
+ ubi->peb_size, ubi->peb_size >> 10);
+ ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size);
+ ubi_msg("number of good PEBs: %d", ubi->good_peb_count);
+ ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count);
+ ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size);
+ ubi_msg("VID header offset: %d (aligned %d)",
+ ubi->vid_hdr_offset, ubi->vid_hdr_aloffset);
+ ubi_msg("data offset: %d", ubi->leb_start);
+ ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots);
+ ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD);
+ ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT);
+ ubi_msg("number of user volumes: %d",
+ ubi->vol_count - UBI_INT_VOL_COUNT);
+ ubi_msg("available PEBs: %d", ubi->avail_pebs);
+ ubi_msg("total number of reserved PEBs: %d", ubi->rsvd_pebs);
+ ubi_msg("number of PEBs reserved for bad PEB handling: %d",
+ ubi->beb_rsvd_pebs);
+ ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec);
+}
+
+static int ubi_info(int layout)
+{
+ if (layout)
+ display_volume_info(ubi);
+ else
+ display_ubi_info(ubi);
+
+ return 0;
+}
+
+static int ubi_list(const char *var, int numeric)
+{
+ size_t namelen, len, size;
+ char *str, *str2;
+ int i;
+
+ if (!var) {
+ for (i = 0; i < (ubi->vtbl_slots + 1); i++) {
+ if (!ubi->volumes[i])
+ continue;
+ if (ubi->volumes[i]->vol_id >= UBI_INTERNAL_VOL_START)
+ continue;
+ printf("%d: %s\n",
+ ubi->volumes[i]->vol_id,
+ ubi->volumes[i]->name);
+ }
+ return 0;
+ }
+
+ len = 0;
+ size = 16;
+ str = malloc(size);
+ if (!str)
+ return 1;
+
+ for (i = 0; i < (ubi->vtbl_slots + 1); i++) {
+ if (!ubi->volumes[i])
+ continue;
+ if (ubi->volumes[i]->vol_id >= UBI_INTERNAL_VOL_START)
+ continue;
+
+ if (numeric)
+ namelen = 10; /* strlen(stringify(INT_MAX)) */
+ else
+ namelen = strlen(ubi->volumes[i]->name);
+
+ if (len + namelen + 1 > size) {
+ size = roundup_pow_of_two(len + namelen + 1) * 2;
+ str2 = realloc(str, size);
+ if (!str2) {
+ free(str);
+ return 1;
+ }
+ str = str2;
+ }
+
+ if (len)
+ str[len++] = ' ';
+
+ if (numeric) {
+ len += sprintf(str + len, "%d", ubi->volumes[i]->vol_id) + 1;
+ } else {
+ memcpy(str + len, ubi->volumes[i]->name, namelen);
+ len += namelen;
+ str[len] = 0;
+ }
+ }
+
+ env_set(var, str);
+ free(str);
+
+ return 0;
+}
+
+static int ubi_check_volumename(const struct ubi_volume *vol, char *name)
+{
+ return strcmp(vol->name, name);
+}
+
+static int ubi_check(char *name)
+{
+ int i;
+
+ for (i = 0; i < (ubi->vtbl_slots + 1); i++) {
+ if (!ubi->volumes[i])
+ continue; /* Empty record */
+
+ if (!ubi_check_volumename(ubi->volumes[i], name))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int verify_mkvol_req(const struct ubi_device *ubi,
+ const struct ubi_mkvol_req *req)
+{
+ int n, err = EINVAL;
+
+ if (req->bytes < 0 || req->alignment < 0 || req->vol_type < 0 ||
+ req->name_len < 0)
+ goto bad;
+
+ if ((req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) &&
+ req->vol_id != UBI_VOL_NUM_AUTO)
+ goto bad;
+
+ if (req->alignment == 0)
+ goto bad;
+
+ if (req->bytes == 0) {
+ printf("No space left in UBI device!\n");
+ err = ENOMEM;
+ goto bad;
+ }
+
+ if (req->vol_type != UBI_DYNAMIC_VOLUME &&
+ req->vol_type != UBI_STATIC_VOLUME)
+ goto bad;
+
+ if (req->alignment > ubi->leb_size)
+ goto bad;
+
+ n = req->alignment % ubi->min_io_size;
+ if (req->alignment != 1 && n)
+ goto bad;
+
+ if (req->name_len > UBI_VOL_NAME_MAX) {
+ printf("Name too long!\n");
+ err = ENAMETOOLONG;
+ goto bad;
+ }
+
+ return 0;
+bad:
+ return err;
+}
+
+static int ubi_create_vol(char *volume, int64_t size, int dynamic, int vol_id,
+ bool skipcheck)
+{
+ struct ubi_mkvol_req req;
+ int err;
+
+ if (dynamic)
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ else
+ req.vol_type = UBI_STATIC_VOLUME;
+
+ req.vol_id = vol_id;
+ req.alignment = 1;
+ req.bytes = size;
+
+ strcpy(req.name, volume);
+ req.name_len = strlen(volume);
+ req.name[req.name_len] = '\0';
+ req.flags = 0;
+ if (skipcheck)
+ req.flags |= UBI_VOL_SKIP_CRC_CHECK_FLG;
+
+ /* It's duplicated at drivers/mtd/ubi/cdev.c */
+ err = verify_mkvol_req(ubi, &req);
+ if (err) {
+ printf("verify_mkvol_req failed %d\n", err);
+ return err;
+ }
+ printf("Creating %s volume %s of size %lld\n",
+ dynamic ? "dynamic" : "static", volume, size);
+ /* Call real ubi create volume */
+ return ubi_create_volume(ubi, &req);
+}
+
+static struct ubi_volume *ubi_find_volume(char *volume)
+{
+ struct ubi_volume *vol;
+ int i;
+
+ for (i = 0; i < ubi->vtbl_slots; i++) {
+ vol = ubi->volumes[i];
+ if (vol && !strcmp(vol->name, volume))
+ return vol;
+ }
+
+ printf("Volume %s not found!\n", volume);
+ return NULL;
+}
+
+static int ubi_remove_vol(char *volume)
+{
+ int err, reserved_pebs, i;
+ struct ubi_volume *vol;
+
+ vol = ubi_find_volume(volume);
+ if (vol == NULL)
+ return ENODEV;
+
+ printf("Remove UBI volume %s (id %d)\n", vol->name, vol->vol_id);
+
+ if (ubi->ro_mode) {
+ printf("It's read-only mode\n");
+ err = EROFS;
+ goto out_err;
+ }
+
+ err = ubi_change_vtbl_record(ubi, vol->vol_id, NULL);
+ if (err) {
+ printf("Error changing Vol tabel record err=%x\n", err);
+ goto out_err;
+ }
+ reserved_pebs = vol->reserved_pebs;
+ for (i = 0; i < vol->reserved_pebs; i++) {
+ err = ubi_eba_unmap_leb(ubi, vol, i);
+ if (err)
+ goto out_err;
+ }
+
+ kfree(vol->eba_tbl);
+ ubi->volumes[vol->vol_id]->eba_tbl = NULL;
+ ubi->volumes[vol->vol_id] = NULL;
+
+ ubi->rsvd_pebs -= reserved_pebs;
+ ubi->avail_pebs += reserved_pebs;
+ i = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs;
+ if (i > 0) {
+ i = ubi->avail_pebs >= i ? i : ubi->avail_pebs;
+ ubi->avail_pebs -= i;
+ ubi->rsvd_pebs += i;
+ ubi->beb_rsvd_pebs += i;
+ if (i > 0)
+ ubi_msg("reserve more %d PEBs", i);
+ }
+ ubi->vol_count -= 1;
+
+ return 0;
+out_err:
+ ubi_err(ubi, "cannot remove volume %s, error %d", volume, err);
+ if (err < 0)
+ err = -err;
+ return err;
+}
+
+static int ubi_rename_vol(char *oldname, char *newname)
+{
+ struct ubi_volume *vol;
+ struct ubi_rename_entry rename;
+ struct ubi_volume_desc desc;
+ struct list_head list;
+
+ vol = ubi_find_volume(oldname);
+ if (!vol) {
+ printf("%s: volume %s doesn't exist\n", __func__, oldname);
+ return ENODEV;
+ }
+
+ if (!ubi_check(newname)) {
+ printf("%s: volume %s already exist\n", __func__, newname);
+ return EINVAL;
+ }
+
+ printf("Rename UBI volume %s to %s\n", oldname, newname);
+
+ if (ubi->ro_mode) {
+ printf("%s: ubi device is in read-only mode\n", __func__);
+ return EROFS;
+ }
+
+ rename.new_name_len = strlen(newname);
+ strcpy(rename.new_name, newname);
+ rename.remove = 0;
+ desc.vol = vol;
+ desc.mode = 0;
+ rename.desc = &desc;
+ INIT_LIST_HEAD(&rename.list);
+ INIT_LIST_HEAD(&list);
+ list_add(&rename.list, &list);
+
+ return ubi_rename_volumes(ubi, &list);
+}
+
+static int ubi_volume_continue_write(char *volume, void *buf, size_t size)
+{
+ int err;
+ struct ubi_volume *vol;
+
+ vol = ubi_find_volume(volume);
+ if (vol == NULL)
+ return ENODEV;
+
+ if (!vol->updating) {
+ printf("UBI volume update was not initiated\n");
+ return EINVAL;
+ }
+
+ err = ubi_more_update_data(ubi, vol, buf, size);
+ if (err < 0) {
+ printf("Couldnt or partially wrote data\n");
+ return -err;
+ }
+
+ if (err) {
+ size = err;
+
+ err = ubi_check_volume(ubi, vol->vol_id);
+ if (err < 0)
+ return -err;
+
+ if (err) {
+ ubi_warn(ubi, "volume %d on UBI device %d is corrupt",
+ vol->vol_id, ubi->ubi_num);
+ vol->corrupted = 1;
+ }
+
+ vol->checked = 1;
+ ubi_gluebi_updated(vol);
+ }
+
+ return 0;
+}
+
+int ubi_volume_begin_write(char *volume, void *buf, size_t size,
+ size_t full_size)
+{
+ int err;
+ int rsvd_bytes;
+ struct ubi_volume *vol;
+
+ vol = ubi_find_volume(volume);
+ if (vol == NULL)
+ return ENODEV;
+
+ rsvd_bytes = vol->reserved_pebs * (ubi->leb_size - vol->data_pad);
+ if (size > rsvd_bytes) {
+ printf("size > volume size! Aborting!\n");
+ return EINVAL;
+ }
+
+ err = ubi_start_update(ubi, vol, full_size);
+ if (err < 0) {
+ printf("Cannot start volume update\n");
+ return -err;
+ }
+
+ /* The volume is just wiped out */
+ if (!full_size)
+ return 0;
+
+ return ubi_volume_continue_write(volume, buf, size);
+}
+
+static int ubi_volume_offset_write(char *volume, void *buf, loff_t offset,
+ size_t size)
+{
+ int len, tbuf_size, ret;
+ u64 lnum;
+ struct ubi_volume *vol;
+ loff_t off = offset;
+ void *tbuf;
+
+ vol = ubi_find_volume(volume);
+ if (!vol)
+ return -ENODEV;
+
+ if (size > vol->reserved_pebs * (ubi->leb_size - vol->data_pad))
+ return -EINVAL;
+
+ tbuf_size = vol->usable_leb_size;
+ tbuf = malloc_cache_aligned(tbuf_size);
+ if (!tbuf)
+ return -ENOMEM;
+
+ lnum = off;
+ off = do_div(lnum, vol->usable_leb_size);
+
+ do {
+ struct ubi_volume_desc desc = {
+ .vol = vol,
+ .mode = UBI_READWRITE,
+ };
+
+ len = size > tbuf_size ? tbuf_size : size;
+ if (off + len >= vol->usable_leb_size)
+ len = vol->usable_leb_size - off;
+
+ ret = ubi_read(&desc, (int)lnum, tbuf, 0, tbuf_size);
+ if (ret) {
+ pr_err("Failed to read leb %lld (%d)\n", lnum, ret);
+ goto exit;
+ }
+
+ memcpy(tbuf + off, buf, len);
+
+ ret = ubi_leb_change(&desc, (int)lnum, tbuf, tbuf_size);
+ if (ret) {
+ pr_err("Failed to write leb %lld (%d)\n", lnum, ret);
+ goto exit;
+ }
+
+ off += len;
+ if (off >= vol->usable_leb_size) {
+ lnum++;
+ off -= vol->usable_leb_size;
+ }
+
+ buf += len;
+ size -= len;
+ } while (size);
+
+exit:
+ free(tbuf);
+ return ret;
+}
+
+int ubi_volume_write(char *volume, void *buf, loff_t offset, size_t size)
+{
+ int ret;
+
+ led_activity_blink();
+
+ if (!offset)
+ ret = ubi_volume_begin_write(volume, buf, size, size);
+ else
+ ret = ubi_volume_offset_write(volume, buf, offset, size);
+
+ led_activity_off();
+
+ return ret;
+}
+
+int ubi_volume_read(char *volume, char *buf, loff_t offset, size_t size)
+{
+ int err, lnum, off, len, tbuf_size;
+ void *tbuf;
+ unsigned long long tmp;
+ struct ubi_volume *vol;
+ loff_t offp = offset;
+ size_t len_read;
+
+ vol = ubi_find_volume(volume);
+ if (vol == NULL)
+ return ENODEV;
+
+ if (vol->updating) {
+ printf("updating");
+ return EBUSY;
+ }
+ if (vol->upd_marker) {
+ printf("damaged volume, update marker is set");
+ return EBADF;
+ }
+ if (offp == vol->used_bytes)
+ return 0;
+
+ if (size == 0) {
+ printf("No size specified -> Using max size (%lld)\n", vol->used_bytes);
+ size = vol->used_bytes;
+ }
+
+ printf("Read %zu bytes from volume %s to %p\n", size, volume, buf);
+
+ if (vol->corrupted)
+ printf("read from corrupted volume %d", vol->vol_id);
+ if (offp + size > vol->used_bytes)
+ size = vol->used_bytes - offp;
+
+ tbuf_size = vol->usable_leb_size;
+ if (size < tbuf_size)
+ tbuf_size = ALIGN(size, ubi->min_io_size);
+ tbuf = malloc_cache_aligned(tbuf_size);
+ if (!tbuf) {
+ printf("NO MEM\n");
+ return ENOMEM;
+ }
+ len = size > tbuf_size ? tbuf_size : size;
+
+ led_activity_blink();
+ tmp = offp;
+ off = do_div(tmp, vol->usable_leb_size);
+ lnum = tmp;
+ len_read = size;
+ do {
+ if (off + len >= vol->usable_leb_size)
+ len = vol->usable_leb_size - off;
+
+ err = ubi_eba_read_leb(ubi, vol, lnum, tbuf, off, len, 0);
+ if (err) {
+ printf("read err %x\n", err);
+ err = -err;
+ break;
+ }
+ off += len;
+ if (off == vol->usable_leb_size) {
+ lnum += 1;
+ off -= vol->usable_leb_size;
+ }
+
+ size -= len;
+ offp += len;
+
+ memcpy(buf, tbuf, len);
+
+ buf += len;
+ len = size > tbuf_size ? tbuf_size : size;
+ } while (size);
+
+ if (!size)
+ env_set_hex("filesize", len_read);
+
+ free(tbuf);
+ led_activity_off();
+ return err;
+}
+
+static int ubi_dev_scan(struct mtd_info *info, const char *vid_header_offset)
+{
+ char ubi_mtd_param_buffer[80];
+ int err;
+
+ if (!vid_header_offset)
+ sprintf(ubi_mtd_param_buffer, "%s", info->name);
+ else
+ sprintf(ubi_mtd_param_buffer, "%s,%s", info->name,
+ vid_header_offset);
+
+ err = ubi_mtd_param_parse(ubi_mtd_param_buffer, NULL);
+ if (err)
+ return -err;
+
+ led_activity_blink();
+ err = ubi_init();
+ led_activity_off();
+ if (err)
+ return -err;
+
+ return 0;
+}
+
+static int ubi_set_skip_check(char *volume, bool skip_check)
+{
+ struct ubi_vtbl_record vtbl_rec;
+ struct ubi_volume *vol;
+
+ vol = ubi_find_volume(volume);
+ if (!vol)
+ return ENODEV;
+
+ printf("%sing skip_check on volume %s\n",
+ skip_check ? "Sett" : "Clear", volume);
+
+ vtbl_rec = ubi->vtbl[vol->vol_id];
+ if (skip_check) {
+ vtbl_rec.flags |= UBI_VTBL_SKIP_CRC_CHECK_FLG;
+ vol->skip_check = 1;
+ } else {
+ vtbl_rec.flags &= ~UBI_VTBL_SKIP_CRC_CHECK_FLG;
+ vol->skip_check = 0;
+ }
+
+ return ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec);
+}
+
+static int ubi_detach(void)
+{
+#ifdef CONFIG_CMD_UBIFS
+ /*
+ * Automatically unmount UBIFS partition when user
+ * changes the UBI device. Otherwise the following
+ * UBIFS commands will crash.
+ */
+ if (ubifs_is_mounted())
+ cmd_ubifs_umount();
+#endif
+
+ /*
+ * Call ubi_exit() before re-initializing the UBI subsystem
+ */
+ if (ubi)
+ ubi_exit();
+
+ ubi = NULL;
+
+ return 0;
+}
+
+int ubi_part(char *part_name, const char *vid_header_offset)
+{
+ struct mtd_info *mtd;
+ int err;
+
+ if (ubi && ubi->mtd && !strcmp(ubi->mtd->name, part_name)) {
+ printf("UBI partition '%s' already selected\n", part_name);
+ return 0;
+ }
+
+ ubi_detach();
+
+ mtd_probe_devices();
+ mtd = get_mtd_device_nm(part_name);
+ if (IS_ERR(mtd)) {
+ printf("Partition %s not found!\n", part_name);
+ return 1;
+ }
+ put_mtd_device(mtd);
+
+ err = ubi_dev_scan(mtd, vid_header_offset);
+ if (err) {
+ printf("UBI init error %d\n", err);
+ printf("Please check, if the correct MTD partition is used (size big enough?)\n");
+ return err;
+ }
+
+ ubi = ubi_devices[0];
+
+ return 0;
+}
+
+static int do_ubi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int64_t size;
+ ulong addr = 0;
+ bool skipcheck = false;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (strcmp(argv[1], "detach") == 0)
+ return ubi_detach();
+
+ if (strcmp(argv[1], "part") == 0) {
+ const char *vid_header_offset = NULL;
+
+ /* Print current partition */
+ if (argc == 2) {
+ if (!ubi) {
+ printf("Error, no UBI device selected!\n");
+ return 1;
+ }
+
+ printf("Device %d: %s, MTD partition %s\n",
+ ubi->ubi_num, ubi->ubi_name, ubi->mtd->name);
+ return 0;
+ }
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ if (argc > 3)
+ vid_header_offset = argv[3];
+
+ return ubi_part(argv[2], vid_header_offset);
+ }
+
+ if ((strcmp(argv[1], "part") != 0) && !ubi) {
+ printf("Error, no UBI device selected!\n");
+ return 1;
+ }
+
+ if (strcmp(argv[1], "info") == 0) {
+ int layout = 0;
+ if (argc > 2 && !strncmp(argv[2], "l", 1))
+ layout = 1;
+ return ubi_info(layout);
+ }
+
+ if (strcmp(argv[1], "list") == 0) {
+ int numeric = 0;
+ if (argc >= 3 && argv[2][0] == '-') {
+ if (strcmp(argv[2], "-numeric") == 0)
+ numeric = 1;
+ else
+ return CMD_RET_USAGE;
+ }
+ if (!numeric && argc != 2 && argc != 3)
+ return CMD_RET_USAGE;
+ if (numeric && argc != 3 && argc != 4)
+ return CMD_RET_USAGE;
+ return ubi_list(argv[numeric ? 3 : 2], numeric);
+ }
+
+ if (strcmp(argv[1], "check") == 0) {
+ if (argc > 2)
+ return ubi_check(argv[2]);
+
+ printf("Error, no volume name passed\n");
+ return 1;
+ }
+
+ if (strncmp(argv[1], "create", 6) == 0) {
+ int dynamic = 1; /* default: dynamic volume */
+ int id = UBI_VOL_NUM_AUTO;
+
+ /* Use maximum available size */
+ size = 0;
+
+ /* E.g., create volume with "skipcheck" bit set */
+ if (argc == 7) {
+ skipcheck = strncmp(argv[6], "--skipcheck", 11) == 0;
+ argc--;
+ }
+
+ /* E.g., create volume size type vol_id */
+ if (argc == 6) {
+ id = simple_strtoull(argv[5], NULL, 16);
+ argc--;
+ }
+
+ /* E.g., create volume size type */
+ if (argc == 5) {
+ if (strncmp(argv[4], "s", 1) == 0)
+ dynamic = 0;
+ else if (strncmp(argv[4], "d", 1) != 0) {
+ printf("Incorrect type\n");
+ return 1;
+ }
+ argc--;
+ }
+ /* E.g., create volume size */
+ if (argc == 4) {
+ if (argv[3][0] != '-')
+ size = simple_strtoull(argv[3], NULL, 16);
+ argc--;
+ }
+ /* Use maximum available size */
+ if (!size) {
+ size = (int64_t)ubi->avail_pebs * ubi->leb_size;
+ printf("No size specified -> Using max size (%lld)\n", size);
+ }
+ /* E.g., create volume */
+ if (argc == 3) {
+ return ubi_create_vol(argv[2], size, dynamic, id,
+ skipcheck);
+ }
+ }
+
+ if (strncmp(argv[1], "remove", 6) == 0) {
+ /* E.g., remove volume */
+ if (argc == 3)
+ return ubi_remove_vol(argv[2]);
+ }
+
+ if (IS_ENABLED(CONFIG_CMD_UBI_RENAME) && !strncmp(argv[1], "rename", 6))
+ return ubi_rename_vol(argv[2], argv[3]);
+
+ if (strncmp(argv[1], "skipcheck", 9) == 0) {
+ /* E.g., change skip_check flag */
+ if (argc == 4) {
+ skipcheck = strncmp(argv[3], "on", 2) == 0;
+ return ubi_set_skip_check(argv[2], skipcheck);
+ }
+ }
+
+ if (strncmp(argv[1], "write", 5) == 0) {
+ int ret;
+
+ if (argc < 5) {
+ printf("Please see usage\n");
+ return 1;
+ }
+
+ addr = hextoul(argv[2], NULL);
+ size = hextoul(argv[4], NULL);
+
+ if (strlen(argv[1]) == 10 &&
+ strncmp(argv[1] + 5, ".part", 5) == 0) {
+ if (argc < 6) {
+ ret = ubi_volume_continue_write(argv[3],
+ (void *)addr, size);
+ } else {
+ size_t full_size;
+ full_size = hextoul(argv[5], NULL);
+ ret = ubi_volume_begin_write(argv[3],
+ (void *)addr, size, full_size);
+ }
+ } else {
+ ret = ubi_volume_write(argv[3], (void *)addr, 0, size);
+ }
+ if (!ret) {
+ printf("%lld bytes written to volume %s\n", size,
+ argv[3]);
+ }
+
+ return ret;
+ }
+
+ if (strncmp(argv[1], "read", 4) == 0) {
+ size = 0;
+
+ /* E.g., read volume size */
+ if (argc == 5) {
+ size = hextoul(argv[4], NULL);
+ argc--;
+ }
+
+ /* E.g., read volume */
+ if (argc == 4) {
+ addr = hextoul(argv[2], NULL);
+ argc--;
+ }
+
+ if (argc == 3) {
+ return ubi_volume_read(argv[3], (char *)addr, 0, size);
+ }
+ }
+
+ printf("Please see usage\n");
+ return 1;
+}
+
+U_BOOT_CMD(
+ ubi, 7, 1, do_ubi,
+ "ubi commands",
+ "detach"
+ " - detach ubi from a mtd partition\n"
+ "ubi part [part] [offset]\n"
+ " - Show or set current partition (with optional VID"
+ " header offset)\n"
+ "ubi info [l[ayout]]"
+ " - Display volume and ubi layout information\n"
+ "ubi list [flags]"
+ " - print the list of volumes\n"
+ "ubi list [flags] <varname>"
+ " - set environment variable to the list of volumes"
+ " (flags can be -numeric)\n"
+ "ubi check volumename"
+ " - check if volumename exists\n"
+ "ubi create[vol] volume [size] [type] [id] [--skipcheck]\n"
+ " - create volume name with size ('-' for maximum"
+ " available size)\n"
+ "ubi write[vol] address volume size"
+ " - Write volume from address with size\n"
+ "ubi write.part address volume size [fullsize]\n"
+ " - Write part of a volume from address\n"
+ "ubi read[vol] address volume [size]"
+ " - Read volume to address with size\n"
+ "ubi remove[vol] volume"
+ " - Remove volume\n"
+#if IS_ENABLED(CONFIG_CMD_UBI_RENAME)
+ "ubi rename oldname newname\n"
+#endif
+ "ubi skipcheck volume on/off - Set or clear skip_check flag in volume header\n"
+ "[Legends]\n"
+ " volume: character name\n"
+ " size: specified in bytes\n"
+ " type: s[tatic] or d[ynamic] (default=dynamic)"
+);
diff --git a/cmd/ubifs.c b/cmd/ubifs.c
new file mode 100644
index 00000000000..22e95db8ca5
--- /dev/null
+++ b/cmd/ubifs.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2008
+ * Stefan Roese, DENX Software Engineering, sr@denx.de.
+ */
+
+/*
+ * UBIFS command support
+ */
+
+#undef DEBUG
+
+#include <config.h>
+#include <command.h>
+#include <log.h>
+#include <ubifs_uboot.h>
+#include <vsprintf.h>
+
+static int ubifs_initialized;
+static int ubifs_mounted;
+
+int cmd_ubifs_mount(char *vol_name)
+{
+ int ret;
+
+ debug("Using volume %s\n", vol_name);
+
+ if (ubifs_initialized == 0) {
+ ubifs_init();
+ ubifs_initialized = 1;
+ }
+
+ ret = uboot_ubifs_mount(vol_name);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ ubifs_mounted = 1;
+
+ return ret;
+}
+
+static int do_ubifs_mount(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *vol_name;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ vol_name = argv[1];
+
+ return cmd_ubifs_mount(vol_name);
+}
+
+int ubifs_is_mounted(void)
+{
+ return ubifs_mounted;
+}
+
+int cmd_ubifs_umount(void)
+{
+ if (ubifs_initialized == 0) {
+ printf("No UBIFS volume mounted!\n");
+ return CMD_RET_FAILURE;
+ }
+
+ uboot_ubifs_umount();
+ ubifs_mounted = 0;
+ ubifs_initialized = 0;
+
+ return 0;
+}
+
+static int do_ubifs_umount(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (argc != 1)
+ return CMD_RET_USAGE;
+
+ return cmd_ubifs_umount();
+}
+
+static int do_ubifs_ls(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *filename = "/";
+ int ret;
+
+ if (!ubifs_mounted) {
+ printf("UBIFS not mounted, use ubifsmount to mount volume first!\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc == 2)
+ filename = argv[1];
+ debug("Using filename %s\n", filename);
+
+ ret = ubifs_ls(filename);
+ if (ret) {
+ printf("** File not found %s **\n", filename);
+ ret = CMD_RET_FAILURE;
+ }
+
+ return ret;
+}
+
+static int do_ubifs_load(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *filename;
+ char *endp;
+ int ret;
+ unsigned long addr;
+ u32 size = 0;
+
+ if (!ubifs_mounted) {
+ printf("UBIFS not mounted, use ubifs mount to mount volume first!\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ addr = hextoul(argv[1], &endp);
+ if (endp == argv[1])
+ return CMD_RET_USAGE;
+
+ filename = argv[2];
+
+ if (argc == 4) {
+ size = hextoul(argv[3], &endp);
+ if (endp == argv[3])
+ return CMD_RET_USAGE;
+ }
+ debug("Loading file '%s' to address 0x%08lx (size %d)\n", filename, addr, size);
+
+ ret = ubifs_load(filename, addr, size);
+ if (ret) {
+ printf("** File not found %s **\n", filename);
+ ret = CMD_RET_FAILURE;
+ }
+
+ return ret;
+}
+
+U_BOOT_CMD(
+ ubifsmount, 2, 0, do_ubifs_mount,
+ "mount UBIFS volume",
+ "<volume-name>\n"
+ " - mount 'volume-name' volume"
+);
+
+U_BOOT_CMD(
+ ubifsumount, 1, 0, do_ubifs_umount,
+ "unmount UBIFS volume",
+ " - unmount current volume"
+);
+
+U_BOOT_CMD(
+ ubifsls, 2, 0, do_ubifs_ls,
+ "list files in a directory",
+ "[directory]\n"
+ " - list files in a 'directory' (default '/')"
+);
+
+U_BOOT_CMD(
+ ubifsload, 4, 0, do_ubifs_load,
+ "load file from an UBIFS filesystem",
+ "<addr> <filename> [bytes]\n"
+ " - load file 'filename' to address 'addr'"
+);
diff --git a/cmd/ufetch.c b/cmd/ufetch.c
new file mode 100644
index 00000000000..bc5db08eee1
--- /dev/null
+++ b/cmd/ufetch.c
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Small "fetch" utility for U-Boot */
+
+#ifdef CONFIG_ARM64
+#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[34m"
+#define YELLOW "\033[33m"
+#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,
+ 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)
+};
+
+/*
+ * TODO/ideas:
+ * - Refactor to not use a for loop
+ * - Handle multiple network interfaces
+ * - Include stats about number of bound/probed devices
+ * - Show U-Boot's size and malloc usage, fdt size, etc.
+ */
+
+
+static int do_ufetch(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int num_lines = max((size_t)LAST_LINE + 1, ARRAY_SIZE(logo_lines));
+ const char *model, *compatible;
+ char *ipaddr;
+ int n_cmds, n_cpus = 0, compatlen;
+ size_t size = 0;
+ ofnode np;
+ 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("ipaddr");
+ 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", env_get("stdout"));
+ if (gd->baudrate)
+ printf(" (%d baud)", gd->baudrate);
+ putc('\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_ARM64
+ 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 CONFIG_SYS_ARCH " (%d cores, 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: {
+#ifdef CONFIG_BLK
+ struct udevice *dev;
+ struct blk_desc *desc;
+ int ret;
+
+ 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("%4s %d: " RESET, blk_get_uclass_name(desc->uclass_id),
+ desc->lun);
+ if (size)
+ print_size(size, "");
+ else
+ printf("No media");
+ } else if (ret == -ENODEV && (skip_ascii || line > ARRAY_SIZE(logo_lines))) {
+ break;
+ }
+#endif
+ 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"
+);
diff --git a/cmd/ufs.c b/cmd/ufs.c
new file mode 100644
index 00000000000..790dab50f18
--- /dev/null
+++ b/cmd/ufs.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0+
+/**
+ * ufs.c - UFS specific U-Boot commands
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com
+ *
+ */
+#include <command.h>
+#include <ufs.h>
+#include <vsprintf.h>
+#include <linux/string.h>
+
+static int do_ufs(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int dev, ret;
+
+ if (argc >= 2) {
+ if (!strcmp(argv[1], "init")) {
+ if (argc == 3) {
+ dev = dectoul(argv[2], NULL);
+ ret = ufs_probe_dev(dev);
+ if (ret)
+ return CMD_RET_FAILURE;
+ } else {
+ ufs_probe();
+ }
+
+ return CMD_RET_SUCCESS;
+ }
+ }
+
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(ufs, 3, 1, do_ufs,
+ "UFS sub-system",
+ "init [dev] - init UFS subsystem\n"
+);
diff --git a/cmd/unlz4.c b/cmd/unlz4.c
new file mode 100644
index 00000000000..2eadc753e6c
--- /dev/null
+++ b/cmd/unlz4.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020
+ * FUJITSU COMPUTERTECHNOLOGIES LIMITED. All rights reserved.
+ */
+
+#include <command.h>
+#include <env.h>
+#include <mapmem.h>
+#include <vsprintf.h>
+#include <u-boot/lz4.h>
+
+static int do_unlz4(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ unsigned long src, dst;
+ size_t src_len = ~0UL, dst_len = ~0UL;
+ int ret;
+
+ switch (argc) {
+ case 4:
+ src = hextoul(argv[1], NULL);
+ dst = hextoul(argv[2], NULL);
+ dst_len = hextoul(argv[3], NULL);
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ ret = ulz4fn(map_sysmem(src, 0), src_len, map_sysmem(dst, dst_len),
+ &dst_len);
+ if (ret) {
+ printf("Uncompressed err :%d\n", ret);
+ return 1;
+ }
+
+ printf("Uncompressed size: %zd = 0x%zX\n", dst_len, dst_len);
+ env_set_hex("filesize", dst_len);
+
+ return 0;
+}
+
+U_BOOT_CMD(unlz4, 4, 1, do_unlz4,
+ "lz4 uncompress a memory region",
+ "srcaddr dstaddr dstsize\n"
+ "NOTE: Specify the destination size that is sufficiently larger\n"
+ " than the source size.\n"
+);
diff --git a/cmd/unzip.c b/cmd/unzip.c
new file mode 100644
index 00000000000..e7a3f9808b2
--- /dev/null
+++ b/cmd/unzip.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <env.h>
+#include <gzip.h>
+#include <mapmem.h>
+#include <part.h>
+#include <vsprintf.h>
+
+static int do_unzip(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ unsigned long src, dst;
+ unsigned long src_len = ~0UL, dst_len = ~0UL;
+
+ switch (argc) {
+ case 4:
+ dst_len = hextoul(argv[3], NULL);
+ /* fall through */
+ case 3:
+ src = hextoul(argv[1], NULL);
+ dst = hextoul(argv[2], NULL);
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ if (gunzip(map_sysmem(dst, dst_len), dst_len, map_sysmem(src, 0),
+ &src_len) != 0)
+ return 1;
+
+ printf("Uncompressed size: %lu = 0x%lX\n", src_len, src_len);
+ env_set_hex("filesize", src_len);
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ unzip, 4, 1, do_unzip,
+ "unzip a memory region",
+ "srcaddr dstaddr [dstsize]"
+);
+
+static int do_gzwrite(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ struct blk_desc *bdev;
+ int ret;
+ unsigned char *addr;
+ unsigned long length;
+ unsigned long writebuf = 1<<20;
+ u64 startoffs = 0;
+ u64 szexpected = 0;
+
+ if (argc < 5)
+ return CMD_RET_USAGE;
+ ret = blk_get_device_by_str(argv[1], argv[2], &bdev);
+ if (ret < 0)
+ return CMD_RET_FAILURE;
+
+ addr = (unsigned char *)hextoul(argv[3], NULL);
+ length = hextoul(argv[4], NULL);
+
+ if (5 < argc) {
+ writebuf = hextoul(argv[5], NULL);
+ if (6 < argc) {
+ startoffs = simple_strtoull(argv[6], NULL, 16);
+ if (7 < argc)
+ szexpected = simple_strtoull(argv[7],
+ NULL, 16);
+ }
+ }
+
+ ret = gzwrite(addr, length, bdev, writebuf, startoffs, szexpected);
+
+ return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+ gzwrite, 8, 0, do_gzwrite,
+ "unzip and write memory to block device",
+ "<interface> <dev> <addr> length [wbuf=1M [offs=0 [outsize=0]]]\n"
+ "\twbuf is the size in bytes (hex) of write buffer\n"
+ "\t\tand should be padded to erase size for SSDs\n"
+ "\toffs is the output start offset in bytes (hex)\n"
+ "\toutsize is the size of the expected output (hex bytes)\n"
+ "\t\tand is required for files with uncompressed lengths\n"
+ "\t\t4 GiB or larger\n"
+);
diff --git a/cmd/upl.c b/cmd/upl.c
new file mode 100644
index 00000000000..ef2183d8528
--- /dev/null
+++ b/cmd/upl.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Commands for UPL handoff generation
+ *
+ * Copyright 2024 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <abuf.h>
+#include <alist.h>
+#include <command.h>
+#include <display_options.h>
+#include <env.h>
+#include <mapmem.h>
+#include <string.h>
+#include <upl.h>
+#include <dm/ofnode.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int do_upl_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const struct upl *upl = gd_upl();
+
+ printf("UPL state: %sactive\n", upl ? "" : "in");
+ if (!upl)
+ return 0;
+ if (argc > 1 && !strcmp("-v", argv[1])) {
+ int i;
+
+ printf("fit %lx\n", upl->fit);
+ printf("conf_offset %x\n", upl->conf_offset);
+ for (i = 0; i < upl->image.count; i++) {
+ const struct upl_image *img =
+ alist_get(&upl->image, i, struct upl_image);
+
+ printf("image %d: load %lx size %lx offset %x: %s\n", i,
+ img->load, img->size, img->offset,
+ img->description);
+ }
+ }
+
+ return 0;
+}
+
+static int do_upl_write(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct upl s_upl, *upl = &s_upl;
+ struct unit_test_state uts = { 0 };
+ struct abuf buf;
+ oftree tree;
+ ulong addr;
+ int ret;
+
+ upl_get_test_data(&uts, upl);
+
+ log_debug("Writing UPL\n");
+ ret = upl_create_handoff_tree(upl, &tree);
+ if (ret) {
+ log_err("Failed to write (err=%dE)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ log_debug("Flattening\n");
+ ret = oftree_to_fdt(tree, &buf);
+ if (ret) {
+ log_err("Failed to write (err=%dE)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ addr = map_to_sysmem(abuf_data(&buf));
+ printf("UPL handoff written to %lx size %zx\n", addr, abuf_size(&buf));
+ if (env_set_hex("upladdr", addr) ||
+ env_set_hex("uplsize", abuf_size(&buf))) {
+ printf("Cannot set env var\n");
+ return CMD_RET_FAILURE;
+ }
+
+ log_debug("done\n");
+
+ return 0;
+}
+
+static int do_upl_read(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct upl s_upl, *upl = &s_upl;
+ oftree tree;
+ ulong addr;
+ int ret;
+
+ if (argc < 1)
+ return CMD_RET_USAGE;
+ addr = hextoul(argv[1], NULL);
+
+ printf("Reading UPL at %lx\n", addr);
+ tree = oftree_from_fdt(map_sysmem(addr, 0));
+ ret = upl_read_handoff(upl, tree);
+ if (ret) {
+ log_err("Failed to read (err=%dE)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(upl,
+ "info [-v] - Check UPL status\n"
+ "upl read <addr> - Read handoff information\n"
+ "upl write - Write handoff information");
+
+U_BOOT_CMD_WITH_SUBCMDS(upl, "Universal Payload support", upl_help_text,
+ U_BOOT_SUBCMD_MKENT(info, 2, 1, do_upl_info),
+ U_BOOT_SUBCMD_MKENT(read, 2, 1, do_upl_read),
+ U_BOOT_SUBCMD_MKENT(write, 1, 1, do_upl_write));
diff --git a/cmd/usb.c b/cmd/usb.c
new file mode 100644
index 00000000000..13a2996c1f0
--- /dev/null
+++ b/cmd/usb.c
@@ -0,0 +1,725 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2001
+ * Denis Peter, MPL AG Switzerland
+ *
+ * Adapted for U-Boot driver model
+ * (C) Copyright 2015 Google, Inc
+ *
+ * Most of this source has been derived from the Linux USB
+ * project.
+ */
+
+#include <blk.h>
+#include <bootstage.h>
+#include <command.h>
+#include <console.h>
+#include <dm.h>
+#include <dm/uclass-internal.h>
+#include <memalign.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include <part.h>
+#include <usb.h>
+
+#ifdef CONFIG_USB_STORAGE
+static int usb_stor_curr_dev = -1; /* current device */
+#endif
+
+/* some display routines (info command) */
+static char *usb_get_class_desc(unsigned char dclass)
+{
+ switch (dclass) {
+ case USB_CLASS_PER_INTERFACE:
+ return "See Interface";
+ case USB_CLASS_AUDIO:
+ return "Audio";
+ case USB_CLASS_COMM:
+ return "Communication";
+ case USB_CLASS_HID:
+ return "Human Interface";
+ case USB_CLASS_PRINTER:
+ return "Printer";
+ case USB_CLASS_MASS_STORAGE:
+ return "Mass Storage";
+ case USB_CLASS_HUB:
+ return "Hub";
+ case USB_CLASS_DATA:
+ return "CDC Data";
+ case USB_CLASS_VENDOR_SPEC:
+ return "Vendor specific";
+ default:
+ return "";
+ }
+}
+
+static void usb_display_class_sub(unsigned char dclass, unsigned char subclass,
+ unsigned char proto)
+{
+ switch (dclass) {
+ case USB_CLASS_PER_INTERFACE:
+ printf("See Interface");
+ break;
+ case USB_CLASS_HID:
+ printf("Human Interface, Subclass: ");
+ switch (subclass) {
+ case USB_SUB_HID_NONE:
+ printf("None");
+ break;
+ case USB_SUB_HID_BOOT:
+ printf("Boot ");
+ switch (proto) {
+ case USB_PROT_HID_NONE:
+ printf("None");
+ break;
+ case USB_PROT_HID_KEYBOARD:
+ printf("Keyboard");
+ break;
+ case USB_PROT_HID_MOUSE:
+ printf("Mouse");
+ break;
+ default:
+ printf("reserved");
+ break;
+ }
+ break;
+ default:
+ printf("reserved");
+ break;
+ }
+ break;
+ case USB_CLASS_MASS_STORAGE:
+ printf("Mass Storage, ");
+ switch (subclass) {
+ case US_SC_RBC:
+ printf("RBC ");
+ break;
+ case US_SC_8020:
+ printf("SFF-8020i (ATAPI)");
+ break;
+ case US_SC_QIC:
+ printf("QIC-157 (Tape)");
+ break;
+ case US_SC_UFI:
+ printf("UFI");
+ break;
+ case US_SC_8070:
+ printf("SFF-8070");
+ break;
+ case US_SC_SCSI:
+ printf("Transp. SCSI");
+ break;
+ default:
+ printf("reserved");
+ break;
+ }
+ printf(", ");
+ switch (proto) {
+ case US_PR_CB:
+ printf("Command/Bulk");
+ break;
+ case US_PR_CBI:
+ printf("Command/Bulk/Int");
+ break;
+ case US_PR_BULK:
+ printf("Bulk only");
+ break;
+ default:
+ printf("reserved");
+ break;
+ }
+ break;
+ default:
+ printf("%s", usb_get_class_desc(dclass));
+ break;
+ }
+}
+
+static void usb_display_string(struct usb_device *dev, int index)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(char, buffer, 256);
+
+ if (index != 0) {
+ if (usb_string(dev, index, &buffer[0], 256) > 0)
+ printf("String: \"%s\"", buffer);
+ }
+}
+
+static void usb_display_desc(struct usb_device *dev)
+{
+ uint packet_size = dev->descriptor.bMaxPacketSize0;
+
+ if (dev->descriptor.bDescriptorType == USB_DT_DEVICE) {
+ printf("%d: %s, USB Revision %x.%x\n", dev->devnum,
+ usb_get_class_desc(dev->config.if_desc[0].desc.bInterfaceClass),
+ (dev->descriptor.bcdUSB>>8) & 0xff,
+ dev->descriptor.bcdUSB & 0xff);
+
+ if (strlen(dev->mf) || strlen(dev->prod) ||
+ strlen(dev->serial))
+ printf(" - %s %s %s\n", dev->mf, dev->prod,
+ dev->serial);
+ if (dev->descriptor.bDeviceClass) {
+ printf(" - Class: ");
+ usb_display_class_sub(dev->descriptor.bDeviceClass,
+ dev->descriptor.bDeviceSubClass,
+ dev->descriptor.bDeviceProtocol);
+ printf("\n");
+ } else {
+ printf(" - Class: (from Interface) %s\n",
+ usb_get_class_desc(
+ dev->config.if_desc[0].desc.bInterfaceClass));
+ }
+ if (dev->descriptor.bcdUSB >= cpu_to_le16(0x0300))
+ packet_size = 1 << packet_size;
+ printf(" - PacketSize: %d Configurations: %d\n",
+ packet_size, dev->descriptor.bNumConfigurations);
+ printf(" - Vendor: 0x%04x Product 0x%04x Version %d.%d\n",
+ dev->descriptor.idVendor, dev->descriptor.idProduct,
+ (dev->descriptor.bcdDevice>>8) & 0xff,
+ dev->descriptor.bcdDevice & 0xff);
+ }
+
+}
+
+static void usb_display_conf_desc(struct usb_config_descriptor *config,
+ struct usb_device *dev)
+{
+ printf(" Configuration: %d\n", config->bConfigurationValue);
+ printf(" - Interfaces: %d %s%s%dmA\n", config->bNumInterfaces,
+ (config->bmAttributes & 0x40) ? "Self Powered " : "Bus Powered ",
+ (config->bmAttributes & 0x20) ? "Remote Wakeup " : "",
+ config->bMaxPower*2);
+ if (config->iConfiguration) {
+ printf(" - ");
+ usb_display_string(dev, config->iConfiguration);
+ printf("\n");
+ }
+}
+
+static void usb_display_if_desc(struct usb_interface_descriptor *ifdesc,
+ struct usb_device *dev)
+{
+ printf(" Interface: %d\n", ifdesc->bInterfaceNumber);
+ printf(" - Alternate Setting %d, Endpoints: %d\n",
+ ifdesc->bAlternateSetting, ifdesc->bNumEndpoints);
+ printf(" - Class ");
+ usb_display_class_sub(ifdesc->bInterfaceClass,
+ ifdesc->bInterfaceSubClass, ifdesc->bInterfaceProtocol);
+ printf("\n");
+ if (ifdesc->iInterface) {
+ printf(" - ");
+ usb_display_string(dev, ifdesc->iInterface);
+ printf("\n");
+ }
+}
+
+static void usb_display_ep_desc(struct usb_endpoint_descriptor *epdesc)
+{
+ printf(" - Endpoint %d %s ", epdesc->bEndpointAddress & 0xf,
+ (epdesc->bEndpointAddress & 0x80) ? "In" : "Out");
+ switch ((epdesc->bmAttributes & 0x03)) {
+ case 0:
+ printf("Control");
+ break;
+ case 1:
+ printf("Isochronous");
+ break;
+ case 2:
+ printf("Bulk");
+ break;
+ case 3:
+ printf("Interrupt");
+ break;
+ }
+ printf(" MaxPacket %d", get_unaligned(&epdesc->wMaxPacketSize));
+ if ((epdesc->bmAttributes & 0x03) == 0x3)
+ printf(" Interval %dms", epdesc->bInterval);
+ printf("\n");
+}
+
+/* main routine to diasplay the configs, interfaces and endpoints */
+static void usb_display_config(struct usb_device *dev)
+{
+ struct usb_config *config;
+ struct usb_interface *ifdesc;
+ struct usb_endpoint_descriptor *epdesc;
+ int i, ii;
+
+ config = &dev->config;
+ usb_display_conf_desc(&config->desc, dev);
+ for (i = 0; i < config->no_of_if; i++) {
+ ifdesc = &config->if_desc[i];
+ usb_display_if_desc(&ifdesc->desc, dev);
+ for (ii = 0; ii < ifdesc->no_of_ep; ii++) {
+ epdesc = &ifdesc->ep_desc[ii];
+ usb_display_ep_desc(epdesc);
+ }
+ }
+ printf("\n");
+}
+
+/*
+ * With driver model this isn't right since we can have multiple controllers
+ * and the device numbering starts at 1 on each bus.
+ * TODO(sjg@chromium.org): Add a way to specify the controller/bus.
+ */
+static struct usb_device *usb_find_device(int devnum)
+{
+#ifdef CONFIG_DM_USB
+ struct usb_device *udev;
+ struct udevice *hub;
+ struct uclass *uc;
+ int ret;
+
+ /* Device addresses start at 1 */
+ devnum++;
+ ret = uclass_get(UCLASS_USB_HUB, &uc);
+ if (ret)
+ return NULL;
+
+ uclass_foreach_dev(hub, uc) {
+ struct udevice *dev;
+
+ if (!device_active(hub))
+ continue;
+ udev = dev_get_parent_priv(hub);
+ if (udev->devnum == devnum)
+ return udev;
+
+ for (device_find_first_child(hub, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ if (!device_active(hub))
+ continue;
+
+ udev = dev_get_parent_priv(dev);
+ if (udev->devnum == devnum)
+ return udev;
+ }
+ }
+#else
+ struct usb_device *udev;
+ int d;
+
+ for (d = 0; d < USB_MAX_DEVICE; d++) {
+ udev = usb_get_dev_index(d);
+ if (udev == NULL)
+ return NULL;
+ if (udev->devnum == devnum)
+ return udev;
+ }
+#endif
+
+ return NULL;
+}
+
+static inline const char *portspeed(int speed)
+{
+ switch (speed) {
+ case USB_SPEED_SUPER:
+ return "5 Gb/s";
+ case USB_SPEED_HIGH:
+ return "480 Mb/s";
+ case USB_SPEED_LOW:
+ return "1.5 Mb/s";
+ default:
+ return "12 Mb/s";
+ }
+}
+
+/* shows the device tree recursively */
+static void usb_show_tree_graph(struct usb_device *dev, char *pre)
+{
+ int index;
+ int has_child, last_child;
+
+ index = strlen(pre);
+ printf(" %s", pre);
+#ifdef CONFIG_DM_USB
+ has_child = device_has_active_children(dev->dev);
+ if (device_get_uclass_id(dev->dev) == UCLASS_MASS_STORAGE) {
+ struct udevice *child;
+
+ for (device_find_first_child(dev->dev, &child);
+ child;
+ device_find_next_child(&child)) {
+ if (device_get_uclass_id(child) == UCLASS_BLK)
+ has_child = 0;
+ }
+ }
+#else
+ /* check if the device has connected children */
+ int i;
+
+ has_child = 0;
+ for (i = 0; i < dev->maxchild; i++) {
+ if (dev->children[i] != NULL)
+ has_child = 1;
+ }
+#endif
+ /* check if we are the last one */
+#ifdef CONFIG_DM_USB
+ /* Not the root of the usb tree? */
+ if (device_get_uclass_id(dev->dev->parent) != UCLASS_USB) {
+ last_child = device_is_last_sibling(dev->dev);
+#else
+ if (dev->parent != NULL) { /* not root? */
+ last_child = 1;
+ for (i = 0; i < dev->parent->maxchild; i++) {
+ /* search for children */
+ if (dev->parent->children[i] == dev) {
+ /* found our pointer, see if we have a
+ * little sister
+ */
+ while (i++ < dev->parent->maxchild) {
+ if (dev->parent->children[i] != NULL) {
+ /* found a sister */
+ last_child = 0;
+ break;
+ } /* if */
+ } /* while */
+ } /* device found */
+ } /* for all children of the parent */
+#endif
+ printf("\b+-");
+ /* correct last child */
+ if (last_child && index)
+ pre[index-1] = ' ';
+ } /* if not root hub */
+ else
+ printf(" ");
+ printf("%d ", dev->devnum);
+ pre[index++] = ' ';
+ pre[index++] = has_child ? '|' : ' ';
+ pre[index] = 0;
+ printf(" %s (%s, %dmA)\n", usb_get_class_desc(
+ dev->config.if_desc[0].desc.bInterfaceClass),
+ portspeed(dev->speed),
+ dev->config.desc.bMaxPower * 2);
+ if (strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial))
+ printf(" %s %s %s %s\n", pre, dev->mf, dev->prod, dev->serial);
+ printf(" %s\n", pre);
+#ifdef CONFIG_DM_USB
+ struct udevice *child;
+
+ for (device_find_first_child(dev->dev, &child);
+ child;
+ device_find_next_child(&child)) {
+ struct usb_device *udev;
+
+ if (!device_active(child))
+ continue;
+
+ udev = dev_get_parent_priv(child);
+
+ /*
+ * Ignore emulators and block child devices, we only want
+ * real devices
+ */
+ if (udev &&
+ (device_get_uclass_id(child) != UCLASS_BOOTDEV) &&
+ (device_get_uclass_id(child) != UCLASS_USB_EMUL) &&
+ (device_get_uclass_id(child) != UCLASS_BLK)) {
+ usb_show_tree_graph(udev, pre);
+ pre[index] = 0;
+ }
+ }
+#else
+ if (dev->maxchild > 0) {
+ for (i = 0; i < dev->maxchild; i++) {
+ if (dev->children[i] != NULL) {
+ usb_show_tree_graph(dev->children[i], pre);
+ pre[index] = 0;
+ }
+ }
+ }
+#endif
+}
+
+/* main routine for the tree command */
+static void usb_show_subtree(struct usb_device *dev)
+{
+ char preamble[32];
+
+ memset(preamble, '\0', sizeof(preamble));
+ usb_show_tree_graph(dev, &preamble[0]);
+}
+
+#ifdef CONFIG_DM_USB
+typedef void (*usb_dev_func_t)(struct usb_device *udev);
+
+static void usb_for_each_root_dev(usb_dev_func_t func)
+{
+ struct udevice *bus;
+
+ for (uclass_find_first_device(UCLASS_USB, &bus);
+ bus;
+ uclass_find_next_device(&bus)) {
+ struct usb_device *udev;
+ struct udevice *dev;
+
+ if (!device_active(bus))
+ continue;
+
+ device_find_first_child(bus, &dev);
+ if (dev && device_active(dev)) {
+ udev = dev_get_parent_priv(dev);
+ func(udev);
+ }
+ }
+}
+#endif
+
+void usb_show_tree(void)
+{
+#ifdef CONFIG_DM_USB
+ usb_for_each_root_dev(usb_show_subtree);
+#else
+ struct usb_device *udev;
+ int i;
+
+ for (i = 0; i < USB_MAX_DEVICE; i++) {
+ udev = usb_get_dev_index(i);
+ if (udev == NULL)
+ break;
+ if (udev->parent == NULL)
+ usb_show_subtree(udev);
+ }
+#endif
+}
+
+static int usb_test(struct usb_device *dev, int port, char* arg)
+{
+ int mode;
+
+ if (port > dev->maxchild) {
+ printf("Device is no hub or does not have %d ports.\n", port);
+ return 1;
+ }
+
+ switch (arg[0]) {
+ case 'J':
+ case 'j':
+ printf("Setting Test_J mode");
+ mode = USB_TEST_MODE_J;
+ break;
+ case 'K':
+ case 'k':
+ printf("Setting Test_K mode");
+ mode = USB_TEST_MODE_K;
+ break;
+ case 'S':
+ case 's':
+ printf("Setting Test_SE0_NAK mode");
+ mode = USB_TEST_MODE_SE0_NAK;
+ break;
+ case 'P':
+ case 'p':
+ printf("Setting Test_Packet mode");
+ mode = USB_TEST_MODE_PACKET;
+ break;
+ case 'F':
+ case 'f':
+ printf("Setting Test_Force_Enable mode");
+ mode = USB_TEST_MODE_FORCE_ENABLE;
+ break;
+ default:
+ printf("Unrecognized test mode: %s\nAvailable modes: "
+ "J, K, S[E0_NAK], P[acket], F[orce_Enable]\n", arg);
+ return 1;
+ }
+
+ if (port)
+ printf(" on downstream facing port %d...\n", port);
+ else
+ printf(" on upstream facing port...\n");
+
+ if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_FEATURE,
+ port ? USB_RT_PORT : USB_RECIP_DEVICE,
+ port ? USB_PORT_FEAT_TEST : USB_FEAT_TEST,
+ (mode << 8) | port,
+ NULL, 0, USB_CNTL_TIMEOUT) == -1) {
+ printf("Error during SET_FEATURE.\n");
+ return 1;
+ } else {
+ printf("Test mode successfully set. Use 'usb start' "
+ "to return to normal operation.\n");
+ return 0;
+ }
+}
+
+/******************************************************************************
+ * usb boot command intepreter. Derived from diskboot
+ */
+#ifdef CONFIG_USB_STORAGE
+static int do_usbboot(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return common_diskboot(cmdtp, "usb", argc, argv);
+}
+#endif /* CONFIG_USB_STORAGE */
+
+static void do_usb_start(void)
+{
+ bootstage_mark_name(BOOTSTAGE_ID_USB_START, "usb_start");
+
+ if (usb_init() < 0)
+ return;
+
+ /* Driver model will probe the devices as they are found */
+# ifdef CONFIG_USB_STORAGE
+ /* try to recognize storage devices immediately */
+ usb_stor_curr_dev = usb_stor_scan(1);
+# endif
+}
+
+#ifdef CONFIG_DM_USB
+static void usb_show_info(struct usb_device *udev)
+{
+ struct udevice *child;
+
+ usb_display_desc(udev);
+ usb_display_config(udev);
+ for (device_find_first_child(udev->dev, &child);
+ child;
+ device_find_next_child(&child)) {
+ if (device_active(child) &&
+ (device_get_uclass_id(child) != UCLASS_BOOTDEV) &&
+ (device_get_uclass_id(child) != UCLASS_USB_EMUL) &&
+ (device_get_uclass_id(child) != UCLASS_BLK)) {
+ udev = dev_get_parent_priv(child);
+ if (udev)
+ usb_show_info(udev);
+ }
+ }
+}
+#endif
+
+/******************************************************************************
+ * usb command intepreter
+ */
+static int do_usb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct usb_device *udev = NULL;
+ int i;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (strncmp(argv[1], "start", 5) == 0) {
+ if (usb_started)
+ return 0; /* Already started */
+ printf("starting USB...\n");
+ do_usb_start();
+ return 0;
+ }
+
+ if (strncmp(argv[1], "reset", 5) == 0) {
+ printf("resetting USB...\n");
+ usb_stop();
+ do_usb_start();
+ return 0;
+ }
+ if (strncmp(argv[1], "stop", 4) == 0) {
+ if (argc != 2)
+ console_assign(stdin, "serial");
+ printf("stopping USB..\n");
+ usb_stop();
+ return 0;
+ }
+ if (!usb_started) {
+ printf("USB is stopped. Please issue 'usb start' first.\n");
+ return 1;
+ }
+ if (strncmp(argv[1], "tree", 4) == 0) {
+ puts("USB device tree:\n");
+ usb_show_tree();
+ return 0;
+ }
+ if (strncmp(argv[1], "inf", 3) == 0) {
+ if (argc == 2) {
+#ifdef CONFIG_DM_USB
+ usb_for_each_root_dev(usb_show_info);
+#else
+ int d;
+ for (d = 0; d < USB_MAX_DEVICE; d++) {
+ udev = usb_get_dev_index(d);
+ if (udev == NULL)
+ break;
+ usb_display_desc(udev);
+ usb_display_config(udev);
+ }
+#endif
+ return 0;
+ } else {
+ /*
+ * With driver model this isn't right since we can
+ * have multiple controllers and the device numbering
+ * starts at 1 on each bus.
+ */
+ i = dectoul(argv[2], NULL);
+ printf("config for device %d\n", i);
+ udev = usb_find_device(i);
+ if (udev == NULL) {
+ printf("*** No device available ***\n");
+ return 0;
+ } else {
+ usb_display_desc(udev);
+ usb_display_config(udev);
+ }
+ }
+ return 0;
+ }
+ if (strncmp(argv[1], "test", 4) == 0) {
+ if (argc < 5)
+ return CMD_RET_USAGE;
+ i = dectoul(argv[2], NULL);
+ udev = usb_find_device(i);
+ if (udev == NULL) {
+ printf("Device %d does not exist.\n", i);
+ return 1;
+ }
+ i = dectoul(argv[3], NULL);
+ return usb_test(udev, i, argv[4]);
+ }
+#ifdef CONFIG_USB_STORAGE
+ if (strncmp(argv[1], "stor", 4) == 0)
+ return usb_stor_info();
+
+ return blk_common_cmd(argc, argv, UCLASS_USB, &usb_stor_curr_dev);
+#else
+ return CMD_RET_USAGE;
+#endif /* CONFIG_USB_STORAGE */
+}
+
+U_BOOT_CMD(
+ usb, 5, 1, do_usb,
+ "USB sub-system",
+ "start - start (scan) USB controller\n"
+ "usb reset - reset (rescan) USB controller\n"
+ "usb stop [f] - stop USB [f]=force stop\n"
+ "usb tree - show USB device tree\n"
+ "usb info [dev] - show available USB devices\n"
+ "usb test [dev] [port] [mode] - set USB 2.0 test mode\n"
+ " (specify port 0 to indicate the device's upstream port)\n"
+ " Available modes: J, K, S[E0_NAK], P[acket], F[orce_Enable]\n"
+#ifdef CONFIG_USB_STORAGE
+ "usb storage - show details of USB storage devices\n"
+ "usb dev [dev] - show or set current USB storage device\n"
+ "usb part [dev] - print partition table of one or all USB storage"
+ " devices\n"
+ "usb read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n"
+ " to memory address `addr'\n"
+ "usb write addr blk# cnt - write `cnt' blocks starting at block `blk#'\n"
+ " from memory address `addr'"
+#endif /* CONFIG_USB_STORAGE */
+);
+
+#ifdef CONFIG_USB_STORAGE
+U_BOOT_CMD(
+ usbboot, 3, 1, do_usbboot,
+ "boot from USB device",
+ "loadAddr dev:part"
+);
+#endif /* CONFIG_USB_STORAGE */
diff --git a/cmd/usb_gadget_sdp.c b/cmd/usb_gadget_sdp.c
new file mode 100644
index 00000000000..39259a3b092
--- /dev/null
+++ b/cmd/usb_gadget_sdp.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * cmd_sdp.c -- sdp command
+ *
+ * Copyright (C) 2016 Toradex
+ * Author: Stefan Agner <stefan.agner@toradex.com>
+ */
+
+#include <command.h>
+#include <g_dnl.h>
+#include <sdp.h>
+#include <usb.h>
+#include <linux/printk.h>
+
+static int do_sdp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int controller_index;
+ struct udevice *udc;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ controller_index = simple_strtoul(argv[1], NULL, 0);
+ ret = udc_device_get_by_index(controller_index, &udc);
+ if (ret)
+ return ret;
+
+ g_dnl_clear_detach();
+ ret = g_dnl_register("usb_dnl_sdp");
+ if (ret) {
+ pr_err("SDP dnl register failed: %d\n", ret);
+ goto exit_register;
+ }
+
+ ret = sdp_init(udc);
+ if (ret) {
+ pr_err("SDP init failed: %d\n", ret);
+ goto exit;
+ }
+
+ /* This command typically does not return but jumps to an image */
+ sdp_handle(udc);
+ pr_err("SDP ended\n");
+
+exit:
+ g_dnl_unregister();
+exit_register:
+ udc_device_put(udc);
+
+ return CMD_RET_FAILURE;
+}
+
+U_BOOT_CMD(sdp, 2, 1, do_sdp,
+ "Serial Downloader Protocol",
+ "<USB_controller>\n"
+ " - serial downloader protocol via <USB_controller>\n"
+);
diff --git a/cmd/usb_mass_storage.c b/cmd/usb_mass_storage.c
new file mode 100644
index 00000000000..47e8b70cd10
--- /dev/null
+++ b/cmd/usb_mass_storage.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <blk.h>
+#include <command.h>
+#include <console.h>
+#include <errno.h>
+#include <g_dnl.h>
+#include <malloc.h>
+#include <part.h>
+#include <usb.h>
+#include <usb_mass_storage.h>
+#include <watchdog.h>
+#include <linux/delay.h>
+#include <linux/printk.h>
+
+static int ums_read_sector(struct ums *ums_dev,
+ ulong start, lbaint_t blkcnt, void *buf)
+{
+ struct blk_desc *block_dev = &ums_dev->block_dev;
+ lbaint_t blkstart = start + ums_dev->start_sector;
+
+ return blk_dread(block_dev, blkstart, blkcnt, buf);
+}
+
+static int ums_write_sector(struct ums *ums_dev,
+ ulong start, lbaint_t blkcnt, const void *buf)
+{
+ struct blk_desc *block_dev = &ums_dev->block_dev;
+ lbaint_t blkstart = start + ums_dev->start_sector;
+
+ return blk_dwrite(block_dev, blkstart, blkcnt, buf);
+}
+
+static struct ums *ums;
+static int ums_count;
+
+static void ums_fini(void)
+{
+ int i;
+
+ for (i = 0; i < ums_count; i++)
+ free((void *)ums[i].name);
+ free(ums);
+ ums = NULL;
+ ums_count = 0;
+}
+
+#define UMS_NAME_LEN 16
+
+static int ums_init(const char *devtype, const char *devnums_part_str)
+{
+ char *s, *t, *devnum_part_str, *name;
+ struct blk_desc *block_dev;
+ struct disk_partition info;
+ int partnum;
+ int ret = -1;
+ struct ums *ums_new;
+
+ s = strdup(devnums_part_str);
+ if (!s)
+ return -1;
+
+ t = s;
+ ums_count = 0;
+
+ for (;;) {
+ devnum_part_str = strsep(&t, ",");
+ if (!devnum_part_str)
+ break;
+
+ partnum = part_get_info_by_dev_and_name_or_num(devtype, devnum_part_str,
+ &block_dev, &info, 1);
+
+ if (partnum < 0)
+ goto cleanup;
+
+ /* Check if the argument is in legacy format. If yes,
+ * expose all partitions by setting the partnum = 0
+ * e.g. ums 0 mmc 0
+ */
+ if (!strchr(devnum_part_str, ':'))
+ partnum = 0;
+
+ ums_new = realloc(ums, (ums_count + 1) * sizeof(*ums));
+ if (!ums_new)
+ goto cleanup;
+ ums = ums_new;
+
+ /* if partnum = 0, expose all partitions */
+ if (partnum == 0) {
+ ums[ums_count].start_sector = 0;
+ ums[ums_count].num_sectors = block_dev->lba;
+ } else {
+ ums[ums_count].start_sector = info.start;
+ ums[ums_count].num_sectors = info.size;
+ }
+
+ ums[ums_count].read_sector = ums_read_sector;
+ ums[ums_count].write_sector = ums_write_sector;
+
+ name = malloc(UMS_NAME_LEN);
+ if (!name)
+ goto cleanup;
+ snprintf(name, UMS_NAME_LEN, "UMS disk %d", ums_count);
+ ums[ums_count].name = name;
+ ums[ums_count].block_dev = *block_dev;
+
+ printf("UMS: LUN %d, dev %s %d, hwpart %d, sector %#x, count %#x\n",
+ ums_count, devtype, ums[ums_count].block_dev.devnum,
+ ums[ums_count].block_dev.hwpart,
+ ums[ums_count].start_sector,
+ ums[ums_count].num_sectors);
+
+ ums_count++;
+ }
+
+ if (ums_count)
+ ret = 0;
+
+cleanup:
+ free(s);
+
+ if (ret < 0)
+ ums_fini();
+
+ return ret;
+}
+
+static int do_usb_mass_storage(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ const char *usb_controller;
+ const char *devtype;
+ const char *devnum;
+ unsigned int controller_index;
+ struct udevice *udc;
+ int rc;
+ int cable_ready_timeout __maybe_unused;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ usb_controller = argv[1];
+ if (argc >= 4) {
+ devtype = argv[2];
+ devnum = argv[3];
+ } else {
+ devtype = "mmc";
+ devnum = argv[2];
+ }
+
+ rc = ums_init(devtype, devnum);
+ if (rc < 0)
+ return CMD_RET_FAILURE;
+
+ controller_index = (unsigned int)(simple_strtoul(
+ usb_controller, NULL, 0));
+ rc = udc_device_get_by_index(controller_index, &udc);
+ if (rc) {
+ pr_err("Couldn't init USB controller.\n");
+ rc = CMD_RET_FAILURE;
+ goto cleanup_ums_init;
+ }
+
+ rc = fsg_init(ums, ums_count, udc);
+ if (rc) {
+ pr_err("fsg_init failed\n");
+ rc = CMD_RET_FAILURE;
+ goto cleanup_board;
+ }
+
+ rc = g_dnl_register("usb_dnl_ums");
+ if (rc) {
+ pr_err("g_dnl_register failed\n");
+ rc = CMD_RET_FAILURE;
+ goto cleanup_board;
+ }
+
+ /* Timeout unit: seconds */
+ cable_ready_timeout = UMS_CABLE_READY_TIMEOUT;
+
+ if (!g_dnl_board_usb_cable_connected()) {
+ /*
+ * Won't execute if we don't know whether the cable is
+ * connected.
+ */
+ puts("Please connect USB cable.\n");
+
+ while (!g_dnl_board_usb_cable_connected()) {
+ if (ctrlc()) {
+ puts("\rCTRL+C - Operation aborted.\n");
+ rc = CMD_RET_SUCCESS;
+ goto cleanup_register;
+ }
+ if (!cable_ready_timeout) {
+ puts("\rUSB cable not detected.\n" \
+ "Command exit.\n");
+ rc = CMD_RET_SUCCESS;
+ goto cleanup_register;
+ }
+
+ printf("\rAuto exit in: %.2d s.", cable_ready_timeout);
+ mdelay(1000);
+ cable_ready_timeout--;
+ }
+ puts("\r\n");
+ }
+
+ while (1) {
+ dm_usb_gadget_handle_interrupts(udc);
+
+ rc = fsg_main_thread(NULL);
+ if (rc) {
+ /* Check I/O error */
+ if (rc == -EIO)
+ printf("\rCheck USB cable connection\n");
+
+ /* Check CTRL+C */
+ if (rc == -EPIPE)
+ printf("\rCTRL+C - Operation aborted\n");
+
+ rc = CMD_RET_SUCCESS;
+ goto cleanup_register;
+ }
+
+ if (IS_ENABLED(CONFIG_CMD_UMS_ABORT_KEYED)) {
+ /* Abort by pressing any key */
+ if (tstc()) {
+ getchar();
+ printf("\rOperation aborted.\n");
+ rc = CMD_RET_SUCCESS;
+ goto cleanup_register;
+ }
+ }
+
+ schedule();
+ }
+
+cleanup_register:
+ g_dnl_unregister();
+cleanup_board:
+ udc_device_put(udc);
+cleanup_ums_init:
+ ums_fini();
+
+ return rc;
+}
+
+U_BOOT_CMD(ums, 4, 1, do_usb_mass_storage,
+ "Use the UMS [USB Mass Storage]",
+ "<USB_controller> [<devtype>] <dev[:part]> e.g. ums 0 mmc 0\n"
+ " devtype defaults to mmc"
+);
diff --git a/cmd/vbe.c b/cmd/vbe.c
new file mode 100644
index 00000000000..186f6e6860d
--- /dev/null
+++ b/cmd/vbe.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Verified Boot for Embedded (VBE) command
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <bloblist.h>
+#include <bootmeth.h>
+#include <bootstd.h>
+#include <command.h>
+#include <spl.h>
+#include <vbe.h>
+
+static int do_vbe_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ vbe_list();
+
+ return 0;
+}
+
+static int do_vbe_select(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *std;
+ struct udevice *dev;
+ int ret;
+
+ ret = bootstd_get_priv(&std);
+ if (ret)
+ return CMD_RET_FAILURE;
+ if (argc < 2) {
+ std->vbe_bootmeth = NULL;
+ return 0;
+ }
+ if (vbe_find_by_any(argv[1], &dev))
+ return CMD_RET_FAILURE;
+
+ std->vbe_bootmeth = dev;
+
+ return 0;
+}
+
+static int do_vbe_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct bootstd_priv *std;
+ char buf[256];
+ int ret, len;
+
+ ret = bootstd_get_priv(&std);
+ if (ret)
+ return CMD_RET_FAILURE;
+ if (!std->vbe_bootmeth) {
+ printf("No VBE bootmeth selected\n");
+ return CMD_RET_FAILURE;
+ }
+ ret = bootmeth_get_state_desc(std->vbe_bootmeth, buf, sizeof(buf));
+ if (ret) {
+ printf("Failed (err=%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ len = strnlen(buf, sizeof(buf));
+ if (len >= sizeof(buf)) {
+ printf("Buffer overflow\n");
+ return CMD_RET_FAILURE;
+ }
+
+ puts(buf);
+ if (buf[len] != '\n')
+ putc('\n');
+
+ return 0;
+}
+
+static int do_vbe_state(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct vbe_handoff *handoff = NULL;
+ int i;
+
+ if (IS_ENABLED(CONFIG_BLOBLIST)) {
+ handoff = bloblist_find(BLOBLISTT_VBE,
+ sizeof(struct vbe_handoff));
+ }
+ if (!handoff) {
+ printf("No VBE state\n");
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Phases:");
+ for (i = PHASE_NONE; i < PHASE_COUNT; i++) {
+ if (handoff->phases & (1 << i))
+ printf(" %s", xpl_name(i));
+
+ }
+ if (!handoff->phases)
+ printf(" (none)");
+ printf("\n");
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(vbe,
+ "list - list VBE bootmeths\n"
+ "vbe select - select a VBE bootmeth by sequence or name\n"
+ "vbe info - show information about a VBE bootmeth\n"
+ "vbe state - show VBE state");
+
+U_BOOT_CMD_WITH_SUBCMDS(vbe, "Verified Boot for Embedded", vbe_help_text,
+ U_BOOT_SUBCMD_MKENT(list, 1, 1, do_vbe_list),
+ U_BOOT_SUBCMD_MKENT(select, 2, 1, do_vbe_select),
+ U_BOOT_SUBCMD_MKENT(state, 2, 1, do_vbe_state),
+ U_BOOT_SUBCMD_MKENT(info, 2, 1, do_vbe_info));
diff --git a/cmd/version.c b/cmd/version.c
new file mode 100644
index 00000000000..62406608eb0
--- /dev/null
+++ b/cmd/version.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2000-2009
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <command.h>
+#include <display_options.h>
+#include <version.h>
+#include <version_string.h>
+#include <linux/compiler.h>
+#ifdef CONFIG_SYS_COREBOOT
+#include <asm/cb_sysinfo.h>
+#endif
+
+static int do_version(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char buf[DISPLAY_OPTIONS_BANNER_LENGTH];
+
+ printf(display_options_get_banner(false, buf, sizeof(buf)));
+#ifdef CC_VERSION_STRING
+ puts(CC_VERSION_STRING "\n");
+#endif
+#ifdef LD_VERSION_STRING
+ puts(LD_VERSION_STRING "\n");
+#endif
+#ifdef CONFIG_SYS_COREBOOT
+ printf("coreboot-%s (%s)\n", lib_sysinfo.version, lib_sysinfo.build);
+#endif
+ return 0;
+}
+
+U_BOOT_CMD(
+ version, 1, 1, do_version,
+ "print monitor, compiler and linker version",
+ ""
+);
diff --git a/cmd/video.c b/cmd/video.c
new file mode 100644
index 00000000000..91bd6de14dc
--- /dev/null
+++ b/cmd/video.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * video commands
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <video.h>
+#include <video_console.h>
+
+static int do_video_setcursor(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ unsigned int col, row;
+ struct udevice *dev;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
+ return CMD_RET_FAILURE;
+ col = dectoul(argv[1], NULL);
+ row = dectoul(argv[2], NULL);
+ vidconsole_position_cursor(dev, col, row);
+
+ return 0;
+}
+
+static int do_video_puts(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int ret;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
+ return CMD_RET_FAILURE;
+ ret = vidconsole_put_string(dev, argv[1]);
+ if (!ret)
+ ret = video_sync(dev->parent, false);
+
+ return ret ? CMD_RET_FAILURE : 0;
+}
+
+U_BOOT_CMD(
+ setcurs, 3, 1, do_video_setcursor,
+ "set cursor position within screen",
+ " <col> <row> in character"
+);
+
+U_BOOT_CMD(
+ lcdputs, 2, 1, do_video_puts,
+ "print string on video framebuffer",
+ " <string>"
+);
diff --git a/cmd/virtio.c b/cmd/virtio.c
new file mode 100644
index 00000000000..a42a563ab72
--- /dev/null
+++ b/cmd/virtio.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <blk.h>
+#include <command.h>
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+
+static int virtio_curr_dev;
+
+static int do_virtio(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (argc == 2 && !strcmp(argv[1], "scan")) {
+ /*
+ * make sure all virtio devices are enumerated.
+ * Do the same as virtio_init(), but also call
+ * device_probe() for children (i.e. virtio devices)
+ */
+ struct udevice *bus, *child;
+
+ uclass_first_device(UCLASS_VIRTIO, &bus);
+ if (!bus)
+ return CMD_RET_FAILURE;
+
+ while (bus) {
+ device_foreach_child_probe(child, bus)
+ ;
+ uclass_next_device(&bus);
+ }
+
+ return CMD_RET_SUCCESS;
+ }
+
+ return blk_common_cmd(argc, argv, UCLASS_VIRTIO, &virtio_curr_dev);
+}
+
+U_BOOT_CMD(
+ virtio, 8, 1, do_virtio,
+ "virtio block devices sub-system",
+ "scan - initialize virtio bus\n"
+ "virtio info - show all available virtio block devices\n"
+ "virtio device [dev] - show or set current virtio block device\n"
+ "virtio part [dev] - print partition table of one or all virtio block devices\n"
+ "virtio read addr blk# cnt - read `cnt' blocks starting at block\n"
+ " `blk#' to memory address `addr'\n"
+ "virtio write addr blk# cnt - write `cnt' blocks starting at block\n"
+ " `blk#' from memory address `addr'"
+);
diff --git a/cmd/w1.c b/cmd/w1.c
new file mode 100644
index 00000000000..e462e786a96
--- /dev/null
+++ b/cmd/w1.c
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ *
+ * (C) Copyright 2018
+ * Microchip Technology, Inc.
+ * Eugen Hristev <eugen.hristev@microchip.com>
+ */
+#include <command.h>
+#include <dm.h>
+#include <w1.h>
+#include <w1-eeprom.h>
+#include <dm/device-internal.h>
+
+static int w1_bus(void)
+{
+ struct udevice *bus, *dev;
+ int ret;
+
+ ret = w1_get_bus(0, &bus);
+ if (ret) {
+ printf("one wire interface not found\n");
+ return CMD_RET_FAILURE;
+ }
+ printf("Bus %d:\t%s", dev_seq(bus), bus->name);
+ if (device_active(bus))
+ printf(" (active)");
+ printf("\n");
+
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ ret = device_probe(dev);
+
+ printf("\t%s (%d) uclass %s : ", dev->name, dev_seq(dev),
+ dev->uclass->uc_drv->name);
+
+ if (ret)
+ printf("device error\n");
+ else
+ printf("family 0x%x\n", w1_get_device_family(dev));
+ }
+ return CMD_RET_SUCCESS;
+}
+
+static int w1_read(int argc, char *const argv[])
+{
+ int bus_n = 0, dev_n = 0, offset = 0, len = 512;
+ int i;
+ struct udevice *bus, *dev;
+ int ret;
+ u8 buf[512];
+
+ if (argc > 2)
+ bus_n = dectoul(argv[2], NULL);
+
+ if (argc > 3)
+ dev_n = dectoul(argv[3], NULL);
+
+ if (argc > 4)
+ offset = dectoul(argv[4], NULL);
+
+ if (argc > 5)
+ len = dectoul(argv[5], NULL);
+
+ if (len > 512) {
+ printf("len needs to be <= 512\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = w1_get_bus(bus_n, &bus);
+ if (ret) {
+ printf("one wire interface not found\n");
+ return CMD_RET_FAILURE;
+ }
+
+ for (device_find_first_child(bus, &dev), i = 0;
+ dev && i <= dev_n;
+ device_find_next_child(&dev), i++) {
+ ret = device_probe(dev);
+ if (!ret && i == dev_n)
+ break;
+ }
+
+ if (i != dev_n || ret || !dev) {
+ printf("invalid dev\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (strcmp(dev->uclass->uc_drv->name, "w1_eeprom")) {
+ printf("the device present on the interface is of unknown device class\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = w1_eeprom_read_buf(dev, offset, (u8 *)buf, len);
+ if (ret) {
+ printf("error reading device %s\n", dev->name);
+ return CMD_RET_FAILURE;
+ }
+
+ for (i = 0; i < len; i++)
+ printf("%x", buf[i]);
+ printf("\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+int do_w1(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (!strcmp(argv[1], "bus"))
+ return w1_bus();
+
+ if (!strcmp(argv[1], "read"))
+ return w1_read(argc, argv);
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(w1, 6, 0, do_w1,
+ "onewire interface utility commands",
+ "bus - show onewire bus info (all)\n"
+ "w1 read [<bus> [<dev> [offset [length]]]]"
+ " - read from onewire device 'dev' on onewire bus 'bus'"
+ " starting from offset 'offset' and length 'length'\n"
+ " defaults: bus 0, dev 0, offset 0, length 512 bytes.");
diff --git a/cmd/wdt.c b/cmd/wdt.c
new file mode 100644
index 00000000000..c7a06cca181
--- /dev/null
+++ b/cmd/wdt.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Watchdog commands
+ *
+ * Copyright (c) 2019 Michael Walle <michael@walle.cc>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <wdt.h>
+
+static struct udevice *currdev;
+
+static int do_wdt_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ struct uclass *uc;
+ int ret;
+
+ ret = uclass_get(UCLASS_WDT, &uc);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ uclass_foreach_dev(dev, uc)
+ printf("%s (%s)\n", dev->name, dev->driver->name);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_wdt_dev(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+
+ if (argc > 1) {
+ ret = uclass_get_device_by_name(UCLASS_WDT, argv[1], &currdev);
+ if (ret) {
+ printf("Can't get the watchdog timer: %s\n", argv[1]);
+ return CMD_RET_FAILURE;
+ }
+ } else {
+ if (!currdev) {
+ printf("No watchdog timer device set!\n");
+ return CMD_RET_FAILURE;
+ }
+ printf("dev: %s\n", currdev->name);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int check_currdev(void)
+{
+ if (!currdev) {
+ printf("No device set, use 'wdt dev' first\n");
+ return CMD_RET_FAILURE;
+ }
+ return 0;
+}
+
+static int do_wdt_start(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+ u64 timeout;
+ ulong flags = 0;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ ret = check_currdev();
+ if (ret)
+ return ret;
+
+ timeout = simple_strtoull(argv[1], NULL, 0);
+ if (argc > 2)
+ flags = simple_strtoul(argv[2], NULL, 0);
+
+ ret = wdt_start(currdev, timeout, flags);
+ if (ret == -ENOSYS) {
+ printf("Starting watchdog timer not supported.\n");
+ return CMD_RET_FAILURE;
+ } else if (ret) {
+ printf("Starting watchdog timer failed (%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_wdt_stop(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+
+ ret = check_currdev();
+ if (ret)
+ return ret;
+
+ ret = wdt_stop(currdev);
+ if (ret == -ENOSYS) {
+ printf("Stopping watchdog timer not supported.\n");
+ return CMD_RET_FAILURE;
+ } else if (ret) {
+ printf("Stopping watchdog timer failed (%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_wdt_reset(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+
+ ret = check_currdev();
+ if (ret)
+ return ret;
+
+ ret = wdt_reset(currdev);
+ if (ret == -ENOSYS) {
+ printf("Resetting watchdog timer not supported.\n");
+ return CMD_RET_FAILURE;
+ } else if (ret) {
+ printf("Resetting watchdog timer failed (%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_wdt_expire(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+ ulong flags = 0;
+
+ ret = check_currdev();
+ if (ret)
+ return ret;
+
+ if (argc > 1)
+ flags = simple_strtoul(argv[1], NULL, 0);
+
+ ret = wdt_expire_now(currdev, flags);
+ if (ret == -ENOSYS) {
+ printf("Expiring watchdog timer not supported.\n");
+ return CMD_RET_FAILURE;
+ } else if (ret) {
+ printf("Expiring watchdog timer failed (%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_LONGHELP(wdt,
+ "list - list watchdog devices\n"
+ "wdt dev [<name>] - get/set current watchdog device\n"
+ "wdt start <timeout ms> [flags] - start watchdog timer\n"
+ "wdt stop - stop watchdog timer\n"
+ "wdt reset - reset watchdog timer\n"
+ "wdt expire [flags] - expire watchdog timer immediately\n");
+
+U_BOOT_CMD_WITH_SUBCMDS(wdt, "Watchdog sub-system", wdt_help_text,
+ U_BOOT_SUBCMD_MKENT(list, 1, 1, do_wdt_list),
+ U_BOOT_SUBCMD_MKENT(dev, 2, 1, do_wdt_dev),
+ U_BOOT_SUBCMD_MKENT(start, 3, 1, do_wdt_start),
+ U_BOOT_SUBCMD_MKENT(stop, 1, 1, do_wdt_stop),
+ U_BOOT_SUBCMD_MKENT(reset, 1, 1, do_wdt_reset),
+ U_BOOT_SUBCMD_MKENT(expire, 2, 1, do_wdt_expire));
diff --git a/cmd/wol.c b/cmd/wol.c
new file mode 100644
index 00000000000..45d4ae3f719
--- /dev/null
+++ b/cmd/wol.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Lothar Felte, lothar.felten@gmail.com
+ */
+
+/*
+ * Wake-on-LAN support
+ */
+#include <command.h>
+#include <net.h>
+#include <vsprintf.h>
+
+#if defined(CONFIG_CMD_WOL)
+void wol_set_timeout(ulong);
+
+int do_wol(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ /* Validate arguments */
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ wol_set_timeout(simple_strtol(argv[1], NULL, 10) * 1000);
+ if (net_loop(WOL) < 0)
+ return CMD_RET_FAILURE;
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+ wol, 2, 1, do_wol,
+ "wait for an incoming wake-on-lan packet",
+ "Timeout"
+);
+#endif
diff --git a/cmd/x86/Makefile b/cmd/x86/Makefile
new file mode 100644
index 00000000000..5f3f5be2882
--- /dev/null
+++ b/cmd/x86/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_CMD_CBSYSINFO) += cbsysinfo.o
+obj-y += cpuid.o msr.o mtrr.o
+obj-$(CONFIG_CMD_CBCMOS) += cbcmos.o
+obj-$(CONFIG_CMD_EXCEPTION) += exception.o
+obj-$(CONFIG_USE_HOB) += hob.o
+obj-$(CONFIG_HAVE_FSP) += fsp.o
+obj-$(CONFIG_CMD_ZBOOT) += zboot.o
diff --git a/cmd/x86/cbcmos.c b/cmd/x86/cbcmos.c
new file mode 100644
index 00000000000..fe5582fbf51
--- /dev/null
+++ b/cmd/x86/cbcmos.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Support for booting from coreboot
+ *
+ * Copyright 2021 Google LLC
+ */
+
+#define LOG_CATEGORY UCLASS_RTC
+
+#include <command.h>
+#include <dm.h>
+#include <rtc.h>
+#include <asm/cb_sysinfo.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+const struct sysinfo_t *get_table(void)
+{
+ if (!gd->arch.coreboot_table) {
+ printf("No coreboot sysinfo table found\n");
+ return NULL;
+ }
+
+ return &lib_sysinfo;
+}
+
+static int calc_sum(struct udevice *dev, uint start_bit, uint bit_count)
+{
+ uint start_byte = start_bit / 8;
+ uint byte_count = bit_count / 8;
+ int ret, i;
+ uint sum;
+
+ log_debug("Calc sum from %x: %x bytes\n", start_byte, byte_count);
+ sum = 0;
+ for (i = 0; i < bit_count / 8; i++) {
+ ret = rtc_read8(dev, start_bit / 8 + i);
+ if (ret < 0)
+ return ret;
+ sum += ret;
+ }
+
+ return (sum & 0xff) << 8 | (sum & 0xff00) >> 8;
+}
+
+/**
+ * prep_cbcmos() - Prepare for a CMOS-RAM command
+ *
+ * @tab: coreboot table
+ * @devnum: RTC device name to use, or NULL for the first one
+ * @dep: Returns RTC device on success
+ * Return: calculated checksum for CMOS RAM or -ve on error
+ */
+static int prep_cbcmos(const struct sysinfo_t *tab, const char *devname,
+ struct udevice **devp)
+{
+ struct udevice *dev;
+ int ret;
+
+ if (!tab)
+ return CMD_RET_FAILURE;
+ if (devname)
+ ret = uclass_get_device_by_name(UCLASS_RTC, devname, &dev);
+ else
+ ret = uclass_first_device_err(UCLASS_RTC, &dev);
+ if (ret) {
+ printf("Failed to get RTC device: %dE\n", ret);
+ return ret;
+ }
+
+ ret = calc_sum(dev, tab->cmos_range_start,
+ tab->cmos_range_end + 1 - tab->cmos_range_start);
+ if (ret < 0) {
+ printf("Failed to read RTC device: %dE\n", ret);
+ return ret;
+ }
+ *devp = dev;
+
+ return ret;
+}
+
+static int do_cbcmos_check(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const struct sysinfo_t *tab = get_table();
+ struct udevice *dev;
+ u16 cur, sum;
+ int ret;
+
+ ret = prep_cbcmos(tab, argv[1], &dev);
+ if (ret < 0)
+ return CMD_RET_FAILURE;
+ sum = ret;
+
+ ret = rtc_read16(dev, tab->cmos_checksum_location / 8, &cur);
+ if (ret < 0) {
+ printf("Failed to read RTC device: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ if (sum != cur) {
+ printf("Checksum %04x error: calculated %04x\n", cur, sum);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_cbcmos_update(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const struct sysinfo_t *tab = get_table();
+ struct udevice *dev;
+ u16 sum;
+ int ret;
+
+ ret = prep_cbcmos(tab, argv[1], &dev);
+ if (ret < 0)
+ return CMD_RET_FAILURE;
+ sum = ret;
+
+ ret = rtc_write16(dev, tab->cmos_checksum_location / 8, sum);
+ if (ret < 0) {
+ printf("Failed to read RTC device: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ printf("Checksum %04x written\n", sum);
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(cbcmos,
+ "check - check CMOS RAM\n"
+ "cbcmos update - Update CMOS-RAM checksum";
+);
+
+U_BOOT_CMD_WITH_SUBCMDS(cbcmos, "coreboot CMOS RAM", cbcmos_help_text,
+ U_BOOT_SUBCMD_MKENT(check, 2, 1, do_cbcmos_check),
+ U_BOOT_SUBCMD_MKENT(update, 2, 1, do_cbcmos_update));
diff --git a/cmd/x86/cbsysinfo.c b/cmd/x86/cbsysinfo.c
new file mode 100644
index 00000000000..ea4d89616f6
--- /dev/null
+++ b/cmd/x86/cbsysinfo.c
@@ -0,0 +1,474 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <asm/cb_sysinfo.h>
+#include <command.h>
+#include <console.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static void cbprompt(const char *name)
+{
+ for (; *name == '>'; name++)
+ puts(" ");
+ printf("%-12s: ", name);
+}
+
+static void print_dec(const char *name, int value)
+{
+ cbprompt(name);
+ printf(value > 9 ? "0d%d\n" : "%d\n", value);
+}
+
+static void print_hex(const char *name, int value)
+{
+ cbprompt(name);
+ printf("%x\n", value);
+}
+
+static void print_addr(const char *name, ulong value)
+{
+ cbprompt(name);
+ printf("%08lx\n", value);
+}
+
+static void print_addr64(const char *name, u64 value)
+{
+ cbprompt(name);
+ printf("%16llx\n", value);
+}
+
+static void print_ptr(const char *name, const void *value)
+{
+ cbprompt(name);
+ printf("%p\n", value);
+}
+
+static void print_str(const char *name, const char *value)
+{
+ if (value) {
+ cbprompt(name);
+ printf("%s\n", value);
+ }
+}
+
+static void print_idx(const char *name, uint idx, const u8 *strings)
+{
+ const char *ptr;
+
+ cbprompt(name);
+ ptr = (char *)strings + idx;
+ printf("%d: %s\n", idx, ptr ? ptr : "(unknown)");
+}
+
+static const char *const cb_mem_name[] = {
+ NULL,
+ "ram",
+ "reserved",
+ "acpi",
+ "nvs",
+ "unusable",
+ "vendor",
+};
+
+static const char *get_mem_name(int tag)
+{
+ if (tag >= CB_MEM_RAM && tag <= CB_MEM_VENDOR_RSVD)
+ return cb_mem_name[tag];
+
+ if (tag == CB_MEM_TABLE)
+ return "table";
+
+ return "(unknown)";
+}
+
+static const struct timestamp_id_to_name {
+ uint id;
+ const char *name;
+} timestamp_ids[] = {
+ /* Marker to report base_time */
+ { 0, "1st timestamp" },
+ { TS_START_ROMSTAGE, "start of romstage" },
+ { TS_BEFORE_INITRAM, "before ram initialization" },
+ { TS_AFTER_INITRAM, "after ram initialization" },
+ { TS_END_ROMSTAGE, "end of romstage" },
+ { TS_START_VBOOT, "start of verified boot" },
+ { TS_END_VBOOT, "end of verified boot" },
+ { TS_START_COPYRAM, "starting to load ramstage" },
+ { TS_END_COPYRAM, "finished loading ramstage" },
+ { TS_START_RAMSTAGE, "start of ramstage" },
+ { TS_START_BOOTBLOCK, "start of bootblock" },
+ { TS_END_BOOTBLOCK, "end of bootblock" },
+ { TS_START_COPYROM, "starting to load romstage" },
+ { TS_END_COPYROM, "finished loading romstage" },
+ { TS_START_ULZMA, "starting LZMA decompress (ignore for x86)" },
+ { TS_END_ULZMA, "finished LZMA decompress (ignore for x86)" },
+ { TS_START_ULZ4F, "starting LZ4 decompress (ignore for x86)" },
+ { TS_END_ULZ4F, "finished LZ4 decompress (ignore for x86)" },
+ { TS_DEVICE_ENUMERATE, "device enumeration" },
+ { TS_DEVICE_CONFIGURE, "device configuration" },
+ { TS_DEVICE_ENABLE, "device enable" },
+ { TS_DEVICE_INITIALIZE, "device initialization" },
+ { TS_DEVICE_DONE, "device setup done" },
+ { TS_CBMEM_POST, "cbmem post" },
+ { TS_WRITE_TABLES, "write tables" },
+ { TS_FINALIZE_CHIPS, "finalize chips" },
+ { TS_LOAD_PAYLOAD, "load payload" },
+ { TS_ACPI_WAKE_JUMP, "ACPI wake jump" },
+ { TS_SELFBOOT_JUMP, "selfboot jump" },
+
+ { TS_START_COPYVER, "starting to load verstage" },
+ { TS_END_COPYVER, "finished loading verstage" },
+ { TS_START_TPMINIT, "starting to initialize TPM" },
+ { TS_END_TPMINIT, "finished TPM initialization" },
+ { TS_START_VERIFY_SLOT, "starting to verify keyblock/preamble (RSA)" },
+ { TS_END_VERIFY_SLOT, "finished verifying keyblock/preamble (RSA)" },
+ { TS_START_HASH_BODY, "starting to verify body (load+SHA2+RSA) " },
+ { TS_DONE_LOADING, "finished loading body (ignore for x86)" },
+ { TS_DONE_HASHING, "finished calculating body hash (SHA2)" },
+ { TS_END_HASH_BODY, "finished verifying body signature (RSA)" },
+
+ { TS_START_COPYVPD, "starting to load Chrome OS VPD" },
+ { TS_END_COPYVPD_RO, "finished loading Chrome OS VPD (RO)" },
+ { TS_END_COPYVPD_RW, "finished loading Chrome OS VPD (RW)" },
+
+ { TS_U_BOOT_INITTED, "U-Boot start" },
+ { TS_RO_PARAMS_INIT, "RO parameter init" },
+ { TS_RO_VB_INIT, "RO vboot init" },
+ { TS_RO_VB_SELECT_FIRMWARE, "RO vboot select firmware" },
+ { TS_RO_VB_SELECT_AND_LOAD_KERNEL, "RO vboot select&load kernel" },
+ { TS_RW_VB_SELECT_AND_LOAD_KERNEL, "RW vboot select&load kernel" },
+ { TS_VB_SELECT_AND_LOAD_KERNEL, "vboot select&load kernel" },
+ { TS_VB_EC_VBOOT_DONE, "finished EC verification" },
+ { TS_VB_STORAGE_INIT_DONE, "finished storage device initialization" },
+ { TS_VB_READ_KERNEL_DONE, "finished reading kernel from disk" },
+ { TS_VB_VBOOT_DONE, "finished vboot kernel verification" },
+ { TS_KERNEL_DECOMPRESSION, "starting kernel decompression/relocation" },
+ { TS_START_KERNEL, "jumping to kernel" },
+ { TS_U_BOOT_START_KERNEL, "just before jump to kernel" },
+
+ /* Intel ME-related timestamps */
+ { TS_ME_INFORM_DRAM_WAIT, "waiting for ME acknowledgment of raminit"},
+ { TS_ME_INFORM_DRAM_DONE, "finished waiting for ME response"},
+
+ /* FSP-related timestamps */
+ { TS_FSP_MEMORY_INIT_START, "calling FspMemoryInit" },
+ { TS_FSP_MEMORY_INIT_END, "returning from FspMemoryInit" },
+ { TS_FSP_TEMP_RAM_EXIT_START, "calling FspTempRamExit" },
+ { TS_FSP_TEMP_RAM_EXIT_END, "returning from FspTempRamExit" },
+ { TS_FSP_SILICON_INIT_START, "calling FspSiliconInit" },
+ { TS_FSP_SILICON_INIT_END, "returning from FspSiliconInit" },
+ { TS_FSP_BEFORE_ENUMERATE, "calling FspNotify(AfterPciEnumeration)" },
+ { TS_FSP_AFTER_ENUMERATE,
+ "returning from FspNotify(AfterPciEnumeration)" },
+ { TS_FSP_BEFORE_FINALIZE, "calling FspNotify(ReadyToBoot)" },
+ { TS_FSP_AFTER_FINALIZE, "returning from FspNotify(ReadyToBoot)" },
+ { TS_FSP_BEFORE_END_OF_FIRMWARE, "calling FspNotify(EndOfFirmware)" },
+ { TS_FSP_AFTER_END_OF_FIRMWARE,
+ "returning from FspNotify(EndOfFirmware)" },
+};
+
+static const char *timestamp_name(uint32_t id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
+ if (timestamp_ids[i].id == id)
+ return timestamp_ids[i].name;
+ }
+
+ return "<unknown>";
+}
+
+static void show_option_vals(const struct cb_cmos_option_table *tab,
+ uint id)
+{
+ const void *ptr, *end;
+ bool found = false;
+
+ end = (void *)tab + tab->size;
+ for (ptr = (void *)tab + tab->header_length; ptr < end;) {
+ const struct cb_record *rec = ptr;
+
+ switch (rec->tag) {
+ case CB_TAG_OPTION_ENUM: {
+ const struct cb_cmos_enums *enums = ptr;
+
+ if (enums->config_id == id) {
+ if (!found)
+ printf(" ");
+ printf(" %d:%s", enums->value, enums->text);
+ found = true;
+ }
+ break;
+ }
+ break;
+ case CB_TAG_OPTION_DEFAULTS:
+ case CB_TAG_OPTION_CHECKSUM:
+ case CB_TAG_OPTION:
+ break;
+ default:
+ printf("tag %x\n", rec->tag);
+ break;
+ }
+ ptr += rec->size;
+ }
+}
+
+static void show_option_table(const struct cb_cmos_option_table *tab)
+{
+ const void *ptr, *end;
+
+ print_ptr("option_table", tab);
+ if (!tab->size)
+ return;
+
+ printf(" Bit Len Cfg ID Name\n");
+ end = (void *)tab + tab->size;
+ for (ptr = (void *)tab + tab->header_length; ptr < end;) {
+ const struct cb_record *rec = ptr;
+
+ switch (rec->tag) {
+ case CB_TAG_OPTION: {
+ const struct cb_cmos_entries *entry = ptr;
+
+ printf("%4x %4x %3c %3x %-20s", entry->bit,
+ entry->length, entry->config, entry->config_id,
+ entry->name);
+ show_option_vals(tab, entry->config_id);
+ printf("\n");
+ break;
+ }
+ case CB_TAG_OPTION_ENUM:
+ case CB_TAG_OPTION_DEFAULTS:
+ case CB_TAG_OPTION_CHECKSUM:
+ break;
+ default:
+ printf("tag %x\n", rec->tag);
+ break;
+ }
+ ptr += rec->size;
+ }
+}
+
+static void show_table(struct sysinfo_t *info, bool verbose)
+{
+ struct cb_serial *ser = info->serial;
+ int i;
+
+ printf("Coreboot table at %lx, size %x, records %x (dec %d), decoded to %p",
+ gd->arch.coreboot_table, info->table_size, info->rec_count,
+ info->rec_count, info);
+ if (info->header)
+ printf(", forwarded to %p\n", info->header);
+ printf("\n");
+
+ print_dec("CPU KHz", info->cpu_khz);
+
+ print_addr("Serial I/O port", info->ser_ioport);
+ print_addr(">base", info->ser_base);
+ print_ptr(">pointer", ser);
+ if (ser) {
+ print_hex(">type", ser->type);
+ print_addr(">base", ser->baseaddr);
+ print_dec(">baud", ser->baud);
+ print_hex(">regwidth", ser->regwidth);
+ print_dec(">input_hz", ser->input_hertz);
+ print_addr(">PCI addr", ser->uart_pci_addr);
+ }
+
+ print_dec("Mem ranges", info->n_memranges);
+ printf("%12s: %-11s || base || size\n", "id", "type");
+ for (i = 0; i < info->n_memranges; i++) {
+ const struct memrange *mr = &info->memrange[i];
+
+ printf("%12d: %02x:%-8s %016llx %016llx\n", i, mr->type,
+ get_mem_name(mr->type), mr->base, mr->size);
+ }
+ show_option_table(info->option_table);
+
+ print_hex("CMOS start", info->cmos_range_start);
+ if (info->cmos_range_start) {
+ print_hex(">CMOS end", info->cmos_range_end);
+ print_hex(">CMOS csum loc", info->cmos_checksum_location);
+ }
+
+ print_hex("VBNV start", info->vbnv_start);
+ print_hex("VBNV size", info->vbnv_size);
+
+ print_str("CB version", info->cb_version);
+ print_str(">Extra", info->extra_version);
+ print_str(">Build", info->build);
+ print_str(">Time", info->compile_time);
+ print_str(">By", info->compile_by);
+ print_str(">Host", info->compile_host);
+ print_str(">Domain", info->compile_domain);
+ print_str(">Compiler", info->compiler);
+ print_str(">Linker", info->linker);
+ print_str(">Assembler", info->assembler);
+
+ print_ptr("Framebuffer", info->framebuffer);
+ if (info->framebuffer) {
+ struct cb_framebuffer *fb = info->framebuffer;
+
+ print_addr64(">Phys addr", fb->physical_address);
+ print_dec(">X res", fb->x_resolution);
+ print_dec(">X res", fb->y_resolution);
+ print_hex(">Bytes / line", fb->bytes_per_line);
+ print_dec(">Bpp", fb->bits_per_pixel);
+ printf(" %-12s red %d/%d, green %d/%d, blue %d/%d, reserved %d/%d\n",
+ "pos/size", fb->red_mask_pos, fb->red_mask_size,
+ fb->green_mask_pos, fb->green_mask_size,
+ fb->blue_mask_pos, fb->blue_mask_size,
+ fb->reserved_mask_pos, fb->reserved_mask_size);
+ }
+
+ print_dec("GPIOs", info->num_gpios);
+ printf("%12s: %4s %12s %3s %s\n", "id", "port", "polarity", "val",
+ "name");
+ for (i = 0; i < info->num_gpios; i++) {
+ const struct cb_gpio *gpio = &info->gpios[i];
+ char portstr[4];
+
+ if (gpio->port == 0xffffffff)
+ strcpy(portstr, "-");
+ else
+ sprintf(portstr, "%x", gpio->port);
+ printf("%12d: %4s %12s %3d %s\n", i, portstr,
+ gpio->polarity == CB_GPIO_ACTIVE_LOW ? "active-low" :
+ "active-high", gpio->value, gpio->name);
+ }
+ print_dec("MACs", info->num_macs);
+ for (i = 0; i < info->num_macs; i++) {
+ const struct mac_address *mac = &info->macs[i];
+ int j;
+
+ printf("%12d: ", i);
+ for (j = 0; j < sizeof(mac->mac_addr); j++)
+ printf("%s%02x", j ? ":" : "", mac->mac_addr[j]);
+ printf("\n");
+ }
+ print_str(">Serial #", info->serialno);
+ print_ptr("Multiboot tab", info->mbtable);
+ print_ptr("CB header", info->header);
+ print_ptr("CB mainboard", info->mainboard);
+ if (info->mainboard) {
+ struct cb_mainboard *mb = info->mainboard;
+
+ print_idx(">vendor", mb->vendor_idx, mb->strings);
+ print_idx(">part_number", mb->part_number_idx, mb->strings);
+ }
+ print_ptr("vboot handoff", info->vboot_handoff);
+ print_hex(">size", info->vboot_handoff_size);
+ print_ptr(">vdat addr", info->vdat_addr);
+ print_hex(">size", info->vdat_size);
+
+ print_addr64("SMBIOS", info->smbios_start);
+ print_hex(">size", info->smbios_size);
+ print_hex("ROM MTRR", info->x86_rom_var_mtrr_index);
+
+ print_ptr("Tstamp table", info->tstamp_table);
+ if (verbose && info->tstamp_table) {
+ struct timestamp_table *ts = info->tstamp_table;
+
+ printf("%-12s", "Base_time");
+ print_grouped_ull(ts->base_time, 12);
+ printf("\n");
+ print_dec("Tick MHz", ts->tick_freq_mhz);
+ for (i = 0; i < ts->num_entries; i++) {
+ const struct timestamp_entry *tse;
+
+ tse = &ts->entries[i];
+ printf(" ");
+ print_grouped_ull(tse->entry_stamp, 12);
+ printf(" %s\n", timestamp_name(tse->entry_id));
+ }
+ }
+
+ print_ptr("CBmem cons", info->cbmem_cons);
+ if (info->cbmem_cons) {
+ struct cbmem_console *cons = info->cbmem_cons;
+ int i;
+
+ print_hex("Size", cons->size);
+ print_hex("Cursor", cons->cursor);
+ if (verbose) {
+ for (i = 0; i < cons->cursor; i++) {
+ int ch = cons->body[i];
+
+ putc(ch);
+
+ if (ch == '\n') {
+ /* check for ctrl-c to abort... */
+ if (ctrlc()) {
+ puts("Abort\n");
+ return;
+ }
+ printf(" ");
+ }
+ }
+ printf("\n");
+ }
+ }
+
+ print_ptr("MRC cache", info->mrc_cache);
+ print_ptr("ACPI GNVS", info->acpi_gnvs);
+ print_hex("Board ID", info->board_id);
+ print_hex("RAM code", info->ram_code);
+ print_ptr("WiFi calib", info->wifi_calibration);
+ print_addr64("Ramoops buff", info->ramoops_buffer);
+ print_hex(">size", info->ramoops_buffer_size);
+ print_hex("SF size", info->spi_flash.size);
+ print_hex("SF sector", info->spi_flash.sector_size);
+ print_hex("SF erase cmd", info->spi_flash.erase_cmd);
+
+ print_addr64("FMAP offset", info->fmap_offset);
+ print_addr64("CBFS offset", info->cbfs_offset);
+ print_addr64("CBFS size", info->cbfs_size);
+ print_addr64("Boot media size", info->boot_media_size);
+ print_addr64("MTC start", info->mtc_start);
+ print_hex("MTC size", info->mtc_size);
+
+ print_ptr("Chrome OS VPD", info->chromeos_vpd);
+ print_ptr("RSDP", info->rsdp);
+ printf("%-12s: ", "Unimpl.");
+ if (info->unimpl_count) {
+ for (i = 0; i < info->unimpl_count; i++)
+ printf("%02x ", info->unimpl[i]);
+ printf("\n");
+ } else {
+ printf("(none)\n");
+ }
+}
+
+static int do_cbsysinfo(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool verbose = false;
+
+ if (argc > 1) {
+ if (!strcmp("-v", argv[1]))
+ verbose = true;
+ else
+ return CMD_RET_USAGE;
+ }
+
+ if (!gd->arch.coreboot_table) {
+ printf("No coreboot sysinfo table found\n");
+ return CMD_RET_FAILURE;
+ }
+ show_table(&lib_sysinfo, verbose);
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ cbsysinfo, 2, 1, do_cbsysinfo,
+ "Show coreboot sysinfo table",
+ "[-v] Dumps out the contents of the sysinfo table. This only\n"
+ "works if U-Boot is booted from coreboot"
+);
diff --git a/cmd/x86/cpuid.c b/cmd/x86/cpuid.c
new file mode 100644
index 00000000000..222754b5d15
--- /dev/null
+++ b/cmd/x86/cpuid.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The 'cpuid' command provides access to the CPU's cpuid information
+ *
+ * Copyright 2024 Google, LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <command.h>
+#include <vsprintf.h>
+#include <asm/cpu.h>
+
+static int do_cpuid(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cpuid_result res;
+ ulong op;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ op = hextoul(argv[1], NULL);
+ res = cpuid(op);
+ printf("eax %08x\n", res.eax);
+ printf("ebx %08x\n", res.ebx);
+ printf("ecx %08x\n", res.ecx);
+ printf("edx %08x\n", res.edx);
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(cpuid, "Show CPU Identification information");
+
+U_BOOT_CMD(
+ cpuid, 2, 1, do_cpuid,
+ "cpuid <op>", cpuid_help_text
+);
diff --git a/cmd/x86/exception.c b/cmd/x86/exception.c
new file mode 100644
index 00000000000..02735494a3c
--- /dev/null
+++ b/cmd/x86/exception.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The 'exception' command can be used for testing exception handling.
+ *
+ * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <command.h>
+
+static int do_undefined(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ asm volatile (".word 0xffff\n");
+ return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl cmd_sub[] = {
+ U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined,
+ "", ""),
+};
+
+U_BOOT_LONGHELP(exception,
+ "<ex>\n"
+ " The following exceptions are available:\n"
+ " undefined - undefined instruction\n");
+
+#include <exception.h>
diff --git a/cmd/x86/fsp.c b/cmd/x86/fsp.c
new file mode 100644
index 00000000000..2620ab8ee02
--- /dev/null
+++ b/cmd/x86/fsp.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014-2015, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <command.h>
+#include <asm/fsp/fsp_support.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int do_hdr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct fsp_header *hdr;
+ u32 img_addr;
+ char *sign;
+ uint addr;
+ int i;
+
+#ifdef CONFIG_FSP_VERSION2
+ /*
+ * Only FSP-S is displayed. FSP-M was used in SPL but may not still be
+ * around, and we didn't keep a pointer to it.
+ */
+ hdr = gd->arch.fsp_s_hdr;
+ img_addr = hdr->img_base;
+ addr = img_addr;
+#else
+ addr = CONFIG_FSP_ADDR;
+ hdr = fsp_find_header();
+ img_addr = hdr->img_base;
+#endif
+ sign = (char *)&hdr->sign;
+
+ printf("FSP : binary %08x, header %08x\n", addr, (int)hdr);
+ printf("Header : sign ");
+ for (i = 0; i < sizeof(hdr->sign); i++)
+ printf("%c", *sign++);
+ printf(", size %x, rev %d\n", hdr->hdr_len, hdr->hdr_rev);
+ printf("Image : rev ");
+ if (hdr->hdr_rev == FSP_HEADER_REVISION_1) {
+ printf("%d.%d",
+ (hdr->img_rev >> 8) & 0xff, hdr->img_rev & 0xff);
+ } else {
+ printf("%d.%d.%d.%d",
+ (hdr->img_rev >> 24) & 0xff, (hdr->img_rev >> 16) & 0xff,
+ (hdr->img_rev >> 8) & 0xff, hdr->img_rev & 0xff);
+ }
+ printf(", id ");
+ for (i = 0; i < ARRAY_SIZE(hdr->img_id); i++)
+ printf("%c", hdr->img_id[i]);
+ printf(", addr %08x, size %x\n", img_addr, hdr->img_size);
+ if (hdr->hdr_rev >= FSP_HEADER_REVISION_1) {
+ printf("GFX :%ssupported\n",
+ hdr->img_attr & FSP_ATTR_GRAPHICS_SUPPORT ? " " : " un");
+ }
+ printf("VPD : addr %08x, size %x\n",
+ hdr->cfg_region_off + img_addr, hdr->cfg_region_size);
+ if (hdr->hdr_rev <= FSP_HEADER_REVISION_2)
+ printf("\nNumber of APIs Supported : %d\n", hdr->api_num);
+ if (hdr->fsp_tempram_init)
+ printf("\tTempRamInit : %08x\n",
+ hdr->fsp_tempram_init + img_addr);
+ if (hdr->fsp_init)
+ printf("\tFspInit : %08x\n", hdr->fsp_init + img_addr);
+ if (hdr->fsp_notify)
+ printf("\tFspNotify : %08x\n", hdr->fsp_notify + img_addr);
+ if (hdr->hdr_rev >= FSP_HEADER_REVISION_1) {
+ if (hdr->fsp_mem_init)
+ printf("\tMemoryInit : %08x\n",
+ hdr->fsp_mem_init + img_addr);
+ if (hdr->fsp_tempram_exit)
+ printf("\tTempRamExit : %08x\n",
+ hdr->fsp_tempram_exit + img_addr);
+ if (hdr->fsp_silicon_init)
+ printf("\tSiliconInit : %08x\n",
+ hdr->fsp_silicon_init + img_addr);
+ }
+
+ return 0;
+}
+
+static struct cmd_tbl fsp_commands[] = {
+ U_BOOT_CMD_MKENT(hdr, 0, 1, do_hdr, "", ""),
+};
+
+static int do_fsp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct cmd_tbl *fsp_cmd;
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ fsp_cmd = find_cmd_tbl(argv[1], fsp_commands, ARRAY_SIZE(fsp_commands));
+ argc -= 2;
+ argv += 2;
+ if (!fsp_cmd || argc > fsp_cmd->maxargs)
+ return CMD_RET_USAGE;
+
+ ret = fsp_cmd->cmd(fsp_cmd, flag, argc, argv);
+
+ return cmd_process_error(fsp_cmd, ret);
+}
+
+U_BOOT_CMD(
+ fsp, 2, 1, do_fsp,
+ "Show Intel Firmware Support Package (FSP) related information",
+ "hdr - Print FSP header information"
+);
diff --git a/cmd/x86/hob.c b/cmd/x86/hob.c
new file mode 100644
index 00000000000..d3713cef331
--- /dev/null
+++ b/cmd/x86/hob.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014-2015, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <command.h>
+#include <efi.h>
+#include <u-boot/uuid.h>
+#include <asm/global_data.h>
+#include <asm/hob.h>
+#include <asm/fsp/fsp_hob.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static char *hob_type[] = {
+ "reserved",
+ "Hand-off",
+ "Mem Alloc",
+ "Res Desc",
+ "GUID Ext",
+ "FV",
+ "CPU",
+ "Mem Pool",
+ "reserved",
+ "FV2",
+ "Load PEIM",
+ "Capsule",
+};
+
+static char *res_type[] = {
+ "System",
+ "Memory-mapped I/O",
+ "I/O",
+ "Firmware device",
+ "Memory-mapped I/O port",
+ "Reserved",
+ "I/O reserved",
+};
+
+static struct guid_name {
+ efi_guid_t guid;
+ const char *name;
+} guid_name[] = {
+ { FSP_HOB_RESOURCE_OWNER_TSEG_GUID, "TSEG" },
+ { FSP_HOB_RESOURCE_OWNER_FSP_GUID, "FSP" },
+ { FSP_HOB_RESOURCE_OWNER_SMM_PEI_SMRAM_GUID, "SMM PEI SMRAM" },
+ { FSP_NON_VOLATILE_STORAGE_HOB_GUID, "NVS" },
+ { FSP_VARIABLE_NV_DATA_HOB_GUID, "Variable NVS" },
+ { FSP_GRAPHICS_INFO_HOB_GUID, "Graphics info" },
+ { FSP_HOB_RESOURCE_OWNER_PCD_DATABASE_GUID1, "PCD database ea" },
+ { FSP_HOB_RESOURCE_OWNER_PCD_DATABASE_GUID2, "PCD database 9b" },
+ { FSP_HOB_RESOURCE_OWNER_PEIM_DXE_GUID, "PEIM Init DXE" },
+ { FSP_HOB_RESOURCE_OWNER_ALLOC_STACK_GUID, "Alloc stack" },
+ { FSP_HOB_RESOURCE_OWNER_SMBIOS_MEMORY_GUID, "SMBIOS memory" },
+ { {}, "zero-guid" },
+ {}
+};
+
+static const char *guid_to_name(const efi_guid_t *guid)
+{
+ struct guid_name *entry;
+
+ for (entry = guid_name; entry->name; entry++) {
+ if (!guidcmp(guid, &entry->guid))
+ return entry->name;
+ }
+
+ return NULL;
+}
+
+static void show_hob_details(const struct hob_header *hdr)
+{
+ const void *ptr = hdr;
+
+ switch (hdr->type) {
+ case HOB_TYPE_RES_DESC: {
+ const struct hob_res_desc *res = ptr;
+ const char *typename;
+
+ typename = res->type >= RES_SYS_MEM && res->type <= RES_MAX_MEM_TYPE ?
+ res_type[res->type] : "unknown";
+
+ printf(" base = %08llx, len = %08llx, end = %08llx, type = %d (%s)\n\n",
+ res->phys_start, res->len, res->phys_start + res->len,
+ res->type, typename);
+ break;
+ }
+ }
+}
+
+static int do_hob(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ const struct hob_header *hdr;
+ uint type;
+ char *desc;
+ int i = 0;
+ efi_guid_t *guid;
+ char uuid[UUID_STR_LEN + 1];
+ bool verbose = false;
+ int seq = -1; /* Show all by default */
+
+ argc--;
+ argv++;
+ if (argc) {
+ if (!strcmp("-v", *argv)) {
+ verbose = true;
+ argc--;
+ argv++;
+ }
+ if (argc)
+ seq = simple_strtol(*argv, NULL, 16);
+ }
+ hdr = gd->arch.hob_list;
+
+ printf("HOB list address: 0x%08x\n\n", (unsigned int)hdr);
+
+ printf("# | Address | Type | Len | ");
+ printf("%36s\n", "GUID");
+ printf("---|----------|-----------|------|-");
+ printf("------------------------------------\n");
+ for (i = 0; !end_of_hob(hdr); i++, hdr = get_next_hob(hdr)) {
+ if (seq != -1 && seq != i)
+ continue;
+ printf("%02x | %08x | ", i, (unsigned int)hdr);
+ type = hdr->type;
+ if (type == HOB_TYPE_UNUSED)
+ desc = "*Unused*";
+ else if (type == HOB_TYPE_EOH)
+ desc = "*EOH*";
+ else if (type >= 0 && type <= ARRAY_SIZE(hob_type))
+ desc = hob_type[type];
+ else
+ desc = "*Invalid*";
+ printf("%-9s | %04x | ", desc, hdr->len);
+
+ if (type == HOB_TYPE_MEM_ALLOC || type == HOB_TYPE_RES_DESC ||
+ type == HOB_TYPE_GUID_EXT) {
+ const char *name;
+
+ guid = (efi_guid_t *)(hdr + 1);
+ name = guid_to_name(guid);
+ if (!name) {
+ uuid_bin_to_str(guid->b, uuid,
+ UUID_STR_FORMAT_GUID);
+ name = uuid;
+ }
+ printf("%36s", name);
+ } else {
+ printf("%36s", "Not Available");
+ }
+ printf("\n");
+ if (verbose)
+ show_hob_details(hdr);
+ }
+
+ return 0;
+}
+
+U_BOOT_CMD(hob, 3, 1, do_hob,
+ "[-v] [seq] Print Hand-Off Block (HOB) information",
+ " -v - Show detailed HOB information where available\n"
+ " seq - Record # to show (all by default)"
+);
diff --git a/cmd/x86/msr.c b/cmd/x86/msr.c
new file mode 100644
index 00000000000..3cb70d1f89a
--- /dev/null
+++ b/cmd/x86/msr.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The 'cpuid' command provides access to the CPU's cpuid information
+ *
+ * Copyright 2024 Google, LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <command.h>
+#include <vsprintf.h>
+#include <asm/msr.h>
+
+static int do_read(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct msr_t msr;
+ ulong op;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ op = hextoul(argv[1], NULL);
+ msr = msr_read(op);
+ printf("%08x %08x\n", msr.hi, msr.lo);
+
+ return 0;
+}
+
+static int do_write(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct msr_t msr;
+ ulong op;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ op = hextoul(argv[1], NULL);
+ msr.hi = hextoul(argv[2], NULL);
+ msr.lo = hextoul(argv[3], NULL);
+ msr_write(op, msr);
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(msr,
+ "read <op> - read a machine-status register (MSR) as <hi 32-bits> <lo 32-bits>\n"
+ "write <op< <hi> <lo> - write an MSR");
+
+U_BOOT_CMD_WITH_SUBCMDS(msr, "Machine Status Registers", msr_help_text,
+ U_BOOT_CMD_MKENT(read, CONFIG_SYS_MAXARGS, 1, do_read, "", ""),
+ U_BOOT_CMD_MKENT(write, CONFIG_SYS_MAXARGS, 1, do_write, "", ""));
diff --git a/cmd/x86/mtrr.c b/cmd/x86/mtrr.c
new file mode 100644
index 00000000000..91130453039
--- /dev/null
+++ b/cmd/x86/mtrr.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2014 Google, Inc
+ */
+
+#include <command.h>
+#include <log.h>
+#include <vsprintf.h>
+#include <linux/string.h>
+#include <asm/msr.h>
+#include <asm/mp.h>
+#include <asm/mtrr.h>
+
+static int do_mtrr_set(int cpu_select, uint reg, int argc, char *const argv[])
+{
+ const char *typename = argv[0];
+ u64 start, size;
+ u64 base, mask;
+ int type = -1;
+ bool valid;
+ int ret;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+ type = mtrr_get_type_by_name(typename);
+ if (type < 0) {
+ printf("Invalid type name %s\n", typename);
+ return CMD_RET_USAGE;
+ }
+ start = hextoull(argv[1], NULL);
+ size = hextoull(argv[2], NULL);
+
+ base = start | type;
+ valid = native_read_msr(MTRR_PHYS_MASK_MSR(reg)) & MTRR_PHYS_MASK_VALID;
+ mask = mtrr_to_mask(size);
+ if (valid)
+ mask |= MTRR_PHYS_MASK_VALID;
+
+ ret = mtrr_set(cpu_select, reg, base, mask);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+static int do_mtrr(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int reg_count = mtrr_get_var_count();
+ int cmd;
+ int cpu_select;
+ uint reg;
+ int ret;
+
+ cpu_select = MP_SELECT_BSP;
+ if (argc >= 3 && !strcmp("-c", argv[1])) {
+ const char *cpustr;
+
+ cpustr = argv[2];
+ if (*cpustr == 'a')
+ cpu_select = MP_SELECT_ALL;
+ else
+ cpu_select = simple_strtol(cpustr, NULL, 16);
+ argc -= 2;
+ argv += 2;
+ }
+ argc--;
+ argv++;
+ cmd = argv[0] ? *argv[0] : 0;
+ if (argc < 1 || !cmd) {
+ cmd = 'l';
+ reg = 0;
+ }
+ if (cmd != 'l') {
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ reg = hextoul(argv[1], NULL);
+ if (reg >= reg_count) {
+ printf("Invalid register number\n");
+ return CMD_RET_USAGE;
+ }
+ }
+ if (cmd == 'l') {
+ bool first;
+ int i;
+
+ i = mp_first_cpu(cpu_select);
+ if (i < 0) {
+ printf("Invalid CPU (err=%d)\n", i);
+ return CMD_RET_FAILURE;
+ }
+ first = true;
+ for (; i >= 0; i = mp_next_cpu(cpu_select, i)) {
+ if (!first)
+ printf("\n");
+ printf("CPU %d:\n", i);
+ ret = mtrr_list(reg_count, i);
+ if (ret) {
+ printf("Failed to read CPU %s (err=%d)\n",
+ i < MP_SELECT_ALL ? simple_itoa(i) : "",
+ ret);
+ return CMD_RET_FAILURE;
+ }
+ first = false;
+ }
+ } else {
+ switch (cmd) {
+ case 'e':
+ ret = mtrr_set_valid(cpu_select, reg, true);
+ break;
+ case 'd':
+ ret = mtrr_set_valid(cpu_select, reg, false);
+ break;
+ case 's':
+ ret = do_mtrr_set(cpu_select, reg, argc - 2, argv + 2);
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ if (ret) {
+ printf("Operation failed (err=%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ mtrr, 8, 1, do_mtrr,
+ "Use x86 memory type range registers (32-bit only)",
+ "[list] - list current registers\n"
+ "set <reg> <type> <start> <size> - set a register\n"
+ "\t<type> is Uncacheable, Combine, Through, Protect, Back\n"
+ "disable <reg> - disable a register\n"
+ "enable <reg> - enable a register\n"
+ "\n"
+ "Precede command with '-c <n>|all' to access a particular hex CPU, e.g.\n"
+ " mtrr -c all list; mtrr -c 2e list"
+);
diff --git a/cmd/x86/zboot.c b/cmd/x86/zboot.c
new file mode 100644
index 00000000000..3876d163236
--- /dev/null
+++ b/cmd/x86/zboot.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * (C) Copyright 2002
+ * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
+ */
+
+#define LOG_CATEGORY LOGC_BOOT
+
+#include <command.h>
+#include <env.h>
+#include <mapmem.h>
+#include <vsprintf.h>
+#include <asm/zimage.h>
+
+static int do_zboot_start(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong bzimage_addr = 0, bzimage_size, initrd_addr, initrd_size;
+ const char *s, *cmdline;
+ ulong base_addr;
+ int i;
+
+ log_debug("argc %d:", argc);
+ for (i = 0; i < argc; i++)
+ log_debug(" %s", argv[i]);
+ log_debug("\n");
+
+ /* argv[1] holds the address of the bzImage */
+ s = cmd_arg1(argc, argv) ? : env_get("fileaddr");
+ if (s)
+ bzimage_addr = hextoul(s, NULL);
+ bzimage_size = argc > 2 ? hextoul(argv[2], NULL) : 0;
+ initrd_addr = argc > 3 ? hextoul(argv[3], NULL) : 0;
+ initrd_size = argc > 4 ? hextoul(argv[4], NULL) : 0;
+ base_addr = argc > 5 ? hextoul(argv[5], NULL) : 0;
+ cmdline = argc > 6 ? env_get(argv[6]) : NULL;
+
+ zboot_start(bzimage_addr, bzimage_size, initrd_addr, initrd_size,
+ base_addr, cmdline);
+
+ return 0;
+}
+
+static int do_zboot_load(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+
+ ret = zboot_load();
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int do_zboot_setup(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ if (!state.base_ptr) {
+ printf("base is not set: use 'zboot load' first\n");
+ return CMD_RET_FAILURE;
+ }
+ if (zboot_setup()) {
+ puts("Setting up boot parameters failed ...\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (zboot_setup())
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+static int do_zboot_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ zboot_info();
+
+ return 0;
+}
+
+static int do_zboot_go(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int ret;
+
+ ret = zboot_go();
+ if (ret) {
+ printf("Kernel returned! (err=%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_zboot_dump(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct boot_params *base_ptr = state.base_ptr;
+
+ if (argc > 1)
+ base_ptr = (void *)hextoul(argv[1], NULL);
+ if (!base_ptr) {
+ printf("No zboot setup_base\n");
+ return CMD_RET_FAILURE;
+ }
+ zimage_dump(base_ptr, true);
+
+ return 0;
+}
+
+/* Note: This defines the complete_zboot() function */
+U_BOOT_SUBCMDS(zboot,
+ U_BOOT_CMD_MKENT(start, 8, 1, do_zboot_start, "", ""),
+ U_BOOT_CMD_MKENT(load, 1, 1, do_zboot_load, "", ""),
+ U_BOOT_CMD_MKENT(setup, 1, 1, do_zboot_setup, "", ""),
+ U_BOOT_CMD_MKENT(info, 1, 1, do_zboot_info, "", ""),
+ U_BOOT_CMD_MKENT(go, 1, 1, do_zboot_go, "", ""),
+ U_BOOT_CMD_MKENT(dump, 2, 1, do_zboot_dump, "", ""),
+)
+
+int do_zboot_states(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[], int state_mask)
+{
+ int ret = 0;
+
+ log_debug("state_mask %x\n", state_mask);
+ if (state_mask & ZBOOT_STATE_START)
+ ret = do_zboot_start(cmdtp, flag, argc, argv);
+ if (!ret && (state_mask & ZBOOT_STATE_LOAD))
+ ret = do_zboot_load(cmdtp, flag, argc, argv);
+ if (!ret && (state_mask & ZBOOT_STATE_SETUP))
+ ret = do_zboot_setup(cmdtp, flag, argc, argv);
+ if (!ret && (state_mask & ZBOOT_STATE_INFO))
+ ret = do_zboot_info(cmdtp, flag, argc, argv);
+ if (!ret && (state_mask & ZBOOT_STATE_GO))
+ ret = do_zboot_go(cmdtp, flag, argc, argv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int do_zboot_parent(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[], int *repeatable)
+{
+ /* determine if we have a sub command */
+ if (argc > 1) {
+ char *endp;
+
+ hextoul(argv[1], &endp);
+ /*
+ * endp pointing to nul means that argv[1] was just a valid
+ * number, so pass it along to the normal processing
+ */
+ if (*endp)
+ return do_zboot(cmdtp, flag, argc, argv, repeatable);
+ }
+
+ do_zboot_states(cmdtp, flag, argc, argv, ZBOOT_STATE_START |
+ ZBOOT_STATE_LOAD | ZBOOT_STATE_SETUP |
+ ZBOOT_STATE_INFO | ZBOOT_STATE_GO);
+
+ return CMD_RET_FAILURE;
+}
+
+U_BOOT_CMDREP_COMPLETE(
+ zboot, 8, do_zboot_parent, "Boot bzImage",
+ "[addr] [size] [initrd addr] [initrd size] [setup] [cmdline]\n"
+ " addr - The optional starting address of the bzimage.\n"
+ " If not set it defaults to the environment\n"
+ " variable \"fileaddr\".\n"
+ " size - The optional size of the bzimage. Defaults to\n"
+ " zero.\n"
+ " initrd addr - The address of the initrd image to use, if any.\n"
+ " initrd size - The size of the initrd image to use, if any.\n"
+ " setup - The address of the kernel setup region, if this\n"
+ " is not at addr\n"
+ " cmdline - Environment variable containing the kernel\n"
+ " command line, to override U-Boot's normal\n"
+ " cmdline generation\n"
+ "\n"
+ "Sub-commands to do part of the zboot sequence:\n"
+ "\tstart [addr [arg ...]] - specify arguments\n"
+ "\tload - load OS image\n"
+ "\tsetup - set up table\n"
+ "\tinfo - show summary info\n"
+ "\tgo - start OS\n"
+ "\tdump [addr] - dump info (optional address of boot params)",
+ complete_zboot
+);
diff --git a/cmd/ximg.c b/cmd/ximg.c
new file mode 100644
index 00000000000..e97167a79cc
--- /dev/null
+++ b/cmd/ximg.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000-2004
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2003
+ * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de>
+ */
+
+/*
+ * Multi Image extract
+ */
+#include <command.h>
+#include <cpu_func.h>
+#include <env.h>
+#include <gzip.h>
+#if IS_ENABLED(CONFIG_ZSTD)
+#include <linux/zstd.h>
+#endif
+#include <image.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <watchdog.h>
+#if defined(CONFIG_BZIP2)
+#include <bzlib.h>
+#endif
+#include <asm/byteorder.h>
+#include <asm/cache.h>
+#include <asm/io.h>
+#include <u-boot/zlib.h>
+
+static int
+do_imgextract(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ ulong addr = image_load_addr;
+ ulong dest = 0;
+ ulong data, len;
+ int verify;
+ int part = 0;
+#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
+ ulong count;
+ struct legacy_img_hdr *hdr = NULL;
+#endif
+#if defined(CONFIG_FIT)
+ const char *uname = NULL;
+ const void* fit_hdr;
+ int noffset;
+ const void *fit_data;
+ size_t fit_len;
+#endif
+#ifdef CONFIG_GZIP
+ uint unc_len = CONFIG_SYS_XIMG_LEN;
+#endif
+ uint8_t comp;
+
+ verify = env_get_yesno("verify");
+
+ if (argc > 1) {
+ addr = hextoul(argv[1], NULL);
+ }
+ if (argc > 2) {
+ part = hextoul(argv[2], NULL);
+#if defined(CONFIG_FIT)
+ uname = argv[2];
+#endif
+ }
+ if (argc > 3) {
+ dest = hextoul(argv[3], NULL);
+ }
+
+ switch (genimg_get_format((void *)addr)) {
+#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
+ case IMAGE_FORMAT_LEGACY:
+
+ printf("## Copying part %d from legacy image "
+ "at %08lx ...\n", part, addr);
+
+ hdr = (struct legacy_img_hdr *)addr;
+ if (!image_check_magic(hdr)) {
+ printf("Bad Magic Number\n");
+ return 1;
+ }
+
+ if (!image_check_hcrc(hdr)) {
+ printf("Bad Header Checksum\n");
+ return 1;
+ }
+#ifdef DEBUG
+ image_print_contents(hdr);
+#endif
+
+ if (!image_check_type(hdr, IH_TYPE_MULTI) &&
+ !image_check_type(hdr, IH_TYPE_SCRIPT)) {
+ printf("Wrong Image Type for %s command\n",
+ cmdtp->name);
+ return 1;
+ }
+
+ comp = image_get_comp(hdr);
+ if ((comp != IH_COMP_NONE) && (argc < 4)) {
+ printf("Must specify load address for %s command "
+ "with compressed image\n",
+ cmdtp->name);
+ return 1;
+ }
+
+ if (verify) {
+ printf(" Verifying Checksum ... ");
+ if (!image_check_dcrc(hdr)) {
+ printf("Bad Data CRC\n");
+ return 1;
+ }
+ printf("OK\n");
+ }
+
+ count = image_multi_count(hdr);
+ if (part >= count) {
+ printf("Bad Image Part\n");
+ return 1;
+ }
+
+ image_multi_getimg(hdr, part, &data, &len);
+ break;
+#endif
+#if defined(CONFIG_FIT)
+ case IMAGE_FORMAT_FIT:
+ if (uname == NULL) {
+ puts("No FIT subimage unit name\n");
+ return 1;
+ }
+
+ printf("## Copying '%s' subimage from FIT image "
+ "at %08lx ...\n", uname, addr);
+
+ fit_hdr = (const void *)addr;
+ if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) {
+ puts("Bad FIT image format\n");
+ return 1;
+ }
+
+ /* get subimage node offset */
+ noffset = fit_image_get_node(fit_hdr, uname);
+ if (noffset < 0) {
+ printf("Can't find '%s' FIT subimage\n", uname);
+ return 1;
+ }
+
+ if (!fit_image_check_comp(fit_hdr, noffset, IH_COMP_NONE)
+ && (argc < 4)) {
+ printf("Must specify load address for %s command "
+ "with compressed image\n",
+ cmdtp->name);
+ return 1;
+ }
+
+ /* verify integrity */
+ if (verify) {
+ if (!fit_image_verify(fit_hdr, noffset)) {
+ puts("Bad Data Hash\n");
+ return 1;
+ }
+ }
+
+ /* get subimage/external data address and length */
+ if (fit_image_get_data(fit_hdr, noffset, &fit_data, &fit_len)) {
+ puts("Could not find script subimage data\n");
+ return 1;
+ }
+
+ if (fit_image_get_comp(fit_hdr, noffset, &comp))
+ comp = IH_COMP_NONE;
+
+ data = (ulong)fit_data;
+ len = (ulong)fit_len;
+ break;
+#endif
+ default:
+ puts("Invalid image type for imxtract\n");
+ return 1;
+ }
+
+ if (argc > 3) {
+ switch (comp) {
+ case IH_COMP_NONE:
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+ {
+ size_t l = len;
+ size_t tail;
+ void *to = (void *) dest;
+ void *from = (void *)data;
+
+ printf(" Loading part %d ... ", part);
+
+ while (l > 0) {
+ tail = (l > CHUNKSZ) ? CHUNKSZ : l;
+ schedule();
+ memmove(to, from, tail);
+ to += tail;
+ from += tail;
+ l -= tail;
+ }
+ }
+#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
+ printf(" Loading part %d ... ", part);
+ memmove((char *) dest, (char *)data, len);
+#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
+ break;
+#ifdef CONFIG_GZIP
+ case IH_COMP_GZIP:
+ {
+ int ret = 0;
+ printf(" Uncompressing part %d ... ", part);
+ ret = gunzip((void *)dest, unc_len,
+ (uchar *)data, &len);
+ if (ret == Z_BUF_ERROR) {
+ puts("Image too large: increase CONFIG_SYS_XIMG_LEN\n");
+ return 1;
+ } else if (ret != 0) {
+ puts("GUNZIP ERROR - image not loaded\n");
+ return 1;
+ }
+ }
+ break;
+#endif
+#if defined(CONFIG_BZIP2) && defined(CONFIG_LEGACY_IMAGE_FORMAT)
+ case IH_COMP_BZIP2:
+ {
+ int i;
+
+ printf(" Uncompressing part %d ... ", part);
+ /*
+ * If we've got less than 4 MB of malloc()
+ * space, use slower decompression algorithm
+ * which requires at most 2300 KB of memory.
+ */
+ i = BZ2_bzBuffToBuffDecompress(
+ map_sysmem(ntohl(hdr->ih_load), 0),
+ &unc_len, (char *)data, len,
+ CONFIG_SYS_MALLOC_LEN < (4096 * 1024),
+ 0);
+ if (i != BZ_OK) {
+ printf("BUNZIP2 ERROR %d - "
+ "image not loaded\n", i);
+ return 1;
+ }
+ }
+ break;
+#endif /* CONFIG_BZIP2 */
+#if IS_ENABLED(CONFIG_ZSTD)
+ case IH_COMP_ZSTD:
+ {
+ int ret;
+ struct abuf in, out;
+
+ printf(" Uncompressing part %d ... ", part);
+
+ abuf_init_set(&in, (void *)data, len);
+ abuf_init_set(&out, (void *)dest, unc_len);
+ ret = zstd_decompress(&in, &out);
+ if (ret < 0) {
+ printf("ZSTD ERROR %d - "
+ "image not loaded\n", ret);
+ return 1;
+ }
+ len = ret;
+ }
+ break;
+#endif
+ default:
+ printf("Unimplemented compression type %d\n", comp);
+ return 1;
+ }
+ puts("OK\n");
+ }
+
+ flush_cache(dest, ALIGN(len, ARCH_DMA_MINALIGN));
+
+ env_set_hex("fileaddr", data);
+ env_set_hex("filesize", len);
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(imgextract,
+ "addr part [dest]\n"
+ " - extract <part> from legacy image at <addr> and copy to <dest>"
+#if defined(CONFIG_FIT)
+ "\n"
+ "addr uname [dest]\n"
+ " - extract <uname> subimage from FIT image at <addr> and copy to <dest>"
+#endif
+ );
+
+U_BOOT_CMD(
+ imxtract, 4, 1, do_imgextract,
+ "extract a part of a multi-image", imgextract_help_text
+);
diff --git a/cmd/xxd.c b/cmd/xxd.c
new file mode 100644
index 00000000000..8ae05f910cb
--- /dev/null
+++ b/cmd/xxd.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022
+ * Roger Knecht <rknecht@pm.de>
+ */
+
+#include <command.h>
+#include <display_options.h>
+#include <fs.h>
+#include <malloc.h>
+#include <mapmem.h>
+
+static int do_xxd(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *ifname;
+ char *dev;
+ char *file;
+ char *buffer;
+ phys_addr_t addr;
+ loff_t file_size;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ ifname = argv[1];
+ dev = argv[2];
+ file = argv[3];
+
+ // check file exists
+ if (fs_set_blk_dev(ifname, dev, FS_TYPE_ANY))
+ return CMD_RET_FAILURE;
+
+ if (!fs_exists(file)) {
+ log_err("File does not exist: ifname=%s dev=%s file=%s\n", ifname, dev, file);
+ return CMD_RET_FAILURE;
+ }
+
+ // get file size
+ if (fs_set_blk_dev(ifname, dev, FS_TYPE_ANY))
+ return CMD_RET_FAILURE;
+
+ if (fs_size(file, &file_size)) {
+ log_err("Cannot read file size: ifname=%s dev=%s file=%s\n", ifname, dev, file);
+ return CMD_RET_FAILURE;
+ }
+
+ // allocate memory for file content
+ buffer = calloc(sizeof(char), file_size);
+ if (!buffer) {
+ log_err("Out of memory\n");
+ return CMD_RET_FAILURE;
+ }
+
+ // map pointer to system memory
+ addr = map_to_sysmem(buffer);
+
+ // read file to memory
+ if (fs_set_blk_dev(ifname, dev, FS_TYPE_ANY))
+ return CMD_RET_FAILURE;
+
+ if (fs_read(file, addr, 0, 0, &file_size)) {
+ log_err("Cannot read file: ifname=%s dev=%s file=%s\n", ifname, dev, file);
+ return CMD_RET_FAILURE;
+ }
+
+ // print file content
+ print_buffer(0, buffer, sizeof(char), file_size, 0);
+
+ free(buffer);
+
+ return 0;
+}
+
+U_BOOT_LONGHELP(xxd,
+ "<interface> <dev[:part]> <file>\n"
+ " - Print file from 'dev' on 'interface' as hexdump to standard output\n");
+
+U_BOOT_CMD(xxd, 4, 1, do_xxd,
+ "Print file as hexdump to standard output",
+ xxd_help_text
+);
diff --git a/cmd/zfs.c b/cmd/zfs.c
new file mode 100644
index 00000000000..471c63f2150
--- /dev/null
+++ b/cmd/zfs.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *
+ * ZFS filesystem porting to Uboot by
+ * Jorgen Lundman <lundman at lundman.net>
+ *
+ * zfsfs support
+ * made from existing GRUB Sources by Sun, GNU and others.
+ */
+
+#include <part.h>
+#include <config.h>
+#include <command.h>
+#include <env.h>
+#include <image.h>
+#include <linux/ctype.h>
+#include <asm/byteorder.h>
+#include <zfs_common.h>
+#include <linux/stat.h>
+#include <malloc.h>
+
+#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE)
+#include <usb.h>
+#endif
+
+#if !CONFIG_IS_ENABLED(DOS_PARTITION) && !CONFIG_IS_ENABLED(EFI_PARTITION)
+#error DOS or EFI partition support must be selected
+#endif
+
+#define DOS_PART_MAGIC_OFFSET 0x1fe
+#define DOS_FS_TYPE_OFFSET 0x36
+#define DOS_FS32_TYPE_OFFSET 0x52
+
+static int do_zfs_load(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *filename = NULL;
+ int dev;
+ int part;
+ ulong addr = 0;
+ struct disk_partition info;
+ struct blk_desc *dev_desc;
+ unsigned long count;
+ const char *addr_str;
+ struct zfs_file zfile;
+ struct device_s vdev;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ count = 0;
+ addr = hextoul(argv[3], NULL);
+ filename = env_get("bootfile");
+ switch (argc) {
+ case 3:
+ addr_str = env_get("loadaddr");
+ if (addr_str != NULL)
+ addr = hextoul(addr_str, NULL);
+ else
+ addr = CONFIG_SYS_LOAD_ADDR;
+
+ break;
+ case 4:
+ break;
+ case 5:
+ filename = argv[4];
+ break;
+ case 6:
+ filename = argv[4];
+ count = hextoul(argv[5], NULL);
+ break;
+
+ default:
+ return cmd_usage(cmdtp);
+ }
+
+ if (!filename) {
+ puts("** No boot file defined **\n");
+ return 1;
+ }
+
+ part = blk_get_device_part_str(argv[1], argv[2], &dev_desc, &info, 1);
+ if (part < 0)
+ return 1;
+
+ dev = dev_desc->devnum;
+ printf("Loading file \"%s\" from %s device %d%c%c\n",
+ filename, argv[1], dev,
+ part ? ':' : ' ', part ? part + '0' : ' ');
+
+ zfs_set_blk_dev(dev_desc, &info);
+ vdev.part_length = info.size;
+
+ memset(&zfile, 0, sizeof(zfile));
+ zfile.device = &vdev;
+ if (zfs_open(&zfile, filename)) {
+ printf("** File not found %s **\n", filename);
+ return 1;
+ }
+
+ if ((count < zfile.size) && (count != 0))
+ zfile.size = (uint64_t)count;
+
+ if (zfs_read(&zfile, (char *)addr, zfile.size) != zfile.size) {
+ printf("** Unable to read \"%s\" from %s %d:%d **\n",
+ filename, argv[1], dev, part);
+ zfs_close(&zfile);
+ return 1;
+ }
+
+ zfs_close(&zfile);
+
+ /* Loading ok, update default load address */
+ image_load_addr = addr;
+
+ printf("%llu bytes read\n", zfile.size);
+ env_set_hex("filesize", zfile.size);
+
+ return 0;
+}
+
+int zfs_print(const char *entry, const struct zfs_dirhook_info *data)
+{
+ printf("%s %s\n",
+ data->dir ? "<DIR> " : " ",
+ entry);
+ return 0; /* 0 continue, 1 stop */
+}
+
+static int do_zfs_ls(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *filename = "/";
+ int part;
+ struct blk_desc *dev_desc;
+ struct disk_partition info;
+ struct device_s vdev;
+
+ if (argc < 2)
+ return cmd_usage(cmdtp);
+
+ if (argc == 4)
+ filename = argv[3];
+
+ part = blk_get_device_part_str(argv[1], argv[2], &dev_desc, &info, 1);
+ if (part < 0)
+ return 1;
+
+ zfs_set_blk_dev(dev_desc, &info);
+ vdev.part_length = info.size;
+
+ zfs_ls(&vdev, filename,
+ zfs_print);
+
+ return 0;
+}
+
+U_BOOT_CMD(zfsls, 4, 1, do_zfs_ls,
+ "list files in a directory (default /)",
+ "<interface> <dev[:part]> [directory]\n"
+ " - list files from 'dev' on 'interface' in a '/DATASET/@/$dir/'");
+
+U_BOOT_CMD(zfsload, 6, 0, do_zfs_load,
+ "load binary file from a ZFS filesystem",
+ "<interface> <dev[:part]> [addr] [filename] [bytes]\n"
+ " - load binary file '/DATASET/@/$dir/$file' from 'dev' on 'interface'\n"
+ " to address 'addr' from ZFS filesystem");
diff --git a/cmd/zip.c b/cmd/zip.c
new file mode 100644
index 00000000000..2d255428822
--- /dev/null
+++ b/cmd/zip.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2012
+ * Lei Wen <leiwen@marvell.com>, Marvell Inc.
+ */
+
+#include <command.h>
+#include <env.h>
+#include <gzip.h>
+#include <vsprintf.h>
+
+static int do_zip(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ unsigned long src, dst;
+ unsigned long src_len, dst_len = ~0UL;
+
+ switch (argc) {
+ case 5:
+ dst_len = hextoul(argv[4], NULL);
+ /* fall through */
+ case 4:
+ src = hextoul(argv[1], NULL);
+ src_len = hextoul(argv[2], NULL);
+ dst = hextoul(argv[3], NULL);
+ break;
+ default:
+ return cmd_usage(cmdtp);
+ }
+
+ if (gzip((void *) dst, &dst_len, (void *) src, src_len) != 0)
+ return 1;
+
+ printf("Compressed size: %lu = 0x%lX\n", dst_len, dst_len);
+ env_set_hex("filesize", dst_len);
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ zip, 5, 1, do_zip,
+ "zip a memory region",
+ "srcaddr srcsize dstaddr [dstsize]"
+);