[Concept,06/24] sandbox: Add devon and devoff subcommands to sb command

Message ID 20251031065439.3251464-7-sjg@u-boot.org
State New
Headers
Series luks: Provide basic support for unlocking a LUKS1 partition |

Commit Message

Simon Glass Oct. 31, 2025, 6:54 a.m. UTC
  From: Simon Glass <sjg@chromium.org>

There are quite a few media devices in test.dts which are not enabled by
default, so are not bound on startup. Sometimes it is useful to be able
to use these from the command line.

Add 'sb devon' and 'sb devoff' subcommands to enable and disable devices
from the device tree. For example, running sandbox with -T, then
'sb devon mmc11' enables the mmc11 device mentioned in test.dts

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
---

 cmd/sb.c             | 107 +++++++++++++++++++++++++++++++++++--
 doc/usage/cmd/sb.rst |  40 +++++++++++++-
 test/cmd/Makefile    |   1 +
 test/cmd/sb.c        | 123 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 267 insertions(+), 4 deletions(-)
 create mode 100644 test/cmd/sb.c
  

Patch

diff --git a/cmd/sb.c b/cmd/sb.c
index 79f3fb0aacd..7de43e7edc3 100644
--- a/cmd/sb.c
+++ b/cmd/sb.c
@@ -10,6 +10,10 @@ 
 #include <asm/cpu.h>
 #include <asm/global_data.h>
 #include <asm/state.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+
+DECLARE_GLOBAL_DATA_PTR;
 
 static int do_sb_handoff(struct cmd_tbl *cmdtp, int flag, int argc,
 			 char *const argv[])
@@ -47,12 +51,109 @@  static int do_sb_state(struct cmd_tbl *cmdtp, int flag, int argc,
 	return 0;
 }
 
