pxe.c 6.1 KB

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