sandbox: Add os_jump_to_image() to run another executable

For some tests it is useful to be able to run U-Boot again but pass on the
same memory contents. Add a function to achieve this.

Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c
index 98f565e..52e1096 100644
--- a/arch/sandbox/cpu/os.c
+++ b/arch/sandbox/cpu/os.c
@@ -443,3 +443,92 @@
 
 	return 0;
 }
+
+static int make_exec(char *fname, const void *data, int size)
+{
+	int fd;
+
+	strcpy(fname, "/tmp/u-boot.jump.XXXXXX");
+	fd = mkstemp(fname);
+	if (fd < 0)
+		return -ENOENT;
+	if (write(fd, data, size) < 0)
+		return -EIO;
+	close(fd);
+	if (chmod(fname, 0777))
+		return -ENOEXEC;
+
+	return 0;
+}
+
+static int add_args(char ***argvp, const char *add_args[], int count)
+{
+	char **argv;
+	int argc;
+
+	for (argv = *argvp, argc = 0; (*argvp)[argc]; argc++)
+		;
+
+	argv = malloc((argc + count + 1) * sizeof(char *));
+	if (!argv) {
+		printf("Out of memory for %d argv\n", count);
+		return -ENOMEM;
+	}
+	memcpy(argv, *argvp, argc * sizeof(char *));
+	memcpy(argv + argc, add_args, count * sizeof(char *));
+	argv[argc + count] = NULL;
+
+	*argvp = argv;
+	return 0;
+}
+
+int os_jump_to_image(const void *dest, int size)
+{
+	struct sandbox_state *state = state_get_current();
+	char fname[30], mem_fname[30];
+	int fd, err;
+	const char *extra_args[4];
+	char **argv = state->argv;
+#ifdef DEBUG
+	int argc, i;
+#endif
+
+	err = make_exec(fname, dest, size);
+	if (err)
+		return err;
+
+	strcpy(mem_fname, "/tmp/u-boot.mem.XXXXXX");
+	fd = mkstemp(mem_fname);
+	if (fd < 0)
+		return -ENOENT;
+	close(fd);
+	err = os_write_ram_buf(mem_fname);
+	if (err)
+		return err;
+
+	os_fd_restore();
+
+	extra_args[0] = "-j";
+	extra_args[1] = fname;
+	extra_args[2] = "-m";
+	extra_args[3] = mem_fname;
+	err = add_args(&argv, extra_args,
+		       sizeof(extra_args) / sizeof(extra_args[0]));
+	if (err)
+		return err;
+
+#ifdef DEBUG
+	for (i = 0; argv[i]; i++)
+		printf("%d %s\n", i, argv[i]);
+#endif
+
+	if (state_uninit())
+		os_exit(2);
+
+	err = execv(fname, argv);
+	free(argv);
+	if (err)
+		return err;
+
+	return unlink(fname);
+}