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