@@ -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));
@@ -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``::
@@ -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
new file mode 100644
@@ -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);