[PATCH v4 06/14] command: Replace the cmd_rep pointer with a CMDF_REPEATABLE flag

Simon Glass sjg at chromium.org
Wed Jul 1 13:42:26 CEST 2026


Each struct cmd_tbl entry carries a ->cmd_rep function pointer used to
report whether a command may be repeated by pressing Enter at an empty
prompt. For all but one command it points at cmd_always_repeatable() or
cmd_never_repeatable(), trampolines that just set the repeatable flag
and call cmd_invoke() - so a whole pointer per command encodes a single
bit, on every board.

Replace it with a CMDF_REPEATABLE flag in the existing cmd_flags byte.
The _rep argument of the U_BOOT_CMD*() macros now sets this bit, and
cmd_call() reads it directly before invoking ->cmd, dropping the
trampolines entirely.

Sub-command dispatchers (U_BOOT_CMD_WITH_SUBCMDS, and the x86 zboot
command) still need to report the repeatability of the sub-command they
pick, which they do through the extended (..., int *repeatable)
signature. Keep that ability with a CMDF_SUBCMD_REP flag: such a command
stores its dispatcher in ->cmd and is tagged CMDF_SUBCMD_REP, and
cmd_call() calls it with the repeatable pointer. The dispatcher refines
*repeatable from the chosen sub-command's CMDF_REPEATABLE bit.

This removes a pointer from every struct cmd_tbl entry: the struct
shrinks from 56 to 48 bytes on 64-bit and by 4 bytes per entry on
32-bit, with no change in behaviour and no new config option. On
turris_1x_nor (ppc32, ~100 commands) this recovers about 670 bytes,
enough to keep the board within its NOR section after the getopt
conversion.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

Changes in v4:
- Add new patch to replace the ->cmd_rep pointer with a
  CMDF_REPEATABLE flag, dropping a pointer from every command entry

 cmd/help.c        |   2 +-
 cmd/test.c        |   2 +-
 cmd/x86/zboot.c   |   2 +-
 common/command.c  |  44 +++++++-------------
 include/command.h | 104 ++++++++++++++++++++++------------------------
 5 files changed, 67 insertions(+), 87 deletions(-)

