cmd: Prepare sysboot command independence
[platform/kernel/u-boot.git] / cmd / pxe.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2010-2011 Calxeda, Inc.
4  * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
5  */
6
7 #include <common.h>
8 #include <command.h>
9 #include <fs.h>
10
11 #include "pxe_utils.h"
12
13 #ifdef CONFIG_CMD_NET
14 const char *pxe_default_paths[] = {
15 #ifdef CONFIG_SYS_SOC
16 #ifdef CONFIG_SYS_BOARD
17         "default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC "-" CONFIG_SYS_BOARD,
18 #endif
19         "default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC,
20 #endif
21         "default-" CONFIG_SYS_ARCH,
22         "default",
23         NULL
24 };
25
26 static int do_get_tftp(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
27 {
28         char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
29
30         tftp_argv[1] = file_addr;
31         tftp_argv[2] = (void *)file_path;
32
33         if (do_tftpb(cmdtp, 0, 3, tftp_argv))
34                 return -ENOENT;
35
36         return 1;
37 }
38 #endif
39
40 static char *fs_argv[5];
41
42 static int do_get_ext2(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
43 {
44 #ifdef CONFIG_CMD_EXT2
45         fs_argv[0] = "ext2load";
46         fs_argv[3] = file_addr;
47         fs_argv[4] = (void *)file_path;
48
49         if (!do_ext2load(cmdtp, 0, 5, fs_argv))
50                 return 1;
51 #endif
52         return -ENOENT;
53 }
54
55 static int do_get_fat(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
56 {
57 #ifdef CONFIG_CMD_FAT
58         fs_argv[0] = "fatload";
59         fs_argv[3] = file_addr;
60         fs_argv[4] = (void *)file_path;
61
62         if (!do_fat_fsload(cmdtp, 0, 5, fs_argv))
63                 return 1;
64 #endif
65         return -ENOENT;
66 }
67
68 static int do_get_any(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
69 {
70 #ifdef CONFIG_CMD_FS_GENERIC
71         fs_argv[0] = "load";
72         fs_argv[3] = file_addr;
73         fs_argv[4] = (void *)file_path;
74
75         if (!do_load(cmdtp, 0, 5, fs_argv, FS_TYPE_ANY))
76                 return 1;
77 #endif
78         return -ENOENT;
79 }
80
81 #ifdef CONFIG_CMD_NET
82
83 /*
84  * Looks for a pxe file with a name based on the pxeuuid environment variable.
85  *
86  * Returns 1 on success or < 0 on error.
87  */
88 static int pxe_uuid_path(cmd_tbl_t *cmdtp, unsigned long pxefile_addr_r)
89 {
90         char *uuid_str;
91
92         uuid_str = from_env("pxeuuid");
93
94         if (!uuid_str)
95                 return -ENOENT;
96
97         return get_pxelinux_path(cmdtp, uuid_str, pxefile_addr_r);
98 }
99
100 /*
101  * Looks for a pxe file with a name based on the 'ethaddr' environment
102  * variable.
103  *
104  * Returns 1 on success or < 0 on error.
105  */
106 static int pxe_mac_path(cmd_tbl_t *cmdtp, unsigned long pxefile_addr_r)
107 {
108         char mac_str[21];
109         int err;
110
111         err = format_mac_pxe(mac_str, sizeof(mac_str));
112
113         if (err < 0)
114                 return err;
115
116         return get_pxelinux_path(cmdtp, mac_str, pxefile_addr_r);
117 }
118
119 /*
120  * Looks for pxe files with names based on our IP address. See pxelinux
121  * documentation for details on what these file names look like.  We match
122  * that exactly.
123  *
124  * Returns 1 on success or < 0 on error.
125  */
126 static int pxe_ipaddr_paths(cmd_tbl_t *cmdtp, unsigned long pxefile_addr_r)
127 {
128         char ip_addr[9];
129         int mask_pos, err;
130
131         sprintf(ip_addr, "%08X", ntohl(net_ip.s_addr));
132
133         for (mask_pos = 7; mask_pos >= 0;  mask_pos--) {
134                 err = get_pxelinux_path(cmdtp, ip_addr, pxefile_addr_r);
135
136                 if (err > 0)
137                         return err;
138
139                 ip_addr[mask_pos] = '\0';
140         }
141
142         return -ENOENT;
143 }
144 /*
145  * Entry point for the 'pxe get' command.
146  * This Follows pxelinux's rules to download a config file from a tftp server.
147  * The file is stored at the location given by the pxefile_addr_r environment
148  * variable, which must be set.
149  *
150  * UUID comes from pxeuuid env variable, if defined
151  * MAC addr comes from ethaddr env variable, if defined
152  * IP
153  *
154  * see http://syslinux.zytor.com/wiki/index.php/PXELINUX
155  *
156  * Returns 0 on success or 1 on error.
157  */
158 static int
159 do_pxe_get(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
160 {
161         char *pxefile_addr_str;
162         unsigned long pxefile_addr_r;
163         int err, i = 0;
164
165         do_getfile = do_get_tftp;
166
167         if (argc != 1)
168                 return CMD_RET_USAGE;
169
170         pxefile_addr_str = from_env("pxefile_addr_r");
171
172         if (!pxefile_addr_str)
173                 return 1;
174
175         err = strict_strtoul(pxefile_addr_str, 16,
176                                 (unsigned long *)&pxefile_addr_r);
177         if (err < 0)
178                 return 1;
179
180         /*
181          * Keep trying paths until we successfully get a file we're looking
182          * for.
183          */
184         if (pxe_uuid_path(cmdtp, pxefile_addr_r) > 0 ||
185             pxe_mac_path(cmdtp, pxefile_addr_r) > 0 ||
186             pxe_ipaddr_paths(cmdtp, pxefile_addr_r) > 0) {
187                 printf("Config file found\n");
188
189                 return 0;
190         }
191
192         while (pxe_default_paths[i]) {
193                 if (get_pxelinux_path(cmdtp, pxe_default_paths[i],
194                                       pxefile_addr_r) > 0) {
195                         printf("Config file found\n");
196                         return 0;
197                 }
198                 i++;
199         }
200
201         printf("Config file not found\n");
202
203         return 1;
204 }
205
206 /*
207  * Boots a system using a pxe file
208  *
209  * Returns 0 on success, 1 on error.
210  */
211 static int
212 do_pxe_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
213 {
214         unsigned long pxefile_addr_r;
215         struct pxe_menu *cfg;
216         char *pxefile_addr_str;
217
218         do_getfile = do_get_tftp;
219
220         if (argc == 1) {
221                 pxefile_addr_str = from_env("pxefile_addr_r");
222                 if (!pxefile_addr_str)
223                         return 1;
224
225         } else if (argc == 2) {
226                 pxefile_addr_str = argv[1];
227         } else {
228                 return CMD_RET_USAGE;
229         }
230
231         if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
232                 printf("Invalid pxefile address: %s\n", pxefile_addr_str);
233                 return 1;
234         }
235
236         cfg = parse_pxefile(cmdtp, pxefile_addr_r);
237
238         if (cfg == NULL) {
239                 printf("Error parsing config file\n");
240                 return 1;
241         }
242
243         handle_pxe_menu(cmdtp, cfg);
244
245         destroy_pxe_menu(cfg);
246
247         copy_filename(net_boot_file_name, "", sizeof(net_boot_file_name));
248
249         return 0;
250 }
251
252 static cmd_tbl_t cmd_pxe_sub[] = {
253         U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""),
254         U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "")
255 };
256
257 static int do_pxe(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
258 {
259         cmd_tbl_t *cp;
260
261         if (argc < 2)
262                 return CMD_RET_USAGE;
263
264         is_pxe = true;
265
266         /* drop initial "pxe" arg */
267         argc--;
268         argv++;
269
270         cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub));
271
272         if (cp)
273                 return cp->cmd(cmdtp, flag, argc, argv);
274
275         return CMD_RET_USAGE;
276 }
277
278 U_BOOT_CMD(
279         pxe, 3, 1, do_pxe,
280         "commands to get and boot from pxe files",
281         "get - try to retrieve a pxe file using tftp\npxe "
282         "boot [pxefile_addr_r] - boot from the pxe file at pxefile_addr_r\n"
283 );
284 #endif
285
286 /*
287  * Boots a system using a local disk syslinux/extlinux file
288  *
289  * Returns 0 on success, 1 on error.
290  */
291 static int do_sysboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
292 {
293         unsigned long pxefile_addr_r;
294         struct pxe_menu *cfg;
295         char *pxefile_addr_str;
296         char *filename;
297         int prompt = 0;
298
299         is_pxe = false;
300
301         if (argc > 1 && strstr(argv[1], "-p")) {
302                 prompt = 1;
303                 argc--;
304                 argv++;
305         }
306
307         if (argc < 4)
308                 return cmd_usage(cmdtp);
309
310         if (argc < 5) {
311                 pxefile_addr_str = from_env("pxefile_addr_r");
312                 if (!pxefile_addr_str)
313                         return 1;
314         } else {
315                 pxefile_addr_str = argv[4];
316         }
317
318         if (argc < 6)
319                 filename = env_get("bootfile");
320         else {
321                 filename = argv[5];
322                 env_set("bootfile", filename);
323         }
324
325         if (strstr(argv[3], "ext2"))
326                 do_getfile = do_get_ext2;
327         else if (strstr(argv[3], "fat"))
328                 do_getfile = do_get_fat;
329         else if (strstr(argv[3], "any"))
330                 do_getfile = do_get_any;
331         else {
332                 printf("Invalid filesystem: %s\n", argv[3]);
333                 return 1;
334         }
335         fs_argv[1] = argv[1];
336         fs_argv[2] = argv[2];
337
338         if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
339                 printf("Invalid pxefile address: %s\n", pxefile_addr_str);
340                 return 1;
341         }
342
343         if (get_pxe_file(cmdtp, filename, pxefile_addr_r) < 0) {
344                 printf("Error reading config file\n");
345                 return 1;
346         }
347
348         cfg = parse_pxefile(cmdtp, pxefile_addr_r);
349
350         if (cfg == NULL) {
351                 printf("Error parsing config file\n");
352                 return 1;
353         }
354
355         if (prompt)
356                 cfg->prompt = 1;
357
358         handle_pxe_menu(cmdtp, cfg);
359
360         destroy_pxe_menu(cfg);
361
362         return 0;
363 }
364
365 U_BOOT_CMD(
366         sysboot, 7, 1, do_sysboot,
367         "command to get and boot from syslinux files",
368         "[-p] <interface> <dev[:part]> <ext2|fat|any> [addr] [filename]\n"
369         "    - load and parse syslinux menu file 'filename' from ext2, fat\n"
370         "      or any filesystem on 'dev' on 'interface' to address 'addr'"
371 );