[Concept,18/19] bootstage: Add save/restore subcommands

Message ID 20260314231618.338113-19-sjg@u-boot.org
State New
Headers
Series test: Fix pytest inter-test side effects |

Commit Message

Simon Glass March 14, 2026, 11:16 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

Add 'bootstage save' and 'bootstage restore' subcommands behind a
new BOOTSTAGE_SAVE Kconfig (default y when UNIT_TEST is enabled).
These store and retrieve the record count via $bootstage_count.

Some commands (e.g. bootm) add bootstage records with unique IDs.
In a pytest session with many tests, these accumulate and fill the
bootstage table. The new commands allow the test framework to save
the count before a test and restore it afterwards.

Also add inline stubs for bootstage_get_rec_count() and
bootstage_set_rec_count() when BOOTSTAGE is disabled, along with a
test and command documentation.

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

 boot/Kconfig                | 11 +++++
 cmd/bootstage.c             | 28 ++++++++++++
 doc/usage/cmd/bootstage.rst | 86 +++++++++++++++++++++++++++++++++++++
 doc/usage/index.rst         |  1 +
 include/bootstage.h         |  9 ++++
 test/cmd/bootstage.c        | 24 +++++++++++
 6 files changed, 159 insertions(+)
 create mode 100644 doc/usage/cmd/bootstage.rst
  

Patch

diff --git a/boot/Kconfig b/boot/Kconfig
index cd66060f4db..d9d5214ca0f 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -1479,6 +1479,17 @@  config BOOTSTAGE_FDT
 
 	  Code in the Linux kernel can find this in /proc/devicetree.
 
+config BOOTSTAGE_SAVE
+	bool "Allow saving and restoring the bootstage record count"
+	depends on BOOTSTAGE
+	default y if UNIT_TEST
+	help
+	  Provide 'bootstage save' and 'bootstage restore' subcommands
+	  which store and retrieve the record count via the bootstage_count
+	  environment variable. These are used by Python tests to prevent
+	  records from accumulating across tests and filling the bootstage
+	  table.
+
 config BOOTSTAGE_STASH
 	bool "Stash the boot timing information in memory before booting OS"
 	depends on BOOTSTAGE
diff --git a/cmd/bootstage.c b/cmd/bootstage.c
index 5c6d5a3ab45..ce69097b96c 100644
--- a/cmd/bootstage.c
+++ b/cmd/bootstage.c
@@ -5,6 +5,7 @@ 
 
 #include <bootstage.h>
 #include <command.h>
+#include <env.h>
 #include <vsprintf.h>
 #include <linux/string.h>
 
@@ -62,8 +63,31 @@  static int do_bootstage_stash(struct cmd_tbl *cmdtp, int flag, int argc,
 }
 #endif
 
+static int __maybe_unused do_bootstage_save(struct cmd_tbl *cmdtp, int flag,
+					    int argc, char *const argv[])
+{
+	env_set_hex("bootstage_count", bootstage_get_rec_count());
+
+	return 0;
+}
+
+static int __maybe_unused do_bootstage_restore(struct cmd_tbl *cmdtp, int flag,
+					       int argc, char *const argv[])
+{
+	ulong count = env_get_hex("bootstage_count", 0);
+
+	if (count)
+		bootstage_set_rec_count(count);
+
+	return 0;
+}
+
 static struct cmd_tbl cmd_bootstage_sub[] = {
 	U_BOOT_CMD_MKENT(report, 2, 1, do_bootstage_report, "", ""),
+#if IS_ENABLED(CONFIG_BOOTSTAGE_SAVE)
+	U_BOOT_CMD_MKENT(save, 1, 0, do_bootstage_save, "", ""),
+	U_BOOT_CMD_MKENT(restore, 1, 0, do_bootstage_restore, "", ""),
+#endif
 #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, "", ""),
@@ -95,6 +119,10 @@  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_SAVE)
+	"save                        - Save record count to $bootstage_count\n"
+	"restore                     - Restore record count from $bootstage_count\n"
+#endif
 #if IS_ENABLED(CONFIG_BOOTSTAGE_STASH)
 	"stash [<start> [<size>]]    - Stash data into memory\n"
 	"unstash [<start> [<size>]]  - Unstash data from memory\n"