diff --git a/cmd/help.c b/cmd/help.c
index b87c19003d4..ab6f9c95739 100644
--- a/cmd/help.c
+++ b/cmd/help.c
@@ -28,7 +28,7 @@ U_BOOT_CMD(
  * nor can we rely on the CONFIG_SYS_LONGHELP helper macro
  */
 ll_entry_declare(struct cmd_tbl, question_mark, cmd) = {
-	"?",	CONFIG_SYS_MAXARGS, 0, cmd_always_repeatable,	do_help,
+	"?",	CONFIG_SYS_MAXARGS, CMDF_REPEATABLE,	do_help,
 	"alias for 'help'",
 #ifdef  CONFIG_SYS_LONGHELP
 	""
diff --git a/cmd/test.c b/cmd/test.c
index f0b89aa0af0..32c5238972d 100644
--- a/cmd/test.c
+++ b/cmd/test.c
@@ -244,7 +244,7 @@ U_BOOT_CMD(
  * This does not use the U_BOOT_CMD macro as [ can't be used in symbol names
  */
 ll_entry_declare(struct cmd_tbl, lbracket, cmd) = {
-	"[",	CONFIG_SYS_MAXARGS, 0, cmd_always_repeatable, do_test,
+	"[",	CONFIG_SYS_MAXARGS, CMDF_REPEATABLE, do_test,
 	"alias for 'test'",
 #ifdef CONFIG_SYS_LONGHELP
 	" <test expression> ]"
diff --git a/cmd/x86/zboot.c b/cmd/x86/zboot.c
index 3876d163236..ef3e27e622d 100644
--- a/cmd/x86/zboot.c
+++ b/cmd/x86/zboot.c
@@ -165,7 +165,7 @@ int do_zboot_parent(struct cmd_tbl *cmdtp, int flag, int argc,
 	return CMD_RET_FAILURE;
 }
 
-U_BOOT_CMDREP_COMPLETE(
+U_BOOT_SUBCMD_DECL(
 	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"
diff --git a/common/command.c b/common/command.c
index c747bef7883..4204c6e2df2 100644
--- a/common/command.c
+++ b/common/command.c
@@ -508,11 +508,6 @@ void fixup_cmdtable(struct cmd_tbl *cmdtp, int size)
 	for (i = 0; i < size; i++) {
 		ulong addr;
 
-		addr = (ulong)(cmdtp->cmd_rep) + gd->reloc_off;
-		cmdtp->cmd_rep =
-			(int (*)(struct cmd_tbl *, int, int,
-				 char * const [], int *))addr;
-
 		addr = (ulong)(cmdtp->cmd) + gd->reloc_off;
 #ifdef DEBUG_COMMANDS
 		printf("Command \"%s\": 0x%08lx => 0x%08lx\n",
@@ -558,30 +553,6 @@ int cmd_invoke(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 	return cmdtp->cmd(cmdtp, flag, argc, argv);
 }
 
-int cmd_always_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
-			  char *const argv[], int *repeatable)
-{
-	*repeatable = 1;
-
-	return cmd_invoke(cmdtp, flag, argc, argv);
-}
-
-int cmd_never_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
-			 char *const argv[], int *repeatable)
-{
-	*repeatable = 0;
-
-	return cmd_invoke(cmdtp, flag, argc, argv);
-}
-
-int cmd_discard_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
-			   char *const argv[])
-{
-	int repeatable;
-
-	return cmdtp->cmd_rep(cmdtp, flag, argc, argv, &repeatable);
-}
-
 /**
  * Call a command function. This should be the only route in U-Boot to call
  * a command, so that we can track whether we are waiting for input or
@@ -599,7 +570,20 @@ static int cmd_call(struct cmd_tbl *cmdtp, int flag, int argc,
 {
 	int result;
 
-	result = cmdtp->cmd_rep(cmdtp, flag, argc, argv, repeatable);
+	*repeatable = !!(cmdtp->cmd_flags & CMDF_REPEATABLE);
+
+	if (cmdtp->cmd_flags & CMDF_SUBCMD_REP) {
+		/*
+		 * A sub-command dispatcher refines *repeatable for the
+		 * sub-command it picks, so call it with the extended signature.
+		 */
+		cmd_rep_func_t func = (cmd_rep_func_t)cmdtp->cmd;
+
+		result = func(cmdtp, flag, argc, argv, repeatable);
+	} else {
+		result = cmd_invoke(cmdtp, flag, argc, argv);
+	}
+
 	if (result)
 		debug("Command failed, result=%d\n", result);
 	return result;
diff --git a/include/command.h b/include/command.h
index 495d6a07d75..b201100870a 100644
--- a/include/command.h
+++ b/include/command.h
@@ -39,16 +39,6 @@ struct cmd_tbl {
 	char		*name;		/* Command Name			*/
 	short		maxargs;	/* maximum number of arguments	*/
 	u8		cmd_flags;	/* CMDF_... flags		*/
-					/*
-					 * Same as ->cmd() except the command
-					 * tells us if it can be repeated.
-					 * Replaces the old ->repeatable field
-					 * which was not able to make
-					 * repeatable property different for
-					 * the main command and sub-commands.
-					 */
-	int		(*cmd_rep)(struct cmd_tbl *cmd, int flags, int argc,
-				   char *const argv[], int *repeatable);
 					/* Implementation function	*/
 	int		(*cmd)(struct cmd_tbl *cmd, int flags, int argc,
 			       char *const argv[]);
@@ -76,8 +66,24 @@ enum {
 	 * (int (*)(struct getopt_state *)) instead of the classic one
 	 */
 	CMDF_GETOPT	= BIT(0),
+	/* the command may be repeated by pressing Enter at an empty prompt */
+	CMDF_REPEATABLE	= BIT(1),
+	/*
+	 * ->cmd holds a sub-command dispatcher with the extended signature
+	 * (..., int *repeatable), so it can report the repeatable property of
+	 * the sub-command it dispatched to. cmd_call() calls it accordingly.
+	 */
+	CMDF_SUBCMD_REP	= BIT(2),
 };
 
+/*
+ * Type of a sub-command dispatcher (a CMDF_SUBCMD_REP command's ->cmd). It has
+ * the classic command arguments plus a @repeatable output which it sets from
+ * the sub-command it dispatches to.
+ */
+typedef int (*cmd_rep_func_t)(struct cmd_tbl *cmdtp, int flag, int argc,
+			      char *const argv[], int *repeatable);
+
 /**
  * cmd_arg_get() - Get a particular argument
  *
@@ -142,17 +148,9 @@ int cmd_usage(const struct cmd_tbl *cmdtp);
  */
 int cmd_invoke(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 
-/* Dummy ->cmd and ->cmd_rep wrappers. */
-int cmd_always_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
-			  char *const argv[], int *repeatable);
-int cmd_never_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
-			 char *const argv[], int *repeatable);
-int cmd_discard_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
-			   char *const argv[]);
-
 static inline bool cmd_is_repeatable(struct cmd_tbl *cmdtp)
 {
-	return cmdtp->cmd_rep == cmd_always_repeatable;
+	return cmdtp->cmd_flags & CMDF_REPEATABLE;
 }
 
 #ifdef CONFIG_AUTO_COMPLETE
@@ -416,8 +414,9 @@ int cmd_source_script(ulong addr, const char *fit_uname, const char *confname);
 		    !cmd_is_repeatable(subcmd))				\
 			return CMD_RET_SUCCESS;				\
 									\
-		return subcmd->cmd_rep(subcmd, flag, argc - 1,		\
-				       argv + 1, repeatable);		\
+		*repeatable &= cmd_is_repeatable(subcmd);		\
+									\
+		return cmd_invoke(subcmd, flag, argc - 1, argv + 1);	\
 	}
 
 #ifdef CONFIG_AUTO_COMPLETE
@@ -441,21 +440,17 @@ int cmd_source_script(ulong addr, const char *fit_uname, const char *confname);
 	U_BOOT_SUBCMDS_COMPLETE(_cmdname)
 
 #if CONFIG_IS_ENABLED(CMDLINE)
-#define U_BOOT_CMDREP_MKENT_COMPLETE(_name, _maxargs, _cmd_rep,		\
-				     _usage, _help, _comp)		\
-		{ #_name, _maxargs, 0, _cmd_rep, cmd_discard_repeatable,	\
-		  _usage, _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
+/* Encode the _rep argument as the CMDF_REPEATABLE flag bit */
+#define _CMD_REP_FLAG(_rep)	((_rep) ? CMDF_REPEATABLE : 0)
 
 #define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,		\
 				_usage, _help, _comp)			\
-		{ #_name, _maxargs, 0,					\
-		 _rep ? cmd_always_repeatable : cmd_never_repeatable,	\
+		{ #_name, _maxargs, _CMD_REP_FLAG(_rep),		\
 		 _cmd, _usage, _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
 
 #define U_BOOT_CMD_MKENT_GETOPT_COMPLETE(_name, _maxargs, _rep, _cmd,	\
 					 _usage, _help, _comp)		\
-		{ #_name, _maxargs, CMDF_GETOPT,			\
-		 _rep ? cmd_always_repeatable : cmd_never_repeatable,	\
+		{ #_name, _maxargs, CMDF_GETOPT | _CMD_REP_FLAG(_rep),	\
 		 (int (*)(struct cmd_tbl *, int, int,			\
 			  char *const []))(_cmd),			\
 		 _usage, _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
@@ -480,11 +475,18 @@ int cmd_source_script(ulong addr, const char *fit_uname, const char *confname);
 		U_BOOT_CMD_MKENT_GETOPT_COMPLETE(_name, _maxargs, _rep,	\
 						 _cmd, _usage, _help, _comp)
 
-#define U_BOOT_CMDREP_COMPLETE(_name, _maxargs, _cmd_rep, _usage,	\
-			       _help, _comp)				\
-	ll_entry_declare(struct cmd_tbl, _name, cmd) =			\
-		U_BOOT_CMDREP_MKENT_COMPLETE(_name, _maxargs, _cmd_rep,	\
-					     _usage, _help, _comp)
+/*
+ * Declare a top-level command whose ->cmd is a sub-command dispatcher with
+ * the extended (..., int *repeatable) signature (see CMDF_SUBCMD_REP). The
+ * dispatcher is stored in ->cmd and tagged so cmd_call() invokes it with the
+ * repeatable pointer.
+ */
+#define U_BOOT_SUBCMD_DECL(_name, _maxargs, _do_cmd, _usage, _help, _comp) \
+	ll_entry_declare(struct cmd_tbl, _name, cmd) = {		\
+		#_name, _maxargs, CMDF_SUBCMD_REP | CMDF_REPEATABLE,	\
+		(int (*)(struct cmd_tbl *, int, int,			\
+			 char *const []))(_do_cmd),			\
+		_usage, _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
 
 #else
 #define U_BOOT_SUBCMD_START(name)	static struct cmd_tbl name[] = {};
@@ -498,14 +500,6 @@ int cmd_source_script(ulong addr, const char *fit_uname, const char *confname);
 		return 0;						\
 	}
 
-#define _CMD_REMOVE_REP(_name, _cmd)					\
-	int __remove_ ## _name(void)					\
-	{								\
-		if (0)							\
-			_cmd(NULL, 0, 0, NULL, NULL);			\
-		return 0;						\
-	}
-
 #define _CMD_REMOVE_GETOPT(_name, _cmd)					\
 	int __remove_ ## _name(void)					\
 	{								\
@@ -514,19 +508,14 @@ int cmd_source_script(ulong addr, const char *fit_uname, const char *confname);
 		return 0;						\
 	}
 
-#define U_BOOT_CMDREP_MKENT_COMPLETE(_name, _maxargs, _cmd_rep,		\
-				     _usage, _help, _comp)		\
-		{ #_name, _maxargs, 0, 0 ? _cmd_rep : NULL, NULL, _usage, \
-			_CMD_HELP(_help) _CMD_COMPLETE(_comp) }
-
 #define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, _usage,	\
 				  _help, _comp)				\
-		{ #_name, _maxargs, 0, NULL, 0 ? _cmd : NULL, _usage,	\
+		{ #_name, _maxargs, 0, 0 ? _cmd : NULL, _usage,		\
 			_CMD_HELP(_help) _CMD_COMPLETE(_comp) }
 
 #define U_BOOT_CMD_MKENT_GETOPT_COMPLETE(_name, _maxargs, _rep, _cmd,	\
 					 _usage, _help, _comp)		\
-		{ #_name, _maxargs, CMDF_GETOPT, NULL,			\
+		{ #_name, _maxargs, CMDF_GETOPT,			\
 		 0 ? (int (*)(struct cmd_tbl *, int, int,		\
 			      char *const []))(_cmd) : NULL, _usage,	\
 			_CMD_HELP(_help) _CMD_COMPLETE(_comp) }
@@ -547,9 +536,16 @@ int cmd_source_script(ulong addr, const char *fit_uname, const char *confname);
 				   _help, _comp)			\
 	_CMD_REMOVE_GETOPT(sub_ ## _name, _cmd)
 
-#define U_BOOT_CMDREP_COMPLETE(_name, _maxargs, _cmd_rep, _usage,	\
-			       _help, _comp)				\
-	_CMD_REMOVE_REP(sub_ ## _name, _cmd_rep)
+#define _CMD_REMOVE_REP(_name, _cmd)					\
+	int __remove_ ## _name(void)					\
+	{								\
+		if (0)							\
+			_cmd(NULL, 0, 0, NULL, NULL);			\
+		return 0;						\
+	}
+
+#define U_BOOT_SUBCMD_DECL(_name, _maxargs, _do_cmd, _usage, _help, _comp) \
+	_CMD_REMOVE_REP(sub_ ## _name, _do_cmd)
 
 #endif /* CONFIG_CMDLINE */
 
@@ -571,7 +567,7 @@ int cmd_source_script(ulong addr, const char *fit_uname, const char *confname);
 
 #define U_BOOT_CMD_WITH_SUBCMDS(_name, _usage, _help, ...)		\
 	U_BOOT_SUBCMDS(_name, __VA_ARGS__)				\
-	U_BOOT_CMDREP_COMPLETE(_name, CONFIG_SYS_MAXARGS, do_##_name,	\
-			       _usage, _help, complete_##_name)
+	U_BOOT_SUBCMD_DECL(_name, CONFIG_SYS_MAXARGS, do_##_name,	\
+			   _usage, _help, complete_##_name)
 
 #endif	/* __COMMAND_H */
-- 
2.43.0



More information about the U-Boot mailing list