eth_bootdev.c 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Bootdev for ethernet (uses PXE)
  4. *
  5. * Copyright 2021 Google LLC
  6. * Written by Simon Glass <sjg@chromium.org>
  7. */
  8. #define LOG_CATEGORY UCLASS_BOOTSTD
  9. #include <common.h>
  10. #include <bootdev.h>
  11. #include <bootflow.h>
  12. #include <command.h>
  13. #include <bootmeth.h>
  14. #include <dm.h>
  15. #include <extlinux.h>
  16. #include <init.h>
  17. #include <log.h>
  18. #include <net.h>
  19. #include <test/test.h>
  20. static int eth_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
  21. struct bootflow *bflow)
  22. {
  23. char name[60];
  24. int ret;
  25. /* Must be an Ethernet device */
  26. ret = bootflow_iter_check_net(iter);
  27. if (ret)
  28. return log_msg_ret("net", ret);
  29. ret = bootmeth_check(bflow->method, iter);
  30. if (ret)
  31. return log_msg_ret("check", ret);
  32. /*
  33. * Like extlinux boot, this assumes there is only one Ethernet device.
  34. * In this case, that means that @eth is ignored
  35. */
  36. snprintf(name, sizeof(name), "%s.%d", dev->name, iter->part);
  37. bflow->name = strdup(name);
  38. if (!bflow->name)
  39. return log_msg_ret("name", -ENOMEM);
  40. /* See distro_pxe_read_bootflow() for the standard impl of this */
  41. log_debug("dhcp complete - reading bootflow with method '%s'\n",
  42. bflow->method->name);
  43. ret = bootmeth_read_bootflow(bflow->method, bflow);
  44. log_debug("reading bootflow returned %d\n", ret);
  45. if (ret)
  46. return log_msg_ret("method", ret);
  47. return 0;
  48. }
  49. static int eth_bootdev_bind(struct udevice *dev)
  50. {
  51. struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
  52. ucp->prio = BOOTDEVP_6_NET_BASE;
  53. return 0;
  54. }
  55. static int eth_bootdev_hunt(struct bootdev_hunter *info, bool show)
  56. {
  57. int ret;
  58. if (!test_eth_enabled())
  59. return 0;
  60. /* init PCI first since this is often used to provide Ehternet */
  61. if (IS_ENABLED(CONFIG_PCI)) {
  62. ret = pci_init();
  63. if (ret)
  64. log_warning("Failed to init PCI (%dE)\n", ret);
  65. }
  66. /*
  67. * Ethernet devices can also come from USB, but that is a higher
  68. * priority (BOOTDEVP_5_SCAN_SLOW) than ethernet, so it should have been
  69. * enumerated already. If something like 'bootflow scan dhcp' is used
  70. * then the user will need to run 'usb start' first.
  71. */
  72. if (IS_ENABLED(CONFIG_CMD_DHCP)) {
  73. ret = dhcp_run(0, NULL, false);
  74. if (ret)
  75. return -EINVAL;
  76. }
  77. return 0;
  78. }
  79. struct bootdev_ops eth_bootdev_ops = {
  80. .get_bootflow = eth_get_bootflow,
  81. };
  82. static const struct udevice_id eth_bootdev_ids[] = {
  83. { .compatible = "u-boot,bootdev-eth" },
  84. { }
  85. };
  86. U_BOOT_DRIVER(eth_bootdev) = {
  87. .name = "eth_bootdev",
  88. .id = UCLASS_BOOTDEV,
  89. .ops = &eth_bootdev_ops,
  90. .bind = eth_bootdev_bind,
  91. .of_match = eth_bootdev_ids,
  92. };
  93. BOOTDEV_HUNTER(eth_bootdev_hunt) = {
  94. .prio = BOOTDEVP_6_NET_BASE,
  95. .uclass = UCLASS_ETH,
  96. .hunt = eth_bootdev_hunt,
  97. .drv = DM_DRIVER_REF(eth_bootdev),
  98. };