diff --git a/doc/usage/cmd/bootstage.rst b/doc/usage/cmd/bootstage.rst
new file mode 100644
index 00000000000..44069e6fedd
--- /dev/null
+++ b/doc/usage/cmd/bootstage.rst
@@ -0,0 +1,86 @@ 
+.. SPDX-License-Identifier: GPL-2.0+
+
+.. index::
+   single: bootstage (command)
+
+bootstage command
+=================
+
+Synopsis
+--------
+
+::
+
+    bootstage report
+    bootstage save
+    bootstage restore
+    bootstage stash [<start> [<size>]]
+    bootstage unstash [<start> [<size>]]
+
+Description
+-----------
+
+The *bootstage* command provides access to U-Boot's boot-timing records.
+
+bootstage report
+    Print a report of all bootstage records, showing the timestamp and elapsed
+    time for each stage.
+
+bootstage save
+    Save the current record count to the *bootstage_count* environment
+    variable. This can be used to snapshot the bootstage state before an
+    operation that adds records.
+
+bootstage restore
+    Restore the record count from *bootstage_count*, discarding any records
+    added since the last save. This is used by the Python test framework to
+    prevent records from accumulating across tests.
+
+bootstage stash
+    Stash bootstage data into memory at the given address and size.
+    Only available when ``CONFIG_BOOTSTAGE_STASH`` is enabled.
+
+bootstage unstash
+    Read back previously stashed bootstage data from memory.
+    Only available when ``CONFIG_BOOTSTAGE_STASH`` is enabled.
+
+Configuration
+-------------
+
+The *bootstage* command is available when ``CONFIG_BOOTSTAGE`` is enabled.
+
+CONFIG_BOOTSTAGE_REPORT
+    Enable output of a boot-time report before booting the OS.
+
+CONFIG_BOOTSTAGE_RECORD_COUNT
+    Number of bootstage records to store (default 50).
+
+CONFIG_BOOTSTAGE_SAVE
+    Enable the *save* and *restore* subcommands. Default y when
+    ``CONFIG_UNIT_TEST`` is set.
+
+CONFIG_BOOTSTAGE_STASH
+    Enable the *stash* and *unstash* subcommands for passing bootstage
+    data to the OS via memory.
+
+Example
+-------
+
+::
+
+    => bootstage report
+    Timer summary in microseconds (8 records):
+           Mark    Elapsed  Stage
+              0          0  reset
+              0          0  board_init_f
+         29,743     29,743  board_init_r
+         52,918     23,175  eth_common_init
+         53,007         89  eth_initialize
+
+    Accumulated time:
+                     1,235  dm_f
+                       670  of_live
+                     5,621  dm_r
+
+    => bootstage save
+    => bootstage restore
diff --git a/doc/usage/index.rst b/doc/usage/index.rst
index 05e1828bf01..ecf0ba16700 100644
--- a/doc/usage/index.rst
+++ b/doc/usage/index.rst
@@ -49,6 +49,7 @@  Shell commands
    cmd/bootm
    cmd/bootmenu
    cmd/bootmeth
+   cmd/bootstage
    cmd/bootstd
    cmd/bootz
    cmd/button
diff --git a/include/bootstage.h b/include/bootstage.h
index da57f9b81c5..30858cbfb3f 100644
--- a/include/bootstage.h
+++ b/include/bootstage.h
@@ -519,6 +519,15 @@  static inline int bootstage_init(bool first)
 	return 0;
 }
 
+static inline uint bootstage_get_rec_count(void)
+{
+	return 0;
+}
+
+static inline void bootstage_set_rec_count(uint count)
+{
+}
+
 #endif /* ENABLE_BOOTSTAGE */
 
 /* helpers for SPL */
diff --git a/test/cmd/bootstage.c b/test/cmd/bootstage.c
index dee4a0671fa..29df4725d1f 100644
--- a/test/cmd/bootstage.c
+++ b/test/cmd/bootstage.c
@@ -30,3 +30,27 @@  static int cmd_bootstage_report(struct unit_test_state *uts)
 	return 0;
 }
 CMD_TEST(cmd_bootstage_report, UTF_CONSOLE);
+
+static int cmd_bootstage_save_restore(struct unit_test_state *uts)
+{
+	uint count;
+
+	count = bootstage_get_rec_count();
+	ut_assert(count > 0);
+
+	/* Save the current count */
+	ut_assertok(run_command("bootstage save", 0));
+	ut_assert_console_end();
+
+	/* Add a new record and check the count grows by one */
+	bootstage_mark_name(BOOTSTAGE_ID_USER + 60, "test_save_restore");
+	ut_asserteq(count + 1, bootstage_get_rec_count());
+
+	/* Restore should bring the count back */
+	ut_assertok(run_command("bootstage restore", 0));
+	ut_assert_console_end();
+	ut_asserteq(count, bootstage_get_rec_count());
+
+	return 0;
+}
+CMD_TEST(cmd_bootstage_save_restore, UTF_CONSOLE);