blob: 0f26b3b421971943e64ab79384aeeec111f5834c [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Jason Hobbs0e3a5932011-08-31 10:37:30 -05002/*
3 * Copyright 2010-2011 Calxeda, Inc.
Bryan Wua1d715b2014-07-31 17:39:59 -07004 * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
Jason Hobbs0e3a5932011-08-31 10:37:30 -05005 */
Wolfgang Denkd79de1d2013-07-08 09:37:19 +02006
Jason Hobbs0e3a5932011-08-31 10:37:30 -05007#include <command.h>
Simon Glass1b3f75f2019-12-28 10:44:44 -07008#include <fs.h>
Simon Glass6e51ee12019-12-28 10:44:43 -07009#include <net.h>
Sean Edmondba802862023-04-11 10:48:47 -070010#include <net6.h>
11#include <malloc.h>
Tom Rinidec7ea02024-05-20 13:35:03 -060012#include <vsprintf.h>
Jason Hobbs0e3a5932011-08-31 10:37:30 -050013
Patrice Chotard17e88042019-11-25 09:07:37 +010014#include "pxe_utils.h"
Jason Hobbs0e3a5932011-08-31 10:37:30 -050015
Rob Herringe26e8d62012-12-02 21:00:28 -060016const char *pxe_default_paths[] = {
Joe Hershberger921a71d2013-06-24 17:21:04 -050017#ifdef CONFIG_SYS_SOC
Marek BehĂșn016ed132019-05-02 15:29:12 +020018#ifdef CONFIG_SYS_BOARD
19 "default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC "-" CONFIG_SYS_BOARD,
20#endif
Rob Herringe26e8d62012-12-02 21:00:28 -060021 "default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC,
Joe Hershberger921a71d2013-06-24 17:21:04 -050022#endif
Rob Herringe26e8d62012-12-02 21:00:28 -060023 "default-" CONFIG_SYS_ARCH,
24 "default",
25 NULL
26};
27
Simon Glass44a20ef2021-10-14 12:47:57 -060028static int do_get_tftp(struct pxe_context *ctx, const char *file_path,
Simon Glassa686ba62024-11-15 16:19:19 -070029 char *file_addr, enum bootflow_img_t type, ulong *sizep)
Rob Herringeee675f2012-05-25 10:47:39 +000030{
31 char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
Simon Glassa9401b92021-10-14 12:48:08 -060032 int ret;
Sean Edmondba802862023-04-11 10:48:47 -070033 int num_args;
Rob Herringeee675f2012-05-25 10:47:39 +000034
35 tftp_argv[1] = file_addr;
Rob Herring824901c2012-12-02 21:00:21 -060036 tftp_argv[2] = (void *)file_path;
Sean Edmondba802862023-04-11 10:48:47 -070037 if (ctx->use_ipv6) {
38 tftp_argv[3] = USE_IP6_CMD_PARAM;
39 num_args = 4;
40 } else {
41 num_args = 3;
42 }
Rob Herringeee675f2012-05-25 10:47:39 +000043
Sean Edmondba802862023-04-11 10:48:47 -070044 if (do_tftpb(ctx->cmdtp, 0, num_args, tftp_argv))
Rob Herringeee675f2012-05-25 10:47:39 +000045 return -ENOENT;
Sean Edmondba802862023-04-11 10:48:47 -070046
Simon Glassa9401b92021-10-14 12:48:08 -060047 ret = pxe_get_file_size(sizep);
48 if (ret)
49 return log_msg_ret("tftp", ret);
50 ctx->pxe_file_size = *sizep;
Rob Herringeee675f2012-05-25 10:47:39 +000051
52 return 1;
53}
Stephen Warren857291b2014-02-05 20:49:20 -070054
Jason Hobbs0e3a5932011-08-31 10:37:30 -050055/*
Sean Edmondba802862023-04-11 10:48:47 -070056 * Looks for a pxe file with specified config file name,
57 * which is received from DHCPv4 option 209 or
58 * DHCPv6 option 60.
59 *
60 * Returns 1 on success or < 0 on error.
61 */
62static int pxe_dhcp_option_path(struct pxe_context *ctx, unsigned long pxefile_addr_r)
63{
64 int ret = get_pxe_file(ctx, pxelinux_configfile, pxefile_addr_r);
65
66 free(pxelinux_configfile);
67
68 return ret;
69}
70
71/*
Jason Hobbs0e3a5932011-08-31 10:37:30 -050072 * Looks for a pxe file with a name based on the pxeuuid environment variable.
73 *
74 * Returns 1 on success or < 0 on error.
75 */
Simon Glassb0d08db2021-10-14 12:47:56 -060076static int pxe_uuid_path(struct pxe_context *ctx, unsigned long pxefile_addr_r)
Jason Hobbs0e3a5932011-08-31 10:37:30 -050077{
78 char *uuid_str;
79
80 uuid_str = from_env("pxeuuid");
81
82 if (!uuid_str)
83 return -ENOENT;
84
Simon Glassb0d08db2021-10-14 12:47:56 -060085 return get_pxelinux_path(ctx, uuid_str, pxefile_addr_r);
Jason Hobbs0e3a5932011-08-31 10:37:30 -050086}
87
88/*
89 * Looks for a pxe file with a name based on the 'ethaddr' environment
90 * variable.
91 *
92 * Returns 1 on success or < 0 on error.
93 */
Simon Glassb0d08db2021-10-14 12:47:56 -060094static int pxe_mac_path(struct pxe_context *ctx, unsigned long pxefile_addr_r)
Jason Hobbs0e3a5932011-08-31 10:37:30 -050095{
96 char mac_str[21];
97 int err;
98
99 err = format_mac_pxe(mac_str, sizeof(mac_str));
100
101 if (err < 0)
102 return err;
103
Simon Glassb0d08db2021-10-14 12:47:56 -0600104 return get_pxelinux_path(ctx, mac_str, pxefile_addr_r);
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500105}
106
107/*
108 * Looks for pxe files with names based on our IP address. See pxelinux
109 * documentation for details on what these file names look like. We match
110 * that exactly.
111 *
112 * Returns 1 on success or < 0 on error.
113 */
Simon Glassb0d08db2021-10-14 12:47:56 -0600114static int pxe_ipaddr_paths(struct pxe_context *ctx, unsigned long pxefile_addr_r)
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500115{
116 char ip_addr[9];
117 int mask_pos, err;
118
Joe Hershberger5874dec2015-04-08 01:41:01 -0500119 sprintf(ip_addr, "%08X", ntohl(net_ip.s_addr));
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500120
121 for (mask_pos = 7; mask_pos >= 0; mask_pos--) {
Simon Glassb0d08db2021-10-14 12:47:56 -0600122 err = get_pxelinux_path(ctx, ip_addr, pxefile_addr_r);
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500123
124 if (err > 0)
125 return err;
126
127 ip_addr[mask_pos] = '\0';
128 }
129
130 return -ENOENT;
131}
Simon Glass1dacc6f2021-10-14 12:48:11 -0600132
Sean Edmondba802862023-04-11 10:48:47 -0700133int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep, bool use_ipv6)
Simon Glass1dacc6f2021-10-14 12:48:11 -0600134{
135 struct cmd_tbl cmdtp[] = {}; /* dummy */
136 struct pxe_context ctx;
137 int i;
138
139 if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false,
Martyn Welch2c47aac2024-10-09 14:15:39 +0100140 env_get("bootfile"), use_ipv6, false))
Simon Glass1dacc6f2021-10-14 12:48:11 -0600141 return -ENOMEM;
Sean Edmondba802862023-04-11 10:48:47 -0700142
Sean Edmond57867112023-07-25 16:20:30 -0700143 if (IS_ENABLED(CONFIG_BOOTP_PXE_DHCP_OPTION) &&
144 pxelinux_configfile && !use_ipv6) {
145 if (pxe_dhcp_option_path(&ctx, pxefile_addr_r) > 0)
146 goto done;
147
148 goto error_exit;
149 }
150
Sean Edmondba802862023-04-11 10:48:47 -0700151 if (IS_ENABLED(CONFIG_DHCP6_PXE_DHCP_OPTION) &&
152 pxelinux_configfile && use_ipv6) {
153 if (pxe_dhcp_option_path(&ctx, pxefile_addr_r) > 0)
154 goto done;
155
156 goto error_exit;
157 }
158
Simon Glass1dacc6f2021-10-14 12:48:11 -0600159 /*
160 * Keep trying paths until we successfully get a file we're looking
161 * for.
162 */
163 if (pxe_uuid_path(&ctx, pxefile_addr_r) > 0 ||
164 pxe_mac_path(&ctx, pxefile_addr_r) > 0 ||
165 pxe_ipaddr_paths(&ctx, pxefile_addr_r) > 0)
166 goto done;
167
168 i = 0;
169 while (pxe_default_paths[i]) {
170 if (get_pxelinux_path(&ctx, pxe_default_paths[i],
171 pxefile_addr_r) > 0)
172 goto done;
173 i++;
174 }
175
Sean Edmondba802862023-04-11 10:48:47 -0700176error_exit:
Simon Glass1dacc6f2021-10-14 12:48:11 -0600177 pxe_destroy_ctx(&ctx);
178
179 return -ENOENT;
180done:
181 *bootdirp = env_get("bootfile");
182
183 /*
184 * The PXE file size is returned but not the name. It is probably not
185 * that useful.
186 */
187 *sizep = ctx.pxe_file_size;
188 pxe_destroy_ctx(&ctx);
189
190 return 0;
191}
192
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500193/*
194 * Entry point for the 'pxe get' command.
195 * This Follows pxelinux's rules to download a config file from a tftp server.
196 * The file is stored at the location given by the pxefile_addr_r environment
197 * variable, which must be set.
198 *
199 * UUID comes from pxeuuid env variable, if defined
200 * MAC addr comes from ethaddr env variable, if defined
201 * IP
202 *
203 * see http://syslinux.zytor.com/wiki/index.php/PXELINUX
204 *
205 * Returns 0 on success or 1 on error.
206 */
207static int
Simon Glassed38aef2020-05-10 11:40:03 -0600208do_pxe_get(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500209{
210 char *pxefile_addr_str;
Simon Glass1dacc6f2021-10-14 12:48:11 -0600211 ulong pxefile_addr_r;
212 char *fname;
213 ulong size;
214 int ret;
Sean Edmondba802862023-04-11 10:48:47 -0700215 bool use_ipv6 = false;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500216
Sean Edmondba802862023-04-11 10:48:47 -0700217 if (IS_ENABLED(CONFIG_IPV6)) {
218 if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM))
219 use_ipv6 = true;
220
221 if (!(argc == 1 || (argc == 2 && use_ipv6)))
222 return CMD_RET_USAGE;
223 } else {
224 if (argc != 1)
225 return CMD_RET_USAGE;
226 }
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500227
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500228 pxefile_addr_str = from_env("pxefile_addr_r");
229
230 if (!pxefile_addr_str)
231 return 1;
232
Simon Glass1dacc6f2021-10-14 12:48:11 -0600233 ret = strict_strtoul(pxefile_addr_str, 16,
Patrice Chotard7f871652019-11-25 09:07:41 +0100234 (unsigned long *)&pxefile_addr_r);
Simon Glass1dacc6f2021-10-14 12:48:11 -0600235 if (ret < 0)
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500236 return 1;
237
Sean Edmondba802862023-04-11 10:48:47 -0700238 ret = pxe_get(pxefile_addr_r, &fname, &size, use_ipv6);
Simon Glass1dacc6f2021-10-14 12:48:11 -0600239 switch (ret) {
240 case 0:
241 printf("Config file '%s' found\n", fname);
242 break;
243 case -ENOMEM:
Simon Glasse719fe02021-10-14 12:48:04 -0600244 printf("Out of memory\n");
245 return CMD_RET_FAILURE;
Simon Glass1dacc6f2021-10-14 12:48:11 -0600246 default:
247 printf("Config file not found\n");
248 return CMD_RET_FAILURE;
Rob Herringe26e8d62012-12-02 21:00:28 -0600249 }
250
Simon Glass1dacc6f2021-10-14 12:48:11 -0600251 return 0;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500252}
253
254/*
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500255 * Boots a system using a pxe file
256 *
257 * Returns 0 on success, 1 on error.
258 */
259static int
Simon Glassed38aef2020-05-10 11:40:03 -0600260do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500261{
262 unsigned long pxefile_addr_r;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500263 char *pxefile_addr_str;
Simon Glassb0d08db2021-10-14 12:47:56 -0600264 struct pxe_context ctx;
Simon Glass791bbfe2021-10-14 12:48:03 -0600265 int ret;
Sean Edmondba802862023-04-11 10:48:47 -0700266 bool use_ipv6 = false;
267
268 if (IS_ENABLED(CONFIG_IPV6)) {
269 if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM))
270 use_ipv6 = true;
271 }
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500272
Sean Edmondba802862023-04-11 10:48:47 -0700273 if (argc == 1 || (argc == 2 && use_ipv6)) {
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500274 pxefile_addr_str = from_env("pxefile_addr_r");
275 if (!pxefile_addr_str)
276 return 1;
277
Sean Edmondba802862023-04-11 10:48:47 -0700278 } else if (argc == 2 || (argc == 3 && use_ipv6)) {
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500279 pxefile_addr_str = argv[1];
280 } else {
Simon Glassa06dfc72011-12-10 08:44:01 +0000281 return CMD_RET_USAGE;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500282 }
283
284 if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
285 printf("Invalid pxefile address: %s\n", pxefile_addr_str);
286 return 1;
287 }
288
Simon Glasse719fe02021-10-14 12:48:04 -0600289 if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false,
Martyn Welch2c47aac2024-10-09 14:15:39 +0100290 env_get("bootfile"), use_ipv6, false)) {
Simon Glasse719fe02021-10-14 12:48:04 -0600291 printf("Out of memory\n");
292 return CMD_RET_FAILURE;
293 }
Simon Glass791bbfe2021-10-14 12:48:03 -0600294 ret = pxe_process(&ctx, pxefile_addr_r, false);
Simon Glasse719fe02021-10-14 12:48:04 -0600295 pxe_destroy_ctx(&ctx);
Simon Glass791bbfe2021-10-14 12:48:03 -0600296 if (ret)
297 return CMD_RET_FAILURE;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500298
Joe Hershberger290c8992015-04-08 01:41:02 -0500299 copy_filename(net_boot_file_name, "", sizeof(net_boot_file_name));
Stephen Warrenc24cbc22014-07-22 18:06:46 -0600300
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500301 return 0;
302}
303
Simon Glassed38aef2020-05-10 11:40:03 -0600304static struct cmd_tbl cmd_pxe_sub[] = {
Sean Edmondba802862023-04-11 10:48:47 -0700305 U_BOOT_CMD_MKENT(get, 2, 1, do_pxe_get, "", ""),
306 U_BOOT_CMD_MKENT(boot, 3, 1, do_pxe_boot, "", "")
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500307};
308
Simon Glassed38aef2020-05-10 11:40:03 -0600309static int do_pxe(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500310{
Simon Glassed38aef2020-05-10 11:40:03 -0600311 struct cmd_tbl *cp;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500312
313 if (argc < 2)
Simon Glassa06dfc72011-12-10 08:44:01 +0000314 return CMD_RET_USAGE;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500315
316 /* drop initial "pxe" arg */
317 argc--;
318 argv++;
319
320 cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub));
321
322 if (cp)
323 return cp->cmd(cmdtp, flag, argc, argv);
324
Simon Glassa06dfc72011-12-10 08:44:01 +0000325 return CMD_RET_USAGE;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500326}
327
Sean Edmondba802862023-04-11 10:48:47 -0700328U_BOOT_CMD(pxe, 4, 1, do_pxe,
Bin Mengc44cfba2023-07-31 16:33:23 +0800329 "get and boot from pxe files",
Sean Edmondba802862023-04-11 10:48:47 -0700330 "get [" USE_IP6_CMD_PARAM "] - try to retrieve a pxe file using tftp\n"
331 "pxe boot [pxefile_addr_r] [-ipv6] - boot from the pxe file at pxefile_addr_r\n"
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500332);