"Das U-Boot" Source Tree
at master 333 lines 8.2 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Bootmethod for distro boot via EFI 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 <efi.h> 17#include <efi_loader.h> 18#include <fs.h> 19#include <malloc.h> 20#include <mapmem.h> 21#include <mmc.h> 22#include <net.h> 23#include <pxe_utils.h> 24#include <linux/sizes.h> 25 26#define EFI_DIRNAME "/EFI/BOOT/" 27 28static int get_efi_pxe_vci(char *str, int max_len) 29{ 30 int ret; 31 32 ret = efi_get_pxe_arch(); 33 if (ret < 0) 34 return ret; 35 36 snprintf(str, max_len, "PXEClient:Arch:%05x:UNDI:003000", ret); 37 38 return 0; 39} 40 41/** 42 * bootmeth_uses_network() - check if the media device is Ethernet 43 * 44 * @bflow: Bootflow to check 45 * Returns: true if the media device is Ethernet, else false 46 */ 47static bool bootmeth_uses_network(struct bootflow *bflow) 48{ 49 const struct udevice *media = dev_get_parent(bflow->dev); 50 51 return IS_ENABLED(CONFIG_CMD_DHCP) && 52 device_get_uclass_id(media) == UCLASS_ETH; 53} 54 55static int efiload_read_file(struct bootflow *bflow, ulong addr) 56{ 57 struct blk_desc *desc = NULL; 58 ulong size; 59 int ret; 60 61 if (bflow->blk) 62 desc = dev_get_uclass_plat(bflow->blk); 63 64 size = SZ_1G; 65 ret = bootmeth_common_read_file(bflow->method, bflow, bflow->fname, 66 addr, BFI_EFI, &size); 67 if (ret) 68 return log_msg_ret("rdf", ret); 69 bflow->buf = map_sysmem(addr, bflow->size); 70 71 return 0; 72} 73 74static int distro_efi_check(struct udevice *dev, struct bootflow_iter *iter) 75{ 76 /* This only works on block and network devices */ 77 if (bootflow_iter_check_blk(iter) && bootflow_iter_check_net(iter)) 78 return log_msg_ret("blk", -ENOTSUPP); 79 80 /* This works on block devices and network devices */ 81 if (iter->method_flags & BOOTFLOW_METHF_PXE_ONLY) 82 return log_msg_ret("pxe", -ENOTSUPP); 83 84 return 0; 85} 86 87/* 88 * distro_efi_try_bootflow_files() - Check that files are present 89 * 90 * This reads any FDT file and checks whether the bootflow file is present, for 91 * later reading. We avoid reading the bootflow now, since it is likely large, 92 * it may take a long time and we want to avoid needing to allocate memory for 93 * it 94 * 95 * @dev: bootmeth device to use 96 * @bflow: bootflow to update 97 */ 98static int distro_efi_try_bootflow_files(struct udevice *dev, 99 struct bootflow *bflow) 100{ 101 struct blk_desc *desc = NULL; 102 ulong fdt_addr, size; 103 char fname[256]; 104 int ret, seq; 105 106 /* We require a partition table */ 107 if (!bflow->part) { 108 log_debug("no partitions\n"); 109 return -ENOENT; 110 } 111 112 strcpy(fname, EFI_DIRNAME); 113 strcat(fname, efi_get_basename()); 114 115 if (bflow->blk) 116 desc = dev_get_uclass_plat(bflow->blk); 117 ret = bootmeth_try_file(bflow, desc, NULL, fname); 118 if (ret) { 119 log_debug("File '%s' not found\n", fname); 120 return log_msg_ret("try", ret); 121 } 122 123 /* Since we can access the file, let's call it ready */ 124 bflow->state = BOOTFLOWST_READY; 125 126 fdt_addr = env_get_hex("fdt_addr_r", 0); 127 128 /* try the various available names */ 129 ret = -ENOENT; 130 *fname = '\0'; 131 for (seq = 0; ret == -ENOENT; seq++) { 132 ret = efi_get_distro_fdt_name(fname, sizeof(fname), seq); 133 if (ret == -EALREADY) 134 bflow->flags = BOOTFLOWF_USE_PRIOR_FDT; 135 if (!ret) { 136 /* Limit FDT files to 4MB */ 137 size = SZ_4M; 138 ret = bootmeth_common_read_file(dev, bflow, fname, 139 fdt_addr, (enum bootflow_img_t)IH_TYPE_FLATDT, 140 &size); 141 } 142 } 143 144 if (*fname) { 145 bflow->fdt_fname = strdup(fname); 146 if (!bflow->fdt_fname) 147 return log_msg_ret("fil", -ENOMEM); 148 } 149 150 if (!ret) { 151 bflow->fdt_size = size; 152 bflow->fdt_addr = fdt_addr; 153 154 /* 155 * TODO: Apply extension overlay 156 * 157 * Here we need to load and apply the extension overlay. This is 158 * not implemented. See do_extension_apply(). The extension 159 * stuff needs an implementation in boot/extension.c so it is 160 * separate from the command code. Really the extension stuff 161 * should use the device tree and a uclass / driver interface 162 * rather than implementing its own list 163 */ 164 } else { 165 log_debug("No device tree available\n"); 166 bflow->flags |= BOOTFLOWF_USE_BUILTIN_FDT; 167 } 168 169 return 0; 170} 171 172static int distro_efi_read_bootflow_net(struct bootflow *bflow) 173{ 174 char file_addr[17], fname[256]; 175 char *tftp_argv[] = {"tftp", file_addr, fname, NULL}; 176 struct cmd_tbl cmdtp = {}; /* dummy */ 177 const char *addr_str, *fdt_addr_str, *bootfile_name; 178 int ret, arch, size; 179 ulong addr, fdt_addr; 180 char str[36]; 181 182 ret = get_efi_pxe_vci(str, sizeof(str)); 183 if (ret) 184 return log_msg_ret("vci", ret); 185 ret = efi_get_pxe_arch(); 186 if (ret < 0) 187 return log_msg_ret("arc", ret); 188 arch = ret; 189 190 ret = env_set("bootp_vci", str); 191 if (ret) 192 return log_msg_ret("vcs", ret); 193 ret = env_set_hex("bootp_arch", arch); 194 if (ret) 195 return log_msg_ret("ars", ret); 196 197 /* figure out the load address */ 198 addr_str = env_get("kernel_addr_r"); 199 addr = addr_str ? hextoul(addr_str, NULL) : image_load_addr; 200 201 /* clear any previous bootfile */ 202 env_set("bootfile", NULL); 203 204 /* read the kernel */ 205 ret = dhcp_run(addr, NULL, true); 206 if (ret) 207 return log_msg_ret("dhc", ret); 208 209 size = env_get_hex("filesize", -1); 210 if (size <= 0) 211 return log_msg_ret("sz", -EINVAL); 212 bflow->size = size; 213 bflow->buf = map_sysmem(addr, size); 214 215 /* bootfile should be setup by dhcp */ 216 bootfile_name = env_get("bootfile"); 217 if (!bootfile_name) 218 return log_msg_ret("bootfile_name", ret); 219 bflow->fname = strdup(bootfile_name); 220 if (!bflow->fname) 221 return log_msg_ret("fi0", -ENOMEM); 222 223 /* read the DT file also */ 224 fdt_addr_str = env_get("fdt_addr_r"); 225 if (!fdt_addr_str) 226 return log_msg_ret("fdt", -EINVAL); 227 fdt_addr = hextoul(fdt_addr_str, NULL); 228 sprintf(file_addr, "%lx", fdt_addr); 229 230 /* We only allow the first prefix with PXE */ 231 ret = efi_get_distro_fdt_name(fname, sizeof(fname), 0); 232 if (ret) 233 return log_msg_ret("nam", ret); 234 235 bflow->fdt_fname = strdup(fname); 236 if (!bflow->fdt_fname) 237 return log_msg_ret("fil", -ENOMEM); 238 239 if (!do_tftpb(&cmdtp, 0, 3, tftp_argv)) { 240 bflow->fdt_size = env_get_hex("filesize", 0); 241 bflow->fdt_addr = fdt_addr; 242 } else { 243 log_debug("No device tree available\n"); 244 bflow->flags |= BOOTFLOWF_USE_BUILTIN_FDT; 245 } 246 247 bflow->state = BOOTFLOWST_READY; 248 249 return 0; 250} 251 252static int distro_efi_read_bootflow(struct udevice *dev, struct bootflow *bflow) 253{ 254 int ret; 255 256 log_debug("dev='%s', part=%d\n", bflow->dev->name, bflow->part); 257 258 /* 259 * bootmeth_efi doesn't allocate any buffer neither for blk nor net device 260 * set flag to avoid freeing static buffer. 261 */ 262 bflow->flags |= BOOTFLOWF_STATIC_BUF; 263 264 if (bootmeth_uses_network(bflow)) { 265 /* we only support reading from one device, so ignore 'dev' */ 266 ret = distro_efi_read_bootflow_net(bflow); 267 if (ret) 268 return log_msg_ret("net", ret); 269 } else { 270 ret = distro_efi_try_bootflow_files(dev, bflow); 271 if (ret) 272 return log_msg_ret("blk", ret); 273 } 274 275 return 0; 276} 277 278static int distro_efi_boot(struct udevice *dev, struct bootflow *bflow) 279{ 280 ulong kernel, fdt; 281 int ret; 282 283 log_debug("distro EFI boot\n"); 284 kernel = env_get_hex("kernel_addr_r", 0); 285 if (!bootmeth_uses_network(bflow)) { 286 ret = efiload_read_file(bflow, kernel); 287 if (ret) 288 return log_msg_ret("read", ret); 289 290 /* 291 * use the provided device tree if not using the built-in fdt 292 */ 293 if (bflow->flags & ~BOOTFLOWF_USE_BUILTIN_FDT) 294 fdt = bflow->fdt_addr; 295 296 } 297 298 if (efi_bootflow_run(bflow)) 299 return log_msg_ret("run", -EINVAL); 300 301 return 0; 302} 303 304static int distro_bootmeth_efi_bind(struct udevice *dev) 305{ 306 struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); 307 308 plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ? 309 "EFI boot from an .efi file" : "EFI"; 310 311 return 0; 312} 313 314static struct bootmeth_ops distro_efi_bootmeth_ops = { 315 .check = distro_efi_check, 316 .read_bootflow = distro_efi_read_bootflow, 317 .read_file = bootmeth_common_read_file, 318 .boot = distro_efi_boot, 319}; 320 321static const struct udevice_id distro_efi_bootmeth_ids[] = { 322 { .compatible = "u-boot,distro-efi" }, 323 { } 324}; 325 326/* Put a number before 'efi' to provide a default ordering */ 327U_BOOT_DRIVER(bootmeth_4efi) = { 328 .name = "bootmeth_efi", 329 .id = UCLASS_BOOTMETH, 330 .of_match = distro_efi_bootmeth_ids, 331 .ops = &distro_efi_bootmeth_ops, 332 .bind = distro_bootmeth_efi_bind, 333};