+static int do_sb_devon(struct cmd_tbl *cmdtp, int flag, int argc,
+		       char *const argv[])
+{
+	struct udevice *dev;
+	ofnode root, node;
+	int ret;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	/* Find the specified device tree node */
+	root = oftree_root(oftree_default());
+	node = ofnode_find_subnode(root, argv[1]);
+	if (!ofnode_valid(node)) {
+		printf("Device tree node '%s' not found\n", argv[1]);
+		return CMD_RET_FAILURE;
+	}
+
+	/* Check if device is already bound */
+	ret = device_find_global_by_ofnode(node, &dev);
+	if (!ret) {
+		printf("Device '%s' is already enabled\n", argv[1]);
+		return CMD_RET_FAILURE;
+	}
+
+	/* Bind the device from device tree */
+	ret = lists_bind_fdt(gd->dm_root, node, &dev, NULL, false);
+	if (ret) {
+		printf("Failed to bind device '%s' (err %dE)\n", argv[1],
+		       ret);
+		return CMD_RET_FAILURE;
+	}
+
+	/* Probe the device */
+	ret = device_probe(dev);
+	if (ret) {
+		printf("Failed to probe device '%s' (err %dE)\n", argv[1],
+		       ret);
+		return CMD_RET_FAILURE;
+	}
+
+	printf("Device '%s' enabled\n", dev->name);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_sb_devoff(struct cmd_tbl *cmdtp, int flag, int argc,
+			char *const argv[])
+{
+	struct udevice *dev;
+	ofnode root, node;
+	int ret;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	/* Find the specified device tree node */
+	root = oftree_root(oftree_default());
+	node = ofnode_find_subnode(root, argv[1]);
+	if (!ofnode_valid(node)) {
+		printf("Device tree node '%s' not found\n", argv[1]);
+		return CMD_RET_FAILURE;
+	}
+
+	/* Find the device bound to this node */
+	ret = device_find_global_by_ofnode(node, &dev);
+	if (ret) {
+		printf("Device '%s' not found or not bound (err %dE)\n",
+		       argv[1], ret);
+		return CMD_RET_FAILURE;
+	}
+
+	/* Remove the device (deactivate it) */
+	ret = device_remove(dev, DM_REMOVE_NORMAL);
+	if (ret) {
+		printf("Failed to remove device '%s' (err %dE)\n", argv[1],
+		       ret);
+		return CMD_RET_FAILURE;
+	}
+
+	/* Unbind the device */
+	ret = device_unbind(dev);
+	if (ret) {
+		printf("Failed to unbind device '%s' (err %dE)\n", argv[1],
+		       ret);
+		return CMD_RET_FAILURE;
+	}
+
+	printf("Device '%s' disabled\n", argv[1]);
+
+	return CMD_RET_SUCCESS;
+}
+
 U_BOOT_LONGHELP(sb,
-	"handoff     - Show handoff data received from SPL\n"
-	"sb map         - Show mapped memory\n"
-	"sb state       - Show sandbox state");
+	"devoff <node>  - Disable device from device tree node\n"
+	"sb devon <node>   - Enable device from device tree node\n"
+	"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(devoff, 2, 1, do_sb_devoff),
+	U_BOOT_SUBCMD_MKENT(devon, 2, 1, do_sb_devon),
 	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/doc/usage/cmd/sb.rst b/doc/usage/cmd/sb.rst
index 37431aff7c8..ee9b3d80767 100644
--- a/doc/usage/cmd/sb.rst
+++ b/doc/usage/cmd/sb.rst
@@ -11,6 +11,8 @@  Synopsis
 
 ::
 
+    sb devoff <node>
+    sb devon <node>
     sb handoff
     sb map
     sb state
@@ -19,7 +21,24 @@  Description
 -----------
 
 The *sb* command is used to display information about sandbox's internal
-operation. See :doc:`/arch/sandbox/index` for more information.
+operation and to manage devices. See :doc:`/arch/sandbox/index` for more
+information.
+
+sb devoff
+~~~~~~~~~
+
+This disables a device that was previously enabled with *sb devon*. The device
+is removed (deactivated) and unbound from the driver model. The parameter is
+the name of a device tree node.
+
+sb devon
+~~~~~~~~
+
+This enables a device from the device tree. The device tree node is located,
+bound to the driver model, and probed (activated). This is useful for testing
+devices that are not automatically bound at startup, i.e. those marked as
+status = "disabled" in the device tree. The parameter is the name of a root
+devicetree node.
 
 sb handoff
 ~~~~~~~~~~
@@ -54,6 +73,25 @@  command-line with which sandbox was started.
 Example
 -------
 
+This shows enabling a device from the `test.dts` device tree. Note that sandbox
+must be run with the -T flag to use the test device tree::
+
+    => sb devon mmc11
+    Device 'mmc11' enabled
+    => ls mmc b
+                extlinux/
+            7   initrd.img-6.8.0-53-generic
+         1616   vmlinuz-6.8.0-53-generic
+
+    2 file(s), 1 dir(s)
+
+    => sb devoff mmc11
+    Device 'mmc11' disabled
+    => ls mmc b
+    ** Bad device specification mmc b **
+    Couldn't find partition mmc b
+    =>
+
 This shows checking for the presence of SPL-handoff information. For this to
 work, ``u-boot-spl`` must be run, with build that enables ``CONFIG_SPL``, such
 as ``sandbox_spl``::
diff --git a/test/cmd/Makefile b/test/cmd/Makefile
index 9cd8ea3aaf0..4d8f93e2551 100644
--- a/test/cmd/Makefile
+++ b/test/cmd/Makefile
@@ -41,6 +41,7 @@  obj-$(CONFIG_CMD_MBR) += mbr.o
 obj-$(CONFIG_CMD_PINMUX) += pinmux.o
 obj-$(CONFIG_CMD_PWM) += pwm.o
 obj-$(CONFIG_CMD_READ) += rw.o
+obj-y += sb.o
 obj-$(CONFIG_CMD_SETEXPR) += setexpr.o
 obj-$(CONFIG_CMD_SMBIOS) += smbios.o
 obj-$(CONFIG_CMD_TEMPERATURE) += temperature.o
diff --git a/test/cmd/sb.c b/test/cmd/sb.c
new file mode 100644
index 00000000000..cb871dffd57
--- /dev/null
+++ b/test/cmd/sb.c
@@ -0,0 +1,123 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for sb command
+ *
+ * Copyright (C) 2025 Canonical Ltd
+ */
+
+#include <dm.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Basic test of 'sb devon' and 'sb devoff' commands */
+static int dm_test_sb_devon_devoff(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	ofnode root, node;
+
+	/* Find the mmc11 device tree node */
+	root = oftree_root(oftree_default());
+	node = ofnode_find_subnode(root, "mmc11");
+	ut_assert(ofnode_valid(node));
+
+	/* Verify device is not initially bound */
+	ut_assert(device_find_global_by_ofnode(node, &dev));
+
+	/* Enable the device using 'sb devon' */
+	ut_assertok(run_command("sb devon mmc11", 0));
+	ut_assert_nextline("Device 'mmc11' enabled");
+	ut_assert_console_end();
+
+	/* Verify device is now bound and probed */
+	ut_assertok(device_find_global_by_ofnode(node, &dev));
+	ut_assertnonnull(dev);
+	ut_assert(device_active(dev));
+
+	/* Disable the device using 'sb devoff' */
+	ut_assertok(run_command("sb devoff mmc11", 0));
+	ut_assert_nextline("Device 'mmc11' disabled");
+	ut_assert_console_end();
+
+	/* Verify device is no longer bound */
+	ut_assert(device_find_global_by_ofnode(node, &dev));
+
+	return 0;
+}
+DM_TEST(dm_test_sb_devon_devoff, UTF_SCAN_FDT | UTF_CONSOLE);
+
+/* Test 'sb devon' with invalid node */
+static int dm_test_sb_devon_invalid(struct unit_test_state *uts)
+{
+	/* Try to enable non-existent device */
+	ut_asserteq(1, run_command("sb devon nonexistent", 0));
+	ut_assert_nextline("Device tree node 'nonexistent' not found");
+	ut_assert_console_end();
+
+	return 0;
+}
+DM_TEST(dm_test_sb_devon_invalid, UTF_SCAN_FDT | UTF_CONSOLE);
+
+/* Test 'sb devoff' with invalid node */
+static int dm_test_sb_devoff_invalid(struct unit_test_state *uts)
+{
+	/* Try to disable non-existent device */
+	ut_asserteq(1, run_command("sb devoff nonexistent", 0));
+	ut_assert_nextline("Device tree node 'nonexistent' not found");
+	ut_assert_console_end();
+
+	return 0;
+}
+DM_TEST(dm_test_sb_devoff_invalid, UTF_SCAN_FDT | UTF_CONSOLE);
+
+/* Test 'sb devon' on device that's already enabled */
+static int dm_test_sb_devon_already_enabled(struct unit_test_state *uts)
+{
+	/* Enable the device first */
+	ut_assertok(run_command("sb devon mmc11", 0));
+	ut_assert_nextline("Device 'mmc11' enabled");
+	ut_assert_console_end();
+
+	/* Try to enable it again - should fail */
+	ut_asserteq(1, run_command("sb devon mmc11", 0));
+	ut_assert_nextline("Device 'mmc11' is already enabled");
+	ut_assert_console_end();
+
+	/* Clean up - disable the device */
+	ut_assertok(run_command("sb devoff mmc11", 0));
+	ut_assert_nextline("Device 'mmc11' disabled");
+	ut_assert_console_end();
+
+	return 0;
+}
+DM_TEST(dm_test_sb_devon_already_enabled, UTF_SCAN_FDT | UTF_CONSOLE);
+
+/* Test 'sb devoff' on device that's not bound */
+static int dm_test_sb_devoff_not_bound(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	ofnode root, node;
+
+	/* Find the mmc11 device tree node */
+	root = oftree_root(oftree_default());
+	node = ofnode_find_subnode(root, "mmc11");
+	ut_assert(ofnode_valid(node));
+
+	/* Ensure device is not bound (clean up from any previous test) */
+	if (!device_find_global_by_ofnode(node, &dev)) {
+		ut_assertok(run_command("sb devoff mmc11", 0));
+		ut_assert_nextlinen("Device 'mmc11' disabled");
+		ut_assert_console_end();
+	}
+
+	/* Verify device is not bound */
+	ut_assert(device_find_global_by_ofnode(node, &dev));
+
+	/* Try to disable a device that's not bound */
+	ut_asserteq(1, run_command("sb devoff mmc11", 0));
+	ut_assert_nextlinen("Device 'mmc11' not found or not bound");
+	ut_assert_console_end();
+
+	return 0;
+}
+DM_TEST(dm_test_sb_devoff_not_bound, UTF_SCAN_FDT | UTF_CONSOLE);