xref: /freebsd/stand/efi/loader/memdisk.c (revision 6413a870fa0721ac509e38f24d5dd5204c603288)
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
download_cancel_requested(void)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
download_cleanup(dl_state * ctx)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
download_chunk(dl_state * ctx,void * buffer,size_t length,size_t offset)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
download_data(IN VOID * Context,IN VOID * Buffer,IN UINTN BufferLength,IN UINTN FileOffset)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
download_finish(IN VOID * Context,IN EFI_STATUS Status)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
fallback_to_md(EFI_PHYSICAL_ADDRESS pa,size_t len)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
download_md_image(const char * url)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
maybe_download_initmd(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 	printf("Downloading initmd from %s\n", url);
249 	error = download_md_image(url);
250 	if (error != 0 && error != ECANCELED)
251 		printf("Could not download initmd: %s\n", strerror(error));
252 #endif
253 }
254 
255 static void
do_download_ramdisk(CHAR8 * url,bool is_disk)256 do_download_ramdisk(CHAR8 *url, bool is_disk)
257 {
258 	EFI_STATUS Status;
259 	EFI_GUID disk_type = is_disk ? virtual_disk_guid : virtual_cd_guid;
260 	EFI_DEVICE_PATH_PROTOCOL *ram_disk_path;
261 	IPXE_DOWNLOAD_FILE token;
262 	dl_state *ctx = &dl;
263 	int error;
264 
265 	printf("Downloading %s as a %s\n", url, is_disk ? "disk" : "cd");
266 	printf("Press Esc to cancel.\n");
267 	memset(ctx, 0, sizeof(*ctx));
268 	ctx->in_progress = true;
269 	Status = ipxe_download->Start(ipxe_download, url, download_data, download_finish,
270 	    &dl, &token);
271 	if (EFI_ERROR(Status)) {
272 		printf("Couldn't start download %u\n", (unsigned)Status);
273 		download_cleanup(ctx);
274 		return;
275 	}
276 	while (ctx->in_progress) {
277 		ipxe_download->Poll(ipxe_download);
278 		if (!ctx->in_progress)
279 			break;
280 		if (download_cancel_requested()) {
281 			printf("\nCancelling download...\n");
282 			Status = ipxe_download->Abort(ipxe_download, token,
283 			    EFI_ABORTED);
284 			if (EFI_ERROR(Status)) {
285 				printf("Could not cancel download %u\n",
286 				    (unsigned)Status);
287 			}
288 		}
289 	}
290 	if (ctx->status == EFI_ABORTED) {
291 		printf("Download cancelled.\n");
292 		download_cleanup(ctx);
293 		return;
294 	}
295 	if (EFI_ERROR(ctx->status)) {
296 		printf("Download had error %u\n", (unsigned)ctx->status);
297 		download_cleanup(ctx);
298 		return;
299 	}
300 	if (ctx->size == 0) {
301 		printf("Nothing downloaded\n");
302 		download_cleanup(ctx);
303 		return;
304 	}
305 
306 	printf("\nDownloaded %llu bytes, actual size %llu -- registering ramdisk\n",
307 	    ULL(ctx->size), ULL(decomp_buffer_length(ctx->dctx)));
308 
309 	/* ram_disk will be NULL if this fails */
310 	BS->LocateProtocol(&ramdiskGuid, NULL, (void**)&ram_disk);
311 
312 	/*
313 	 * If there's no ram_disk protocol installed in this firmware, do the
314 	 * next best thing by saving a pointer and using that later.
315 	 */
316 	if (ram_disk == NULL) {
317 		printf("No RamDisk protocol, falling back to md image\n");
318 		error = fallback_to_md(decomp_buffer(ctx->dctx), decomp_buffer_length(ctx->dctx));
319 		if (error) {
320 			printf("Failed to register as an MD device\n");
321 			download_cleanup(ctx);
322 		}
323 		return;
324 	}
325 
326 	/*
327 	 * Register the RamDisk with UEFI. This registers it so the rest of the
328 	 * boot loader can see it as a block device.
329 	 */
330 	Status = ram_disk->Register(decomp_buffer(ctx->dctx), decomp_buffer_length(ctx->dctx),
331 	    &disk_type, NULL, &ram_disk_path);
332 	if (EFI_ERROR(Status)) {
333 		printf("failed to register ram disk %u, falling back to md image\n", (unsigned)Status);
334 		error = fallback_to_md(decomp_buffer(ctx->dctx), decomp_buffer_length(ctx->dctx));
335 		if (error) {
336 			printf("Failed to register as an MD device\n");
337 			download_cleanup(ctx);
338 		}
339 		return;
340 	}
341 
342 	CHAR16 *text = efi_devpath_name(ram_disk_path);
343 	if (text != NULL) {
344 		CHAR8 uefi_path[1024];
345 		printf("Installed RAM disk as %S\n", text);
346 
347 		cpy16to8(text, uefi_path, sizeof(uefi_path));
348 		setenv("uefi_ignore_boot_mgr", "true", 1);
349 		setenv("uefi_rootdev", uefi_path, 1);
350 		efi_free_devpath_name(text);
351 	} else {
352 		printf("Installed RAM disk to unknown device type\n");
353 	}
354 }
355 
356 /*
357  * Scan the command line for memdisk=url or memcd=url. Do nothing if that's not
358  * present, otherwise try to download that image. Returns true when we've tried
359  * to download an image, whether successful or not.
360  *
361  * Open Question: Do we want some way to chain boot into the /boot/loader.efi or
362  * \efi\boot\bootXXXXX.efi inside the ram disk we load? If so, how do we keep
363  * from infinite chainbooting? Also, I don't understand the load it but don't save
364  * it option...
365  */
366 bool
maybe_download_ramdisk(int argc,CHAR16 ** argv)367 maybe_download_ramdisk(int argc, CHAR16 **argv)
368 {
369 	char var[256];
370 	EFI_STATUS status;
371 
372 	status = BS->LocateProtocol(&ipxeGuid, NULL, (void **)&ipxe_download);
373 	if (EFI_ERROR(status)) {
374 		ipxe_download = NULL;
375 		return (false);
376 	}
377 
378 	for (int i = 0; i < argc; i++) {
379 		cpy16to8(argv[i], var, sizeof(var));
380 		if (strncmp(var, "memdisk=", 8) == 0) {
381 			do_download_ramdisk(var + 8, true);
382 			return (true);
383 		}
384 		if (strncmp(var, "memcd=", 6) == 0) {
385 			do_download_ramdisk(var + 6, false);
386 			return (true);
387 		}
388 	}
389 	return (false);
390 }
391