summaryrefslogtreecommitdiff
path: root/cmd/sysboot.c
blob: 0ea08fd7b5356137f6df589525bd7c5c17cbe9b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// SPDX-License-Identifier: GPL-2.0+

#include <command.h>
#include <env.h>
#include <fs.h>
#include <pxe_utils.h>
#include <vsprintf.h>

/**
 * struct sysboot_info - useful information for sysboot helpers
 *
 * @fstype: Filesystem type (FS_TYPE_...)
 * @ifname: Interface name (e.g. "ide", "scsi")
 * @dev_part_str is in the format:
 *	<dev>.<hw_part>:<part> where <dev> is the device number,
 *	<hw_part> is the optional hardware partition number and
 *	<part> is the partition number
 */
struct sysboot_info {
	int fstype;
	const char *ifname;
	const char *dev_part_str;
};

static int sysboot_read_file(struct pxe_context *ctx, const char *file_path,
			     char *file_addr, ulong *sizep)
{
	struct sysboot_info *info = ctx->userdata;
	loff_t len_read;
	ulong addr;
	int ret;

	addr = simple_strtoul(file_addr, NULL, 16);
	ret = fs_set_blk_dev(info->ifname, info->dev_part_str, info->fstype);
	if (ret)
		return ret;
	ret = fs_read(file_path, addr, 0, 0, &len_read);
	if (ret)
		return ret;
	*sizep = len_read;

	return 0;
}

/*
 * Boots a system using a local disk syslinux/extlinux file
 *
 * Returns 0 on success, 1 on error.
 */
static int do_sysboot(struct cmd_tbl *cmdtp, int flag, int argc,
		      char *const argv[])
{
	unsigned long pxefile_addr_r;
	struct pxe_context ctx;
	char *pxefile_addr_str;
	struct sysboot_info info;
	char *filename;
	int prompt = 0;
	int ret;

	if (argc > 1 && strstr(argv[1], "-p")) {
		prompt = 1;
		argc--;
		argv++;
	}

	if (argc < 4)
		return cmd_usage(cmdtp);

	if (argc < 5) {
		pxefile_addr_str = from_env("pxefile_addr_r");
		if (!pxefile_addr_str)
			return 1;
	} else {
		pxefile_addr_str = argv[4];
	}

	if (argc < 6) {
		filename = env_get("bootfile");
		if (!filename) {
			printf("Specify a filename or set the ${bootfile} environment variable\n");
			return 1;
		}
	} else {
		filename = argv[5];
		env_set("bootfile", filename);
	}

	if (strstr(argv[3], "ext2")) {
		info.fstype = FS_TYPE_EXT;
	} else if (strstr(argv[3], "fat")) {
		info.fstype = FS_TYPE_FAT;
	} else if (strstr(argv[3], "any")) {
		info.fstype = FS_TYPE_ANY;
	} else {
		printf("Invalid filesystem: %s\n", argv[3]);
		return 1;
	}
	info.ifname = argv[1];
	info.dev_part_str = argv[2];

	if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
		printf("Invalid pxefile address: %s\n", pxefile_addr_str);
		return 1;
	}

	if (pxe_setup_ctx(&ctx, cmdtp, sysboot_read_file, &info, true,
			  filename, false)) {
		printf("Out of memory\n");
		return CMD_RET_FAILURE;
	}

	if (get_pxe_file(&ctx, filename, pxefile_addr_r) < 0) {
		printf("Error reading config file\n");
		pxe_destroy_ctx(&ctx);
		return 1;
	}

	ret = pxe_process(&ctx, pxefile_addr_r, prompt);
	pxe_destroy_ctx(&ctx);
	if (ret)
		return CMD_RET_FAILURE;

	return 0;
}

U_BOOT_CMD(sysboot, 7, 1, do_sysboot,
	   "command to get and boot from syslinux files",
	   "[-p] <interface> <dev[:part]> <ext2|fat|any> [addr] [filename]\n"
	   "    - load and parse syslinux menu file 'filename' from ext2, fat\n"
	   "      or any filesystem on 'dev' on 'interface' to address 'addr'"
);