From: Simon Glass <sjg@chromium.org>
Extend the source command to accept VFS paths:
source /boot/boot.scr
source /host/setup.cmd
When the argument contains a '/', it is treated as a VFS path. The
file is loaded into memory and checked for a mkimage header. If
present, cmd_source_script() handles it as a FIT or legacy image
script. Otherwise, the file is executed as a plain text command list
via run_command_list().
This is the key integration point for using VFS in boot scripts.
Signed-off-by: Simon Glass <sjg@chromium.org>
---
cmd/source.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++
test/dm/fs.c | 33 ++++++++++++++++++++++++
2 files changed, 105 insertions(+)
@@ -16,13 +16,78 @@
#include <command.h>
#include <env.h>
+#include <dm.h>
+#include <file.h>
+#include <fs_common.h>
#include <image.h>
#include <log.h>
#include <malloc.h>
#include <mapmem.h>
+#include <vfs.h>
#include <asm/byteorder.h>
#include <asm/io.h>
+#if IS_ENABLED(CONFIG_VFS)
+/**
+ * source_vfs_file() - Load and execute a script from a VFS path
+ *
+ * Loads the file into memory. If it has a valid image header, runs it
+ * via cmd_source_script(). Otherwise treats it as a plain text command
+ * list.
+ *
+ * @path: VFS path to the script file
+ * Return: 0 on success, non-zero on error
+ */
+static int source_vfs_file(const char *path)
+{
+ struct file_uc_priv *uc_priv;
+ struct udevice *fil;
+ long actual;
+ char *buf;
+ ulong addr;
+ int ret;
+
+ ret = vfs_open_file(path, DIR_O_RDONLY, &fil);
+ if (ret) {
+ printf("## Cannot find script '%s'\n", path);
+ return CMD_RET_FAILURE;
+ }
+
+ uc_priv = dev_get_uclass_priv(fil);
+ buf = malloc(uc_priv->size + 1);
+ if (!buf)
+ return CMD_RET_FAILURE;
+
+ actual = file_read_at(fil, buf, 0, uc_priv->size);
+ if (actual < 0) {
+ free(buf);
+ printf("## Cannot read script '%s'\n", path);
+ return CMD_RET_FAILURE;
+ }
+
+ buf[actual] = '\0';
+
+ /* Check for a mkimage header (legacy or FIT) */
+ if (actual >= sizeof(struct legacy_img_hdr) &&
+ image_get_magic((struct legacy_img_hdr *)buf) == IH_MAGIC) {
+ addr = map_to_sysmem(buf);
+ ret = cmd_source_script(addr, NULL, NULL);
+ } else {
+ /* Plain text command list */
+ ret = run_command_list(buf, actual, 0);
+ }
+
+ free(buf);
+
+ return ret;
+}
+
+static bool is_vfs_path(const char *arg)
+{
+ return arg && (arg[0] == '/' || strchr(arg, '/'));
+}
+#endif
+
static int do_source(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
@@ -30,6 +95,13 @@ static int do_source(struct cmd_tbl *cmdtp, int flag, int argc,
int rcode;
const char *fit_uname = NULL, *confname = NULL;
+#if IS_ENABLED(CONFIG_VFS)
+ if (argc >= 2 && is_vfs_path(argv[1])) {
+ printf("## Executing script from '%s'\n", argv[1]);
+ return source_vfs_file(argv[1]);
+ }
+#endif
+
/* Find script image */
if (argc < 2) {
addr = CONFIG_SYS_LOAD_ADDR;
@@ -6,6 +6,7 @@
*/
#include <console.h>
+#include <env.h>
#include <dir.h>
#include <dm.h>
#include <file.h>
@@ -793,6 +794,38 @@ static int dm_test_vfs_symlink(struct unit_test_state *uts)
}
DM_TEST(dm_test_vfs_symlink, UTF_SCAN_FDT);
+/* Test source command with VFS path */
+static int dm_test_vfs_source(struct unit_test_state *uts)
+{
+ ut_assertok(vfs_init());
+
+ ut_assertok(run_command("mount hostfs /host", 0));
+ ut_assert_console_end();
+
+ /* Create a simple script that sets an env var */
+ memcpy(map_sysmem(0x1000, 18), "setenv vfs_test ok", 18);
+ ut_assertok(run_command("save 1000 /host/.vfs_script 12", 0));
+ ut_assert_nextline("18 bytes written");
+ ut_assert_console_end();
+
+ /* Run it via source */
+ ut_assertok(run_command("source /host/.vfs_script", 0));
+ ut_assert_nextlinen("## Executing script");
+ ut_assert_console_end();
+
+ /* Verify the env var was set */
+ ut_asserteq_str("ok", env_get("vfs_test"));
+
+ os_unlink(".vfs_script");
+ env_set("vfs_test", NULL);
+
+ ut_assertok(run_command("umount /host", 0));
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_vfs_source, UTF_SCAN_FDT);
+
/* Test the ln command via VFS */
static int dm_test_vfs_ln(struct unit_test_state *uts)
{