1 /* 2 * Copyright (c) 2026 Netflix, Inc. Written by Warner Losh 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * Derived from memdisk_uefi.c 7 * Copyright 2025 Richard Russo 8 * SPDX-License-Identifier: BSD-2-Clause-Patent 9 */ 10 11 #include "loader_efi.h" 12 #include <bootstrap.h> 13 #include <dev_net.h> 14 #include <efilib.h> 15 #include <Protocol/RamDisk.h> 16 #include "decompress.h" 17 #include <ipxe_download.h> 18 #include <sys/_param.h> 19 20 #define ULL(x) ((unsigned long long)(x)) 21 #define DOWNLOAD_BUFSIZE (64 * 1024) 22 23 static EFI_GUID ipxeGuid = IPXE_DOWNLOAD_PROTOCOL_GUID; 24 static EFI_GUID ramdiskGuid = EFI_RAM_DISK_PROTOCOL_GUID; 25 static EFI_GUID virtual_disk_guid = EFI_VIRTUAL_DISK_GUID; 26 static EFI_GUID virtual_cd_guid = EFI_VIRTUAL_CD_GUID; 27 28 static IPXE_DOWNLOAD_PROTOCOL *ipxe_download; 29 static EFI_RAM_DISK_PROTOCOL *ram_disk; 30 31 static bool 32 download_cancel_requested(void) 33 { 34 int c; 35 36 if (!ischar()) 37 return (false); 38 c = getchar(); 39 return (c == '\033'); 40 } 41 42 struct dl_state; 43 typedef struct dl_state dl_state; 44 45 static struct dl_state 46 { 47 bool in_progress; 48 size_t size; 49 EFI_STATUS status; 50 decomp_state *dctx; 51 bool complete; 52 } dl; 53 54 static void 55 download_cleanup(dl_state *ctx) 56 { 57 if (ctx->dctx) 58 decomp_fini(ctx->dctx, true); 59 ctx->in_progress = false; 60 } 61 62 static EFI_STATUS 63 download_chunk(dl_state *ctx, void *buffer, size_t length, size_t offset) 64 { 65 decomp_state *dctx = ctx->dctx; 66 enum step_return sr; 67 68 if (offset == 0 && length == 0) { 69 printf("Starting the download\n"); 70 return (EFI_SUCCESS); 71 } 72 73 /* 74 * Make a note of the size when we're hinted about it. 75 */ 76 if (length == 0) { 77 printf("We know we will download %llu bytes\n", ULL(offset)); 78 ctx->size = offset; 79 ctx->status = EFI_SUCCESS; 80 return (EFI_SUCCESS); 81 } 82 83 /* 84 * Peek into the first chunk to see the format of the data. 85 */ 86 if (offset == 0) { 87 dctx = decomp_init(buffer, length, ctx->size); 88 if (dctx == NULL) { 89 ctx->in_progress = false; 90 ctx->status = EFI_VOLUME_CORRUPTED; 91 return (ctx->status); 92 } 93 ctx->dctx = dctx; 94 } 95 96 sr = decomp_step(dctx, buffer, length, offset); 97 if (sr == err) { 98 printf("Error on download\n"); 99 return (EFI_VOLUME_CORRUPTED); 100 } 101 ctx->complete = (sr == done); 102 103 unsigned long long sofar = offset + length; 104 #define MB 1000000 105 if (sofar / MB != offset / MB) { 106 if (ctx->size) 107 printf("%dMB / %dMB (%d%%)\r", 108 (int)(sofar / MB), 109 (int)(ctx->size / MB), 110 (int)(100 * sofar / ctx->size)); 111 else 112 printf("%dMB\r", (int)(sofar / MB)); 113 } 114 return (EFI_SUCCESS); 115 } 116 117 static EFI_STATUS EFIAPI 118 download_data(IN VOID *Context, IN VOID *Buffer, IN UINTN BufferLength, 119 IN UINTN FileOffset) 120 { 121 122 return (download_chunk(Context, Buffer, BufferLength, FileOffset)); 123 } 124 125 static void EFIAPI 126 download_finish(IN VOID *Context, IN EFI_STATUS Status) 127 { 128 dl_state *ctx = Context; 129 130 ctx->in_progress = false; 131 ctx->status = Status; 132 if (ctx->dctx != NULL && !EFI_ERROR(Status)) 133 decomp_fini(ctx->dctx, false); 134 } 135 136 static int 137 fallback_to_md(EFI_PHYSICAL_ADDRESS pa, size_t len) 138 { 139 #ifdef LOADER_MD_SUPPORT 140 int unit; 141 142 unit = md_register((void *)(uintptr_t)pa, len, MD_FLAG_KERNEL); 143 if (unit < 0) { 144 printf("Could not register downloaded image as an md: %s\n", 145 strerror(errno)); 146 return (errno); 147 } 148 setenv("uefi_ignore_boot_mgr", "true", 1); 149 return (0); 150 #else 151 printf("No memory disk support in this loader\n"); 152 return (EOPNOTSUPP); 153 #endif 154 } 155 156 int 157 download_md_image(const char *url) 158 { 159 struct stat sb; 160 dl_state ctx; 161 uint8_t *buf; 162 size_t offset; 163 ssize_t nread; 164 int error, fd; 165 166 fd = open(url, O_RDONLY); 167 if (fd < 0) 168 return (errno); 169 if (fstat(fd, &sb) != 0) { 170 error = errno; 171 goto out_close; 172 } 173 if (sb.st_size <= 0 || (uintmax_t)sb.st_size > SIZE_MAX) { 174 error = EINVAL; 175 goto out_close; 176 } 177 178 buf = malloc(DOWNLOAD_BUFSIZE); 179 if (buf == NULL) { 180 error = ENOMEM; 181 goto out_close; 182 } 183 memset(&ctx, 0, sizeof(ctx)); 184 ctx.size = sb.st_size; 185 offset = 0; 186 printf("Press Esc to cancel.\n"); 187 while ((nread = read(fd, buf, DOWNLOAD_BUFSIZE)) > 0) { 188 if (download_cancel_requested()) { 189 printf("\nDownload cancelled.\n"); 190 error = ECANCELED; 191 goto out_decomp; 192 } 193 if (EFI_ERROR(download_chunk(&ctx, buf, nread, offset))) { 194 error = EIO; 195 goto out_decomp; 196 } 197 offset += nread; 198 } 199 if (nread < 0) { 200 error = errno; 201 goto out_decomp; 202 } 203 if (offset != ctx.size || !ctx.complete) { 204 error = EIO; 205 goto out_decomp; 206 } 207 208 decomp_fini(ctx.dctx, false); 209 error = fallback_to_md(decomp_buffer(ctx.dctx), 210 decomp_buffer_length(ctx.dctx)); 211 goto out_free; 212 213 out_decomp: 214 if (ctx.dctx != NULL) 215 decomp_fini(ctx.dctx, true); 216 out_free: 217 free(buf); 218 out_close: 219 close(fd); 220 return (error); 221 } 222 223 void 224 maybe_download_initmd(void) 225 { 226 #ifdef LOADER_NET_SUPPORT 227 struct devdesc dev; 228 const char *url; 229 int error; 230 231 if (efi_find_handle(&efinet_dev, 0) == NULL) 232 return; 233 234 memset(&dev, 0, sizeof(dev)); 235 dev.d_dev = &efinet_dev; 236 dev.d_unit = 0; 237 error = net_configure(&dev); 238 if (error != 0) { 239 printf("Could not configure net0 for initmd: %s\n", 240 strerror(error)); 241 return; 242 } 243 244 url = getenv("dhcp.initmd"); 245 if (url == NULL || *url == '\0') 246 return; 247 248 /* Release SNP so the firmware network stack can fetch the image. */ 249 net_deconfigure(); 250 printf("Downloading initmd from %s\n", url); 251 error = download_md_image(url); 252 if (error != 0 && error != ECANCELED) 253 printf("Could not download initmd: %s\n", strerror(error)); 254 #endif 255 } 256 257 static void 258 do_download_ramdisk(CHAR8 *url, bool is_disk) 259 { 260 EFI_STATUS Status; 261 EFI_GUID disk_type = is_disk ? virtual_disk_guid : virtual_cd_guid; 262 EFI_DEVICE_PATH_PROTOCOL *ram_disk_path; 263 IPXE_DOWNLOAD_FILE token; 264 dl_state *ctx = &dl; 265 int error; 266 267 printf("Downloading %s as a %s\n", url, is_disk ? "disk" : "cd"); 268 printf("Press Esc to cancel.\n"); 269 memset(ctx, 0, sizeof(*ctx)); 270 ctx->in_progress = true; 271 Status = ipxe_download->Start(ipxe_download, url, download_data, download_finish, 272 &dl, &token); 273 if (EFI_ERROR(Status)) { 274 printf("Couldn't start download %u\n", (unsigned)Status); 275 download_cleanup(ctx); 276 return; 277 } 278 while (ctx->in_progress) { 279 ipxe_download->Poll(ipxe_download); 280 if (!ctx->in_progress) 281 break; 282 if (download_cancel_requested()) { 283 printf("\nCancelling download...\n"); 284 Status = ipxe_download->Abort(ipxe_download, token, 285 EFI_ABORTED); 286 if (EFI_ERROR(Status)) { 287 printf("Could not cancel download %u\n", 288 (unsigned)Status); 289 } 290 } 291 } 292 if (ctx->status == EFI_ABORTED) { 293 printf("Download cancelled.\n"); 294 download_cleanup(ctx); 295 return; 296 } 297 if (EFI_ERROR(ctx->status)) { 298 printf("Download had error %u\n", (unsigned)ctx->status); 299 download_cleanup(ctx); 300 return; 301 } 302 if (ctx->size == 0) { 303 printf("Nothing downloaded\n"); 304 download_cleanup(ctx); 305 return; 306 } 307 308 printf("\nDownloaded %llu bytes, actual size %llu -- registering ramdisk\n", 309 ULL(ctx->size), ULL(decomp_buffer_length(ctx->dctx))); 310 311 /* ram_disk will be NULL if this fails */ 312 BS->LocateProtocol(&ramdiskGuid, NULL, (void**)&ram_disk); 313 314 /* 315 * If there's no ram_disk protocol installed in this firmware, do the 316 * next best thing by saving a pointer and using that later. 317 */ 318 if (ram_disk == NULL) { 319 printf("No RamDisk protocol, falling back to md image\n"); 320 error = fallback_to_md(decomp_buffer(ctx->dctx), decomp_buffer_length(ctx->dctx)); 321 if (error) { 322 printf("Failed to register as an MD device\n"); 323 download_cleanup(ctx); 324 } 325 return; 326 } 327 328 /* 329 * Register the RamDisk with UEFI. This registers it so the rest of the 330 * boot loader can see it as a block device. 331 */ 332 Status = ram_disk->Register(decomp_buffer(ctx->dctx), decomp_buffer_length(ctx->dctx), 333 &disk_type, NULL, &ram_disk_path); 334 if (EFI_ERROR(Status)) { 335 printf("failed to register ram disk %u, falling back to md image\n", (unsigned)Status); 336 error = fallback_to_md(decomp_buffer(ctx->dctx), decomp_buffer_length(ctx->dctx)); 337 if (error) { 338 printf("Failed to register as an MD device\n"); 339 download_cleanup(ctx); 340 } 341 return; 342 } 343 344 CHAR16 *text = efi_devpath_name(ram_disk_path); 345 if (text != NULL) { 346 CHAR8 uefi_path[1024]; 347 printf("Installed RAM disk as %S\n", text); 348 349 cpy16to8(text, uefi_path, sizeof(uefi_path)); 350 setenv("uefi_ignore_boot_mgr", "true", 1); 351 setenv("uefi_rootdev", uefi_path, 1); 352 efi_free_devpath_name(text); 353 } else { 354 printf("Installed RAM disk to unknown device type\n"); 355 } 356 } 357 358 /* 359 * Scan the command line for memdisk=url or memcd=url. Do nothing if that's not 360 * present, otherwise try to download that image. Returns true when we've tried 361 * to download an image, whether successful or not. 362 * 363 * Open Question: Do we want some way to chain boot into the /boot/loader.efi or 364 * \efi\boot\bootXXXXX.efi inside the ram disk we load? If so, how do we keep 365 * from infinite chainbooting? Also, I don't understand the load it but don't save 366 * it option... 367 */ 368 bool 369 maybe_download_ramdisk(int argc, CHAR16 **argv) 370 { 371 char var[256]; 372 EFI_STATUS status; 373 374 status = BS->LocateProtocol(&ipxeGuid, NULL, (void **)&ipxe_download); 375 if (EFI_ERROR(status)) { 376 ipxe_download = NULL; 377 return (false); 378 } 379 380 for (int i = 0; i < argc; i++) { 381 cpy16to8(argv[i], var, sizeof(var)); 382 if (strncmp(var, "memdisk=", 8) == 0) { 383 do_download_ramdisk(var + 8, true); 384 return (true); 385 } 386 if (strncmp(var, "memcd=", 6) == 0) { 387 do_download_ramdisk(var + 6, false); 388 return (true); 389 } 390 } 391 return (false); 392 } 393