blob: 71d8b542b28fa891946af8ea370373fbf73bee41 [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);
Sean Edmond0760efe2024-05-08 19:39:01 -070067 /* set to NULL to avoid double-free if DHCP is tried again */
68 pxelinux_configfile = NULL;
Sean Edmondba802862023-04-11 10:48:47 -070069
70 return ret;
71}
72
73/*
Jason Hobbs0e3a5932011-08-31 10:37:30 -050074 * Looks for a pxe file with a name based on the pxeuuid environment variable.
75 *
76 * Returns 1 on success or < 0 on error.
77 */
Simon Glassb0d08db2021-10-14 12:47:56 -060078static int pxe_uuid_path(struct pxe_context *ctx, unsigned long pxefile_addr_r)
Jason Hobbs0e3a5932011-08-31 10:37:30 -050079{
80 char *uuid_str;
81
82 uuid_str = from_env("pxeuuid");
83
84 if (!uuid_str)
85 return -ENOENT;
86
Simon Glassb0d08db2021-10-14 12:47:56 -060087 return get_pxelinux_path(ctx, uuid_str, pxefile_addr_r);
Jason Hobbs0e3a5932011-08-31 10:37:30 -050088}
89
90/*
91 * Looks for a pxe file with a name based on the 'ethaddr' environment
92 * variable.
93 *
94 * Returns 1 on success or < 0 on error.
95 */
Simon Glassb0d08db2021-10-14 12:47:56 -060096static int pxe_mac_path(struct pxe_context *ctx, unsigned long pxefile_addr_r)
Jason Hobbs0e3a5932011-08-31 10:37:30 -050097{
98 char mac_str[21];
99 int err;
100
101 err = format_mac_pxe(mac_str, sizeof(mac_str));
102
103 if (err < 0)
104 return err;
105
Simon Glassb0d08db2021-10-14 12:47:56 -0600106 return get_pxelinux_path(ctx, mac_str, pxefile_addr_r);
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500107}
108
109/*
110 * Looks for pxe files with names based on our IP address. See pxelinux
111 * documentation for details on what these file names look like. We match
112 * that exactly.
113 *
114 * Returns 1 on success or < 0 on error.
115 */
Simon Glassb0d08db2021-10-14 12:47:56 -0600116static int pxe_ipaddr_paths(struct pxe_context *ctx, unsigned long pxefile_addr_r)
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500117{
118 char ip_addr[9];
119 int mask_pos, err;
120
Joe Hershberger5874dec2015-04-08 01:41:01 -0500121 sprintf(ip_addr, "%08X", ntohl(net_ip.s_addr));
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500122
123 for (mask_pos = 7; mask_pos >= 0; mask_pos--) {
Simon Glassb0d08db2021-10-14 12:47:56 -0600124 err = get_pxelinux_path(ctx, ip_addr, pxefile_addr_r);
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500125
126 if (err > 0)
127 return err;
128
129 ip_addr[mask_pos] = '\0';
130 }
131
132 return -ENOENT;
133}
Simon Glass1dacc6f2021-10-14 12:48:11 -0600134
Sean Edmondba802862023-04-11 10:48:47 -0700135int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep, bool use_ipv6)
Simon Glass1dacc6f2021-10-14 12:48:11 -0600136{
137 struct cmd_tbl cmdtp[] = {}; /* dummy */
138 struct pxe_context ctx;
139 int i;
140
141 if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false,
Martyn Welch2c47aac2024-10-09 14:15:39 +0100142 env_get("bootfile"), use_ipv6, false))
Simon Glass1dacc6f2021-10-14 12:48:11 -0600143 return -ENOMEM;
Sean Edmondba802862023-04-11 10:48:47 -0700144
Sean Edmond57867112023-07-25 16:20:30 -0700145 if (IS_ENABLED(CONFIG_BOOTP_PXE_DHCP_OPTION) &&
146 pxelinux_configfile && !use_ipv6) {
147 if (pxe_dhcp_option_path(&ctx, pxefile_addr_r) > 0)
148 goto done;
149
150 goto error_exit;
151 }
152
Sean Edmondba802862023-04-11 10:48:47 -0700153 if (IS_ENABLED(CONFIG_DHCP6_PXE_DHCP_OPTION) &&
154 pxelinux_configfile && use_ipv6) {
155 if (pxe_dhcp_option_path(&ctx, pxefile_addr_r) > 0)
156 goto done;
157
158 goto error_exit;
159 }
160
Simon Glass1dacc6f2021-10-14 12:48:11 -0600161 /*
162 * Keep trying paths until we successfully get a file we're looking
163 * for.
164 */
165 if (pxe_uuid_path(&ctx, pxefile_addr_r) > 0 ||
166 pxe_mac_path(&ctx, pxefile_addr_r) > 0 ||
167 pxe_ipaddr_paths(&ctx, pxefile_addr_r) > 0)
168 goto done;
169
170 i = 0;
171 while (pxe_default_paths[i]) {
172 if (get_pxelinux_path(&ctx, pxe_default_paths[i],
173 pxefile_addr_r) > 0)
174 goto done;
175 i++;
176 }
177
Sean Edmondba802862023-04-11 10:48:47 -0700178error_exit:
Simon Glass1dacc6f2021-10-14 12:48:11 -0600179 pxe_destroy_ctx(&ctx);
180
181 return -ENOENT;
182done:
183 *bootdirp = env_get("bootfile");
184
185 /*
186 * The PXE file size is returned but not the name. It is probably not
187 * that useful.
188 */
189 *sizep = ctx.pxe_file_size;
190 pxe_destroy_ctx(&ctx);
191
192 return 0;
193}
194
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500195/*
196 * Entry point for the 'pxe get' command.
197 * This Follows pxelinux's rules to download a config file from a tftp server.
198 * The file is stored at the location given by the pxefile_addr_r environment
199 * variable, which must be set.
200 *
201 * UUID comes from pxeuuid env variable, if defined
202 * MAC addr comes from ethaddr env variable, if defined
203 * IP
204 *
205 * see http://syslinux.zytor.com/wiki/index.php/PXELINUX
206 *
207 * Returns 0 on success or 1 on error.
208 */
209static int
Simon Glassed38aef2020-05-10 11:40:03 -0600210do_pxe_get(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500211{
212 char *pxefile_addr_str;
Simon Glass1dacc6f2021-10-14 12:48:11 -0600213 ulong pxefile_addr_r;
214 char *fname;
215 ulong size;
216 int ret;
Sean Edmondba802862023-04-11 10:48:47 -0700217 bool use_ipv6 = false;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500218
Sean Edmondba802862023-04-11 10:48:47 -0700219 if (IS_ENABLED(CONFIG_IPV6)) {
220 if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM))
221 use_ipv6 = true;
222
223 if (!(argc == 1 || (argc == 2 && use_ipv6)))
224 return CMD_RET_USAGE;
225 } else {
226 if (argc != 1)
227 return CMD_RET_USAGE;
228 }
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500229
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500230 pxefile_addr_str = from_env("pxefile_addr_r");
231
232 if (!pxefile_addr_str)
233 return 1;
234
Simon Glass1dacc6f2021-10-14 12:48:11 -0600235 ret = strict_strtoul(pxefile_addr_str, 16,
Patrice Chotard7f871652019-11-25 09:07:41 +0100236 (unsigned long *)&pxefile_addr_r);
Simon Glass1dacc6f2021-10-14 12:48:11 -0600237 if (ret < 0)
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500238 return 1;
239
Sean Edmondba802862023-04-11 10:48:47 -0700240 ret = pxe_get(pxefile_addr_r, &fname, &size, use_ipv6);
Simon Glass1dacc6f2021-10-14 12:48:11 -0600241 switch (ret) {
242 case 0:
243 printf("Config file '%s' found\n", fname);
244 break;
245 case -ENOMEM:
Simon Glasse719fe02021-10-14 12:48:04 -0600246 printf("Out of memory\n");
247 return CMD_RET_FAILURE;
Simon Glass1dacc6f2021-10-14 12:48:11 -0600248 default:
249 printf("Config file not found\n");
250 return CMD_RET_FAILURE;
Rob Herringe26e8d62012-12-02 21:00:28 -0600251 }
252
Simon Glass1dacc6f2021-10-14 12:48:11 -0600253 return 0;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500254}
255
256/*
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500257 * Boots a system using a pxe file
258 *
259 * Returns 0 on success, 1 on error.
260 */
261static int
Simon Glassed38aef2020-05-10 11:40:03 -0600262do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500263{
264 unsigned long pxefile_addr_r;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500265 char *pxefile_addr_str;
Simon Glassb0d08db2021-10-14 12:47:56 -0600266 struct pxe_context ctx;
Simon Glass791bbfe2021-10-14 12:48:03 -0600267 int ret;
Sean Edmondba802862023-04-11 10:48:47 -0700268 bool use_ipv6 = false;
269
270 if (IS_ENABLED(CONFIG_IPV6)) {
271 if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM))
272 use_ipv6 = true;
273 }
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500274
Sean Edmondba802862023-04-11 10:48:47 -0700275 if (argc == 1 || (argc == 2 && use_ipv6)) {
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500276 pxefile_addr_str = from_env("pxefile_addr_r");
277 if (!pxefile_addr_str)
278 return 1;
279
Sean Edmondba802862023-04-11 10:48:47 -0700280 } else if (argc == 2 || (argc == 3 && use_ipv6)) {
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500281 pxefile_addr_str = argv[1];
282 } else {
Simon Glassa06dfc72011-12-10 08:44:01 +0000283 return CMD_RET_USAGE;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500284 }
285
286 if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
287 printf("Invalid pxefile address: %s\n", pxefile_addr_str);
288 return 1;
289 }
290
Simon Glasse719fe02021-10-14 12:48:04 -0600291 if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false,
Martyn Welch2c47aac2024-10-09 14:15:39 +0100292 env_get("bootfile"), use_ipv6, false)) {
Simon Glasse719fe02021-10-14 12:48:04 -0600293 printf("Out of memory\n");
294 return CMD_RET_FAILURE;
295 }
Simon Glass791bbfe2021-10-14 12:48:03 -0600296 ret = pxe_process(&ctx, pxefile_addr_r, false);
Simon Glasse719fe02021-10-14 12:48:04 -0600297 pxe_destroy_ctx(&ctx);
Simon Glass791bbfe2021-10-14 12:48:03 -0600298 if (ret)
299 return CMD_RET_FAILURE;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500300
Joe Hershberger290c8992015-04-08 01:41:02 -0500301 copy_filename(net_boot_file_name, "", sizeof(net_boot_file_name));
Stephen Warrenc24cbc22014-07-22 18:06:46 -0600302
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500303 return 0;
304}
305
Simon Glassed38aef2020-05-10 11:40:03 -0600306static struct cmd_tbl cmd_pxe_sub[] = {
Sean Edmondba802862023-04-11 10:48:47 -0700307 U_BOOT_CMD_MKENT(get, 2, 1, do_pxe_get, "", ""),
308 U_BOOT_CMD_MKENT(boot, 3, 1, do_pxe_boot, "", "")
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500309};
310
Simon Glassed38aef2020-05-10 11:40:03 -0600311static int do_pxe(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500312{
Simon Glassed38aef2020-05-10 11:40:03 -0600313 struct cmd_tbl *cp;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500314
315 if (argc < 2)
Simon Glassa06dfc72011-12-10 08:44:01 +0000316 return CMD_RET_USAGE;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500317
318 /* drop initial "pxe" arg */
319 argc--;
320 argv++;
321
322 cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub));
323
324 if (cp)
325 return cp->cmd(cmdtp, flag, argc, argv);
326
Simon Glassa06dfc72011-12-10 08:44:01 +0000327 return CMD_RET_USAGE;
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500328}
329
Sean Edmondba802862023-04-11 10:48:47 -0700330U_BOOT_CMD(pxe, 4, 1, do_pxe,
Bin Mengc44cfba2023-07-31 16:33:23 +0800331 "get and boot from pxe files",
Sean Edmondba802862023-04-11 10:48:47 -0700332 "get [" USE_IP6_CMD_PARAM "] - try to retrieve a pxe file using tftp\n"
333 "pxe boot [pxefile_addr_r] [-ipv6] - boot from the pxe file at pxefile_addr_r\n"
Jason Hobbs0e3a5932011-08-31 10:37:30 -0500334);