"Das U-Boot" Source Tree
at master 197 lines 4.7 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Bootmethod for extlinux boot using PXE (network boot) 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 <bootdev.h> 12#include <bootflow.h> 13#include <bootmeth.h> 14#include <command.h> 15#include <dm.h> 16#include <extlinux.h> 17#include <fs.h> 18#include <log.h> 19#include <malloc.h> 20#include <mapmem.h> 21#include <mmc.h> 22#include <net.h> 23#include <pxe_utils.h> 24 25static int extlinux_pxe_getfile(struct pxe_context *ctx, const char *file_path, 26 char *file_addr, enum bootflow_img_t type, 27 ulong *sizep) 28{ 29 struct extlinux_info *info = ctx->userdata; 30 ulong addr; 31 int ret; 32 33 addr = simple_strtoul(file_addr, NULL, 16); 34 35 /* Allow up to 1GB */ 36 *sizep = 1 << 30; 37 ret = bootmeth_read_file(info->dev, info->bflow, file_path, addr, 38 type, sizep); 39 if (ret) 40 return log_msg_ret("read", ret); 41 42 return 0; 43} 44 45static int extlinux_pxe_check(struct udevice *dev, struct bootflow_iter *iter) 46{ 47 int ret; 48 49 /* This only works on network devices */ 50 ret = bootflow_iter_check_net(iter); 51 if (ret) 52 return log_msg_ret("net", ret); 53 54 if (iter->method_flags & BOOTFLOW_METHF_DHCP_ONLY) 55 return log_msg_ret("dhcp", -ENOTSUPP); 56 57 return 0; 58} 59 60static int extlinux_pxe_read_bootflow(struct udevice *dev, 61 struct bootflow *bflow) 62{ 63 const char *addr_str; 64 char fname[200]; 65 char *bootdir; 66 ulong addr; 67 ulong size; 68 char *buf; 69 int ret; 70 71 addr_str = env_get("pxefile_addr_r"); 72 if (!addr_str) 73 return log_msg_ret("pxeb", -EPERM); 74 addr = simple_strtoul(addr_str, NULL, 16); 75 76 log_debug("calling pxe_get()\n"); 77 ret = pxe_get(addr, &bootdir, &size, false); 78 log_debug("pxe_get() returned %d\n", ret); 79 if (ret) 80 return log_msg_ret("pxeb", ret); 81 bflow->size = size; 82 83 /* Use the directory of the dhcp bootdir as our subdir, if provided */ 84 if (bootdir) { 85 const char *last_slash; 86 int path_len; 87 88 last_slash = strrchr(bootdir, '/'); 89 if (last_slash) { 90 path_len = (last_slash - bootdir) + 1; 91 bflow->subdir = malloc(path_len + 1); 92 memcpy(bflow->subdir, bootdir, path_len); 93 bflow->subdir[path_len] = '\0'; 94 } 95 } 96 snprintf(fname, sizeof(fname), "%s%s", 97 bflow->subdir ? bflow->subdir : "", EXTLINUX_FNAME); 98 99 bflow->fname = strdup(fname); 100 if (!bflow->fname) 101 return log_msg_ret("name", -ENOMEM); 102 103 bflow->state = BOOTFLOWST_READY; 104 105 /* Allocate the buffer, including the \0 byte added by get_pxe_file() */ 106 buf = malloc(size + 1); 107 if (!buf) 108 return log_msg_ret("buf", -ENOMEM); 109 memcpy(buf, map_sysmem(addr, 0), size + 1); 110 bflow->buf = buf; 111 112 return 0; 113} 114 115static int extlinux_pxe_read_file(struct udevice *dev, struct bootflow *bflow, 116 const char *file_path, ulong addr, 117 enum bootflow_img_t type, ulong *sizep) 118{ 119 char *tftp_argv[] = {"tftp", NULL, NULL, NULL}; 120 struct pxe_context *ctx = dev_get_priv(dev); 121 char file_addr[17]; 122 ulong size; 123 int ret; 124 125 sprintf(file_addr, "%lx", addr); 126 tftp_argv[1] = file_addr; 127 tftp_argv[2] = (void *)file_path; 128 129 if (do_tftpb(ctx->cmdtp, 0, 3, tftp_argv)) 130 return -ENOENT; 131 ret = pxe_get_file_size(&size); 132 if (ret) 133 return log_msg_ret("tftp", ret); 134 if (size > *sizep) 135 return log_msg_ret("spc", -ENOSPC); 136 *sizep = size; 137 138 if (!bootflow_img_add(bflow, file_path, type, addr, size)) 139 return log_msg_ret("pxi", -ENOMEM); 140 141 return 0; 142} 143 144static int extlinux_pxe_boot(struct udevice *dev, struct bootflow *bflow) 145{ 146 struct pxe_context *ctx = dev_get_priv(dev); 147 struct cmd_tbl cmdtp = {}; /* dummy */ 148 struct extlinux_info info; 149 ulong addr; 150 int ret; 151 152 addr = map_to_sysmem(bflow->buf); 153 info.dev = dev; 154 info.bflow = bflow; 155 info.cmdtp = &cmdtp; 156 ret = pxe_setup_ctx(ctx, &cmdtp, extlinux_pxe_getfile, &info, false, 157 bflow->subdir, false, false); 158 if (ret) 159 return log_msg_ret("ctx", -EINVAL); 160 161 ret = pxe_process(ctx, addr, false); 162 if (ret) 163 return log_msg_ret("bread", -EINVAL); 164 165 return 0; 166} 167 168static int extlinux_bootmeth_pxe_bind(struct udevice *dev) 169{ 170 struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); 171 172 plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ? 173 "PXE boot from a network device" : "PXE"; 174 175 return 0; 176} 177 178static struct bootmeth_ops extlinux_bootmeth_pxe_ops = { 179 .check = extlinux_pxe_check, 180 .read_bootflow = extlinux_pxe_read_bootflow, 181 .read_file = extlinux_pxe_read_file, 182 .boot = extlinux_pxe_boot, 183}; 184 185static const struct udevice_id extlinux_bootmeth_pxe_ids[] = { 186 { .compatible = "u-boot,extlinux-pxe" }, 187 { } 188}; 189 190U_BOOT_DRIVER(bootmeth_zpxe) = { 191 .name = "bootmeth_pxe", 192 .id = UCLASS_BOOTMETH, 193 .of_match = extlinux_bootmeth_pxe_ids, 194 .ops = &extlinux_bootmeth_pxe_ops, 195 .bind = extlinux_bootmeth_pxe_bind, 196 .priv_auto = sizeof(struct pxe_context), 197};