pxe.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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. #include <common.h>
  7. #include <command.h>
  8. #include <fs.h>
  9. #include <net.h>
  10. #include <net6.h>
  11. #include <malloc.h>
  12. #include "pxe_utils.h"
  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. static int do_get_tftp(struct pxe_context *ctx, const char *file_path,
  26. char *file_addr, ulong *sizep)
  27. {
  28. char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
  29. int ret;
  30. int num_args;
  31. tftp_argv[1] = file_addr;
  32. tftp_argv[2] = (void *)file_path;
  33. if (ctx->use_ipv6) {
  34. tftp_argv[3] = USE_IP6_CMD_PARAM;
  35. num_args = 4;
  36. } else {
  37. num_args = 3;
  38. }
  39. if (do_tftpb(ctx->cmdtp, 0, num_args, tftp_argv))
  40. return -ENOENT;
  41. ret = pxe_get_file_size(sizep);
  42. if (ret)
  43. return log_msg_ret("tftp", ret);
  44. ctx->pxe_file_size = *sizep;
  45. return 1;
  46. }
  47. /*
  48. * Looks for a pxe file with specified config file name,
  49. * which is received from DHCPv4 option 209 or
  50. * DHCPv6 option 60.
  51. *
  52. * Returns 1 on success or < 0 on error.
  53. */
  54. static int pxe_dhcp_option_path(struct pxe_context *ctx, unsigned long pxefile_addr_r)
  55. {
  56. int ret = get_pxe_file(ctx, pxelinux_configfile, pxefile_addr_r);
  57. free(pxelinux_configfile);
  58. return ret;
  59. }
  60. /*
  61. * Looks for a pxe file with a name based on the pxeuuid environment variable.
  62. *
  63. * Returns 1 on success or < 0 on error.
  64. */
  65. static int pxe_uuid_path(struct pxe_context *ctx, unsigned long pxefile_addr_r)
  66. {
  67. char *uuid_str;
  68. uuid_str = from_env("pxeuuid");
  69. if (!uuid_str)
  70. return -ENOENT;
  71. return get_pxelinux_path(ctx, uuid_str, pxefile_addr_r);
  72. }
  73. /*
  74. * Looks for a pxe file with a name based on the 'ethaddr' environment
  75. * variable.
  76. *
  77. * Returns 1 on success or < 0 on error.
  78. */
  79. static int pxe_mac_path(struct pxe_context *ctx, unsigned long pxefile_addr_r)
  80. {
  81. char mac_str[21];
  82. int err;
  83. err = format_mac_pxe(mac_str, sizeof(mac_str));
  84. if (err < 0)
  85. return err;
  86. return get_pxelinux_path(ctx, mac_str, pxefile_addr_r);
  87. }
  88. /*
  89. * Looks for pxe files with names based on our IP address. See pxelinux
  90. * documentation for details on what these file names look like. We match
  91. * that exactly.
  92. *
  93. * Returns 1 on success or < 0 on error.
  94. */
  95. static int pxe_ipaddr_paths(struct pxe_context *ctx, unsigned long pxefile_addr_r)
  96. {
  97. char ip_addr[9];
  98. int mask_pos, err;
  99. sprintf(ip_addr, "%08X", ntohl(net_ip.s_addr));
  100. for (mask_pos = 7; mask_pos >= 0; mask_pos--) {
  101. err = get_pxelinux_path(ctx, ip_addr, pxefile_addr_r);
  102. if (err > 0)
  103. return err;
  104. ip_addr[mask_pos] = '\0';
  105. }
  106. return -ENOENT;
  107. }
  108. int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep, bool use_ipv6)
  109. {
  110. struct cmd_tbl cmdtp[] = {}; /* dummy */
  111. struct pxe_context ctx;
  112. int i;
  113. if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false,
  114. env_get("bootfile"), use_ipv6))
  115. return -ENOMEM;
  116. if (IS_ENABLED(CONFIG_DHCP6_PXE_DHCP_OPTION) &&
  117. pxelinux_configfile && use_ipv6) {
  118. if (pxe_dhcp_option_path(&ctx, pxefile_addr_r) > 0)
  119. goto done;
  120. goto error_exit;
  121. }
  122. /*
  123. * Keep trying paths until we successfully get a file we're looking
  124. * for.
  125. */
  126. if (pxe_uuid_path(&ctx, pxefile_addr_r) > 0 ||
  127. pxe_mac_path(&ctx, pxefile_addr_r) > 0 ||
  128. pxe_ipaddr_paths(&ctx, pxefile_addr_r) > 0)
  129. goto done;
  130. i = 0;
  131. while (pxe_default_paths[i]) {
  132. if (get_pxelinux_path(&ctx, pxe_default_paths[i],
  133. pxefile_addr_r) > 0)
  134. goto done;
  135. i++;
  136. }
  137. error_exit:
  138. pxe_destroy_ctx(&ctx);
  139. return -ENOENT;
  140. done:
  141. *bootdirp = env_get("bootfile");
  142. /*
  143. * The PXE file size is returned but not the name. It is probably not
  144. * that useful.
  145. */
  146. *sizep = ctx.pxe_file_size;
  147. pxe_destroy_ctx(&ctx);
  148. return 0;
  149. }
  150. /*
  151. * Entry point for the 'pxe get' command.
  152. * This Follows pxelinux's rules to download a config file from a tftp server.
  153. * The file is stored at the location given by the pxefile_addr_r environment
  154. * variable, which must be set.
  155. *
  156. * UUID comes from pxeuuid env variable, if defined
  157. * MAC addr comes from ethaddr env variable, if defined
  158. * IP
  159. *
  160. * see http://syslinux.zytor.com/wiki/index.php/PXELINUX
  161. *
  162. * Returns 0 on success or 1 on error.
  163. */
  164. static int
  165. do_pxe_get(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
  166. {
  167. char *pxefile_addr_str;
  168. ulong pxefile_addr_r;
  169. char *fname;
  170. ulong size;
  171. int ret;
  172. bool use_ipv6 = false;
  173. if (IS_ENABLED(CONFIG_IPV6)) {
  174. if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM))
  175. use_ipv6 = true;
  176. if (!(argc == 1 || (argc == 2 && use_ipv6)))
  177. return CMD_RET_USAGE;
  178. } else {
  179. if (argc != 1)
  180. return CMD_RET_USAGE;
  181. }
  182. pxefile_addr_str = from_env("pxefile_addr_r");
  183. if (!pxefile_addr_str)
  184. return 1;
  185. ret = strict_strtoul(pxefile_addr_str, 16,
  186. (unsigned long *)&pxefile_addr_r);
  187. if (ret < 0)
  188. return 1;
  189. ret = pxe_get(pxefile_addr_r, &fname, &size, use_ipv6);
  190. switch (ret) {
  191. case 0:
  192. printf("Config file '%s' found\n", fname);
  193. break;
  194. case -ENOMEM:
  195. printf("Out of memory\n");
  196. return CMD_RET_FAILURE;
  197. default:
  198. printf("Config file not found\n");
  199. return CMD_RET_FAILURE;
  200. }
  201. return 0;
  202. }
  203. /*
  204. * Boots a system using a pxe file
  205. *
  206. * Returns 0 on success, 1 on error.
  207. */
  208. static int
  209. do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
  210. {
  211. unsigned long pxefile_addr_r;
  212. char *pxefile_addr_str;
  213. struct pxe_context ctx;
  214. int ret;
  215. bool use_ipv6 = false;
  216. if (IS_ENABLED(CONFIG_IPV6)) {
  217. if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM))
  218. use_ipv6 = true;
  219. }
  220. if (argc == 1 || (argc == 2 && use_ipv6)) {
  221. pxefile_addr_str = from_env("pxefile_addr_r");
  222. if (!pxefile_addr_str)
  223. return 1;
  224. } else if (argc == 2 || (argc == 3 && use_ipv6)) {
  225. pxefile_addr_str = argv[1];
  226. } else {
  227. return CMD_RET_USAGE;
  228. }
  229. if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
  230. printf("Invalid pxefile address: %s\n", pxefile_addr_str);
  231. return 1;
  232. }
  233. if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false,
  234. env_get("bootfile"), use_ipv6)) {
  235. printf("Out of memory\n");
  236. return CMD_RET_FAILURE;
  237. }
  238. ret = pxe_process(&ctx, pxefile_addr_r, false);
  239. pxe_destroy_ctx(&ctx);
  240. if (ret)
  241. return CMD_RET_FAILURE;
  242. copy_filename(net_boot_file_name, "", sizeof(net_boot_file_name));
  243. return 0;
  244. }
  245. static struct cmd_tbl cmd_pxe_sub[] = {
  246. U_BOOT_CMD_MKENT(get, 2, 1, do_pxe_get, "", ""),
  247. U_BOOT_CMD_MKENT(boot, 3, 1, do_pxe_boot, "", "")
  248. };
  249. static void __maybe_unused pxe_reloc(void)
  250. {
  251. static int relocated_pxe;
  252. if (!relocated_pxe) {
  253. fixup_cmdtable(cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub));
  254. relocated_pxe = 1;
  255. }
  256. }
  257. static int do_pxe(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
  258. {
  259. struct cmd_tbl *cp;
  260. #if defined(CONFIG_NEEDS_MANUAL_RELOC)
  261. pxe_reloc();
  262. #endif
  263. if (argc < 2)
  264. return CMD_RET_USAGE;
  265. /* drop initial "pxe" arg */
  266. argc--;
  267. argv++;
  268. cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub));
  269. if (cp)
  270. return cp->cmd(cmdtp, flag, argc, argv);
  271. return CMD_RET_USAGE;
  272. }
  273. U_BOOT_CMD(pxe, 4, 1, do_pxe,
  274. "commands to get and boot from pxe files\n"
  275. "To use IPv6 add -ipv6 parameter",
  276. "get [" USE_IP6_CMD_PARAM "] - try to retrieve a pxe file using tftp\n"
  277. "pxe boot [pxefile_addr_r] [-ipv6] - boot from the pxe file at pxefile_addr_r\n"
  278. );
  279. #endif /* CONFIG_CMD_NET */