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 int unit; 140 141 unit = md_register((void *)(uintptr_t)pa, len, MD_FLAG_KERNEL); 142 if (unit < 0) { 143 printf("Could not register downloaded image as an md: %s\n", 144 strerror(errno)); 145 return (errno); 146 } 147 setenv("uefi_ignore_boot_mgr", "true", 1); 148 return (0); 149 } 150 151 int 152 download_md_image(const char *url) 153 { 154 struct stat sb; 155 dl_state ctx; 156 uint8_t *buf; 157 size_t offset; 158 ssize_t nread; 159 int error, fd; 160 161 fd = open(url, O_RDONLY); 162 if (fd < 0) 163 return (errno); 164 if (fstat(fd, &sb) != 0) { 165 error = errno; 166 goto out_close; 167 } 168 if (sb.st_size <= 0 || (uintmax_t)sb.st_size > SIZE_MAX) { 169 error = EINVAL; 170 goto out_close; 171 } 172 173 buf = malloc(DOWNLOAD_BUFSIZE); 174 if (buf == NULL) { 175 error = ENOMEM; 176 goto out_close; 177 } 178 memset(&ctx, 0, sizeof(ctx)); 179 ctx.size = sb.st_size; 180 offset = 0; 181 printf("Press Esc to cancel.\n"); 182 while ((nread = read(fd, buf, DOWNLOAD_BUFSIZE)) > 0) { 183 if (download_cancel_requested()) { 184 printf("\nDownload cancelled.\n"); 185 error = ECANCELED; 186 goto out_decomp; 187 } 188 if (EFI_ERROR(download_chunk(&ctx, buf, nread, offset))) { 189 error = EIO; 190 goto out_decomp; 191 } 192 offset += nread; 193 } 194 if (nread < 0) { 195 error = errno; 196 goto out_decomp; 197 } 198 if (offset != ctx.size || !ctx.complete) { 199 error = EIO; 200 goto out_decomp; 201 } 202 203 decomp_fini(ctx.dctx, false); 204 error = fallback_to_md(decomp_buffer(ctx.dctx), 205 decomp_buffer_length(ctx.dctx)); 206 goto out_free; 207 208 out_decomp: 209 if (ctx.dctx != NULL) 210 decomp_fini(ctx.dctx, true); 211 out_free: 212 free(buf); 213 out_close: 214 close(fd); 215 return (error); 216 } 217 218 void 219 maybe_download_initmd(void) 220 { 221 struct devdesc dev; 222 const char *url; 223 int error; 224 225 if (efi_find_handle(&efinet_dev, 0) == NULL) 226 return; 227 228 memset(&dev, 0, sizeof(dev)); 229 dev.d_dev = &efinet_dev; 230 dev.d_unit = 0; 231 error = net_configure(&dev); 232 if (error != 0) { 233 printf("Could not configure net0 for initmd: %s\n", 234 strerror(error)); 235 return; 236 } 237 238 url = getenv("dhcp.initmd"); 239 if (url == NULL || *url == '\0') 240 return; 241 242 printf("Downloading initmd from %s\n", url); 243 error = download_md_image(url); 244 if (error != 0 && error != ECANCELED) 245 printf("Could not download initmd: %s\n", strerror(error)); 246 } 247 248 static void 249 do_download_ramdisk(CHAR8 *url, bool is_disk) 250 { 251 EFI_STATUS Status; 252 EFI_GUID disk_type = is_disk ? virtual_disk_guid : virtual_cd_guid; 253 EFI_DEVICE_PATH_PROTOCOL *ram_disk_path; 254 IPXE_DOWNLOAD_FILE token; 255 dl_state *ctx = &dl; 256 int error; 257 258 printf("Downloading %s as a %s\n", url, is_disk ? "disk" : "cd"); 259 printf("Press Esc to cancel.\n"); 260 memset(ctx, 0, sizeof(*ctx)); 261 ctx->in_progress = true; 262 Status = ipxe_download->Start(ipxe_download, url, download_data, download_finish, 263 &dl, &token); 264 if (EFI_ERROR(Status)) { 265 printf("Couldn't start download %u\n", (unsigned)Status); 266 download_cleanup(ctx); 267 return; 268 } 269 while (ctx->in_progress) { 270 ipxe_download->Poll(ipxe_download); 271 if (!ctx->in_progress) 272 break; 273 if (download_cancel_requested()) { 274 printf("\nCancelling download...\n"); 275 Status = ipxe_download->Abort(ipxe_download, token, 276 EFI_ABORTED); 277 if (EFI_ERROR(Status)) { 278 printf("Could not cancel download %u\n", 279 (unsigned)Status); 280 } 281 } 282 } 283 if (ctx->status == EFI_ABORTED) { 284 printf("Download cancelled.\n"); 285 download_cleanup(ctx); 286 return; 287 } 288 if (EFI_ERROR(ctx->status)) { 289 printf("Download had error %u\n", (unsigned)ctx->status); 290 download_cleanup(ctx); 291 return; 292 } 293 if (ctx->size == 0) { 294 printf("Nothing downloaded\n"); 295 download_cleanup(ctx); 296 return; 297 } 298 299 printf("\nDownloaded %llu bytes, actual size %llu -- registering ramdisk\n", 300 ULL(ctx->size), ULL(decomp_buffer_length(ctx->dctx))); 301 302 /* ram_disk will be NULL if this fails */ 303 BS->LocateProtocol(&ramdiskGuid, NULL, (void**)&ram_disk); 304 305 /* 306 * If there's no ram_disk protocol installed in this firmware, do the 307 * next best thing by saving a pointer and using that later. 308 */ 309 if (ram_disk == NULL) { 310 printf("No RamDisk protocol, falling back to md image\n"); 311 error = fallback_to_md(decomp_buffer(ctx->dctx), decomp_buffer_length(ctx->dctx)); 312 if (error) { 313 printf("Failed to register as an MD device\n"); 314 download_cleanup(ctx); 315 } 316 return; 317 } 318 319 /* 320 * Register the RamDisk with UEFI. This registers it so the rest of the 321 * boot loader can see it as a block device. 322 */ 323 Status = ram_disk->Register(decomp_buffer(ctx->dctx), decomp_buffer_length(ctx->dctx), 324 &disk_type, NULL, &ram_disk_path); 325 if (EFI_ERROR(Status)) { 326 printf("failed to register ram disk %u, falling back to md image\n", (unsigned)Status); 327 error = fallback_to_md(decomp_buffer(ctx->dctx), decomp_buffer_length(ctx->dctx)); 328 if (error) { 329 printf("Failed to register as an MD device\n"); 330 download_cleanup(ctx); 331 } 332 return; 333 } 334 335 CHAR16 *text = efi_devpath_name(ram_disk_path); 336 if (text != NULL) { 337 CHAR8 uefi_path[1024]; 338 printf("Installed RAM disk as %S\n", text); 339 340 cpy16to8(text, uefi_path, sizeof(uefi_path)); 341 setenv("uefi_ignore_boot_mgr", "true", 1); 342 setenv("uefi_rootdev", uefi_path, 1); 343 efi_free_devpath_name(text); 344 } else { 345 printf("Installed RAM disk to unknown device type\n"); 346 } 347 } 348 349 /* 350 * Scan the command line for memdisk=url or memcd=url. Do nothing if that's not 351 * present, otherwise try to download that image. Returns true when we've tried 352 * to download an image, whether successful or not. 353 * 354 * Open Question: Do we want some way to chain boot into the /boot/loader.efi or 355 * \efi\boot\bootXXXXX.efi inside the ram disk we load? If so, how do we keep 356 * from infinite chainbooting? Also, I don't understand the load it but don't save 357 * it option... 358 */ 359 bool 360 maybe_download_ramdisk(int argc, CHAR16 **argv) 361 { 362 char var[256]; 363 EFI_STATUS status; 364 365 status = BS->LocateProtocol(&ipxeGuid, NULL, (void **)&ipxe_download); 366 if (EFI_ERROR(status)) { 367 ipxe_download = NULL; 368 return (false); 369 } 370 371 for (int i = 0; i < argc; i++) { 372 cpy16to8(argv[i], var, sizeof(var)); 373 if (strncmp(var, "memdisk=", 8) == 0) { 374 do_download_ramdisk(var + 8, true); 375 return (true); 376 } 377 if (strncmp(var, "memcd=", 6) == 0) { 378 do_download_ramdisk(var + 6, false); 379 return (true); 380 } 381 } 382 return (false); 383 } 384