xref: /freebsd/stand/efi/libefi/efihttp.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2019 Intel Corporation
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 #include <sys/types.h>
30 
31 #include <netinet/in.h>
32 #include <netinet/in_systm.h>
33 
34 #include <stand.h>
35 #include <bootstrap.h>
36 #include <net.h>
37 
38 #include <efi.h>
39 #include <efilib.h>
40 #include <efiprot.h>
41 #include <Protocol/Http.h>
42 #include <Protocol/Ip4Config2.h>
43 #include <Protocol/ServiceBinding.h>
44 
45 /* Poll timeout in milliseconds */
46 static const int EFIHTTP_POLL_TIMEOUT = 300000;
47 
48 static EFI_GUID http_guid = EFI_HTTP_PROTOCOL_GUID;
49 static EFI_GUID httpsb_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
50 static EFI_GUID ip4config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID;
51 
52 static bool efihttp_init_done = false;
53 
54 static int efihttp_dev_init(void);
55 static int efihttp_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size,
56     char *buf, size_t *rsize);
57 static int efihttp_dev_open(struct open_file *f, ...);
58 static int efihttp_dev_close(struct open_file *f);
59 
60 static int efihttp_fs_open(const char *path, struct open_file *f);
61 static int efihttp_fs_close(struct open_file *f);
62 static int efihttp_fs_read(struct open_file *f, void *buf, size_t size,
63     size_t *resid);
64 static int efihttp_fs_write(struct open_file *f, const void *buf, size_t size,
65     size_t *resid);
66 static off_t efihttp_fs_seek(struct open_file *f, off_t offset, int where);
67 static int efihttp_fs_stat(struct open_file *f, struct stat *sb);
68 static int efihttp_fs_readdir(struct open_file *f, struct dirent *d);
69 
70 struct open_efihttp {
71 	EFI_HTTP_PROTOCOL *http;
72 	EFI_HANDLE	http_handle;
73 	EFI_HANDLE	dev_handle;
74 	char		*uri_base;
75 };
76 
77 struct file_efihttp {
78 	ssize_t		size;
79 	off_t		offset;
80 	char		*path;
81 	bool		is_dir;
82 };
83 
84 struct devsw efihttp_dev = {
85 	.dv_name =	"http",
86 	.dv_type =	DEVT_NET,
87 	.dv_init =	efihttp_dev_init,
88 	.dv_strategy =	efihttp_dev_strategy,
89 	.dv_open =	efihttp_dev_open,
90 	.dv_close =	efihttp_dev_close,
91 	.dv_ioctl =	noioctl,
92 	.dv_print =	NULL,
93 	.dv_cleanup =	nullsys,
94 };
95 
96 struct fs_ops efihttp_fsops = {
97 	.fs_name =	"efihttp",
98 	.fo_open =	efihttp_fs_open,
99 	.fo_close =	efihttp_fs_close,
100 	.fo_read =	efihttp_fs_read,
101 	.fo_write =	efihttp_fs_write,
102 	.fo_seek =	efihttp_fs_seek,
103 	.fo_stat =	efihttp_fs_stat,
104 	.fo_readdir =	efihttp_fs_readdir,
105 };
106 
107 static void EFIAPI
108 notify(EFI_EVENT event __unused, void *context)
109 {
110 	bool *b;
111 
112 	b = (bool *)context;
113 	*b = true;
114 }
115 
116 static int
117 setup_ipv4_config2(EFI_HANDLE handle, MAC_ADDR_DEVICE_PATH *mac,
118     IPv4_DEVICE_PATH *ipv4, DNS_DEVICE_PATH *dns)
119 {
120 	EFI_IP4_CONFIG2_PROTOCOL *ip4config2;
121 	EFI_STATUS status;
122 
123 	status = BS->OpenProtocol(handle, &ip4config2_guid,
124 	    (void **)&ip4config2, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
125 	if (EFI_ERROR(status))
126 		return (efi_status_to_errno(status));
127 	if (ipv4 != NULL) {
128 		if (mac != NULL) {
129 			setenv("boot.netif.hwaddr",
130 			    ether_sprintf((u_char *)mac->MacAddress.Addr), 1);
131 		}
132 		setenv("boot.netif.ip",
133 		    inet_ntoa(*(struct in_addr *)ipv4->LocalIpAddress.Addr), 1);
134 		setenv("boot.netif.netmask",
135 		    intoa(*(n_long *)ipv4->SubnetMask.Addr), 1);
136 		setenv("boot.netif.gateway",
137 		    inet_ntoa(*(struct in_addr *)ipv4->GatewayIpAddress.Addr),
138 		    1);
139 		status = ip4config2->SetData(ip4config2,
140 		    Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY),
141 		    &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyStatic });
142 		if (EFI_ERROR(status))
143 			return (efi_status_to_errno(status));
144 
145 		status = ip4config2->SetData(ip4config2,
146 		    Ip4Config2DataTypeManualAddress,
147 		    sizeof(EFI_IP4_CONFIG2_MANUAL_ADDRESS),
148 		    &(EFI_IP4_CONFIG2_MANUAL_ADDRESS) {
149 			.Address = ipv4->LocalIpAddress,
150 			.SubnetMask = ipv4->SubnetMask });
151 		if (EFI_ERROR(status))
152 			return (efi_status_to_errno(status));
153 
154 		if (ipv4->GatewayIpAddress.Addr[0] != 0) {
155 			status = ip4config2->SetData(ip4config2,
156 			    Ip4Config2DataTypeGateway, sizeof(EFI_IPv4_ADDRESS),
157 			    &ipv4->GatewayIpAddress);
158 			if (EFI_ERROR(status))
159 				return (efi_status_to_errno(status));
160 		}
161 
162 		if (dns != NULL) {
163 			status = ip4config2->SetData(ip4config2,
164 			    Ip4Config2DataTypeDnsServer,
165 			    sizeof(EFI_IPv4_ADDRESS), &dns->DnsServerIp);
166 			if (EFI_ERROR(status))
167 				return (efi_status_to_errno(status));
168 		}
169 	} else {
170 		status = ip4config2->SetData(ip4config2,
171 		    Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY),
172 		    &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyDhcp });
173 		if (EFI_ERROR(status))
174 			return (efi_status_to_errno(status));
175 	}
176 
177 	return (0);
178 }
179 
180 static int
181 efihttp_dev_init(void)
182 {
183 	EFI_DEVICE_PATH *imgpath, *devpath;
184 	URI_DEVICE_PATH *uri;
185 	EFI_HANDLE handle;
186 	EFI_STATUS status;
187 	int err;
188 	bool found_http;
189 
190 	imgpath = efi_lookup_image_devpath(IH);
191 	if (imgpath == NULL)
192 		return (ENXIO);
193 	devpath = imgpath;
194 	found_http = false;
195 	for (; !IsDevicePathEnd(devpath);
196 	    devpath = NextDevicePathNode(devpath)) {
197 		if (DevicePathType(devpath) != MESSAGING_DEVICE_PATH ||
198 		    DevicePathSubType(devpath) != MSG_URI_DP)
199 			continue;
200 		uri = (URI_DEVICE_PATH *)devpath;
201 		if (strncmp("http", (const char *)uri->Uri, 4) == 0)
202 			found_http = true;
203 	}
204 	if (!found_http)
205 		return (ENXIO);
206 
207 	status = BS->LocateDevicePath(&httpsb_guid, &imgpath, &handle);
208 	if (EFI_ERROR(status))
209 		return (efi_status_to_errno(status));
210 
211 	err = efi_register_handles(&efihttp_dev, &handle, NULL, 1);
212 	if (!err)
213 		efihttp_init_done = true;
214 
215 	return (err);
216 }
217 
218 static int
219 efihttp_dev_strategy(void *devdata __unused, int rw __unused,
220     daddr_t blk __unused, size_t size __unused, char *buf __unused,
221     size_t *rsize __unused)
222 {
223 	return (EIO);
224 }
225 
226 static int
227 efihttp_dev_open(struct open_file *f, ...)
228 {
229 	EFI_HTTP_CONFIG_DATA config;
230 	EFI_HTTPv4_ACCESS_POINT config_access;
231 	DNS_DEVICE_PATH *dns;
232 	EFI_DEVICE_PATH *devpath, *imgpath;
233 	EFI_SERVICE_BINDING_PROTOCOL *sb;
234 	IPv4_DEVICE_PATH *ipv4;
235 	MAC_ADDR_DEVICE_PATH *mac;
236 	URI_DEVICE_PATH *uri;
237 	struct devdesc *dev;
238 	struct open_efihttp *oh;
239 	char *c;
240 	EFI_HANDLE handle;
241 	EFI_STATUS status;
242 	int err, len;
243 
244 	if (!efihttp_init_done)
245 		return (ENXIO);
246 
247 	imgpath = efi_lookup_image_devpath(IH);
248 	if (imgpath == NULL)
249 		return (ENXIO);
250 	devpath = imgpath;
251 	status = BS->LocateDevicePath(&httpsb_guid, &devpath, &handle);
252 	if (EFI_ERROR(status))
253 		return (efi_status_to_errno(status));
254 	mac = NULL;
255 	ipv4 = NULL;
256 	dns = NULL;
257 	uri = NULL;
258 	for (; !IsDevicePathEnd(imgpath);
259 	    imgpath = NextDevicePathNode(imgpath)) {
260 		if (DevicePathType(imgpath) != MESSAGING_DEVICE_PATH)
261 			continue;
262 		switch (DevicePathSubType(imgpath)) {
263 		case MSG_MAC_ADDR_DP:
264 			mac = (MAC_ADDR_DEVICE_PATH *)imgpath;
265 			break;
266 		case MSG_IPv4_DP:
267 			ipv4 = (IPv4_DEVICE_PATH *)imgpath;
268 			break;
269 		case MSG_DNS_DP:
270 			dns = (DNS_DEVICE_PATH *)imgpath;
271 			break;
272 		case MSG_URI_DP:
273 			uri = (URI_DEVICE_PATH *)imgpath;
274 			break;
275 		default:
276 			break;
277 		}
278 	}
279 
280 	if (uri == NULL)
281 		return (ENXIO);
282 
283 	err = setup_ipv4_config2(handle, mac, ipv4, dns);
284 	if (err)
285 		return (err);
286 
287 	oh = calloc(1, sizeof(struct open_efihttp));
288 	if (!oh)
289 		return (ENOMEM);
290 	oh->dev_handle = handle;
291 	dev = (struct devdesc *)f->f_devdata;
292 	dev->d_opendata = oh;
293 
294 	status = BS->OpenProtocol(handle, &httpsb_guid, (void **)&sb, IH, NULL,
295 	    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
296 	if (EFI_ERROR(status)) {
297 		err = efi_status_to_errno(status);
298 		goto end;
299 	}
300 
301 	status = sb->CreateChild(sb, &oh->http_handle);
302 	if (EFI_ERROR(status)) {
303 		err = efi_status_to_errno(status);
304 		goto end;
305 	}
306 
307 	status = BS->OpenProtocol(oh->http_handle, &http_guid,
308 	    (void **)&oh->http, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
309 	if (EFI_ERROR(status)) {
310 		sb->DestroyChild(sb, oh->http_handle);
311 		err = efi_status_to_errno(status);
312 		goto end;
313 	}
314 
315 	config.HttpVersion = HttpVersion11;
316 	config.TimeOutMillisec = 0;
317 	config.LocalAddressIsIPv6 = FALSE;
318 	config.AccessPoint.IPv4Node = &config_access;
319 	config_access.UseDefaultAddress = TRUE;
320 	config_access.LocalPort = 0;
321 	status = oh->http->Configure(oh->http, &config);
322 	if (EFI_ERROR(status)) {
323 		sb->DestroyChild(sb, oh->http_handle);
324 		err = efi_status_to_errno(status);
325 		goto end;
326 	}
327 
328 	/*
329 	 * Here we make attempt to construct a "base" URI by stripping
330 	 * the last two path components from the loaded URI under the
331 	 * assumption that it is something like:
332 	 *
333 	 * http://127.0.0.1/foo/boot/loader.efi
334 	 *
335 	 * hoping to arriving at:
336 	 *
337 	 * http://127.0.0.1/foo/
338 	 */
339 	len = DevicePathNodeLength(&uri->Header) - sizeof(URI_DEVICE_PATH);
340 	oh->uri_base = malloc(len + 1);
341 	if (oh->uri_base == NULL) {
342 		err = ENOMEM;
343 		goto end;
344 	}
345 	strncpy(oh->uri_base, (const char *)uri->Uri, len);
346 	oh->uri_base[len] = '\0';
347 	c = strrchr(oh->uri_base, '/');
348 	if (c != NULL)
349 		*c = '\0';
350 	c = strrchr(oh->uri_base, '/');
351 	if (c != NULL && *(c + 1) != '\0')
352 		*(c + 1) = '\0';
353 
354 	err = 0;
355 end:
356 	if (err != 0) {
357 		free(dev->d_opendata);
358 		dev->d_opendata = NULL;
359 	}
360 	return (err);
361 }
362 
363 static int
364 efihttp_dev_close(struct open_file *f)
365 {
366 	EFI_SERVICE_BINDING_PROTOCOL *sb;
367 	struct devdesc *dev;
368 	struct open_efihttp *oh;
369 	EFI_STATUS status;
370 
371 	dev = (struct devdesc *)f->f_devdata;
372 	oh = (struct open_efihttp *)dev->d_opendata;
373 	status = BS->OpenProtocol(oh->dev_handle, &httpsb_guid, (void **)&sb,
374 	    IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
375 	if (EFI_ERROR(status))
376 		return (efi_status_to_errno(status));
377 	sb->DestroyChild(sb, oh->http_handle);
378 	free(oh->uri_base);
379 	free(oh);
380 	dev->d_opendata = NULL;
381 	return (0);
382 }
383 
384 static int
385 _efihttp_fs_open(const char *path, struct open_file *f)
386 {
387 	EFI_HTTP_CONFIG_DATA config;
388 	EFI_HTTPv4_ACCESS_POINT config_access;
389 	EFI_HTTP_TOKEN token;
390 	EFI_HTTP_MESSAGE message;
391 	EFI_HTTP_REQUEST_DATA request;
392 	EFI_HTTP_RESPONSE_DATA response;
393 	EFI_HTTP_HEADER headers[3];
394 	char *host, *hostp;
395 	char *c;
396 	struct devdesc *dev;
397 	struct open_efihttp *oh;
398 	struct file_efihttp *fh;
399 	EFI_STATUS status;
400 	UINTN i;
401 	int polltime;
402 	bool done;
403 
404 	dev = (struct devdesc *)f->f_devdata;
405 	oh = (struct open_efihttp *)dev->d_opendata;
406 	fh = calloc(1, sizeof(struct file_efihttp));
407 	if (fh == NULL)
408 		return (ENOMEM);
409 	f->f_fsdata = fh;
410 	fh->path = strdup(path);
411 
412 	/*
413 	 * Reset the HTTP state.
414 	 *
415 	 * EDK II's persistent HTTP connection handling is graceless,
416 	 * assuming that all connections are persistent regardless of
417 	 * any Connection: header or HTTP version reported by the
418 	 * server, and failing to send requests when a more sane
419 	 * implementation would seem to be just reestablishing the
420 	 * closed connection.
421 	 *
422 	 * In the hopes of having some robustness, we indicate to the
423 	 * server that we will close the connection by using a
424 	 * Connection: close header. And then here we manually
425 	 * unconfigure and reconfigure the http instance to force the
426 	 * connection closed.
427 	 */
428 	memset(&config, 0, sizeof(config));
429 	memset(&config_access, 0, sizeof(config_access));
430 	config.AccessPoint.IPv4Node = &config_access;
431 	status = oh->http->GetModeData(oh->http, &config);
432 	if (EFI_ERROR(status))
433 		return (efi_status_to_errno(status));
434 	status = oh->http->Configure(oh->http, NULL);
435 	if (EFI_ERROR(status))
436 		return (efi_status_to_errno(status));
437 	status = oh->http->Configure(oh->http, &config);
438 	if (EFI_ERROR(status))
439 		return (efi_status_to_errno(status));
440 
441 	/* Send the read request */
442 	done = false;
443 	status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify,
444 	    &done, &token.Event);
445 	if (EFI_ERROR(status))
446 		return (efi_status_to_errno(status));
447 
448 	/* extract the host portion of the URL */
449 	host = strdup(oh->uri_base);
450 	if (host == NULL)
451 		return (ENOMEM);
452 	hostp = host;
453 	/* Remove the protocol scheme */
454 	c = strchr(host, '/');
455 	if (c != NULL && *(c + 1) == '/')
456 		hostp = (c + 2);
457 
458 	/* Remove any path information */
459 	c = strchr(hostp, '/');
460 	if (c != NULL)
461 		*c = '\0';
462 
463 	token.Status = EFI_NOT_READY;
464 	token.Message = &message;
465 	message.Data.Request = &request;
466 	message.HeaderCount = 3;
467 	message.Headers = headers;
468 	message.BodyLength = 0;
469 	message.Body = NULL;
470 	request.Method = HttpMethodGet;
471 	request.Url = calloc(strlen(oh->uri_base) + strlen(path) + 1, 2);
472 	headers[0].FieldName = (CHAR8 *)"Host";
473 	headers[0].FieldValue = (CHAR8 *)hostp;
474 	headers[1].FieldName = (CHAR8 *)"Connection";
475 	headers[1].FieldValue = (CHAR8 *)"close";
476 	headers[2].FieldName = (CHAR8 *)"Accept";
477 	headers[2].FieldValue = (CHAR8 *)"*/*";
478 	cpy8to16(oh->uri_base, request.Url, strlen(oh->uri_base));
479 	cpy8to16(path, request.Url + strlen(oh->uri_base), strlen(path));
480 	status = oh->http->Request(oh->http, &token);
481 	free(request.Url);
482 	free(host);
483 	if (EFI_ERROR(status)) {
484 		BS->CloseEvent(token.Event);
485 		return (efi_status_to_errno(status));
486 	}
487 
488 	polltime = 0;
489 	while (!done && polltime < EFIHTTP_POLL_TIMEOUT) {
490 		status = oh->http->Poll(oh->http);
491 		if (EFI_ERROR(status))
492 			break;
493 
494 		if (!done) {
495 			delay(100 * 1000);
496 			polltime += 100;
497 		}
498 	}
499 	BS->CloseEvent(token.Event);
500 	if (EFI_ERROR(token.Status))
501 		return (efi_status_to_errno(token.Status));
502 
503 	/* Wait for the read response */
504 	done = false;
505 	status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify,
506 	    &done, &token.Event);
507 	if (EFI_ERROR(status))
508 		return (efi_status_to_errno(status));
509 	token.Status = EFI_NOT_READY;
510 	token.Message = &message;
511 	message.Data.Response = &response;
512 	message.HeaderCount = 0;
513 	message.Headers = NULL;
514 	message.BodyLength = 0;
515 	message.Body = NULL;
516 	response.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS;
517 	status = oh->http->Response(oh->http, &token);
518 	if (EFI_ERROR(status)) {
519 		BS->CloseEvent(token.Event);
520 		return (efi_status_to_errno(status));
521 	}
522 
523 	polltime = 0;
524 	while (!done && polltime < EFIHTTP_POLL_TIMEOUT) {
525 		status = oh->http->Poll(oh->http);
526 		if (EFI_ERROR(status))
527 			break;
528 
529 		if (!done) {
530 			delay(100 * 1000);
531 			polltime += 100;
532 		}
533 	}
534 	BS->CloseEvent(token.Event);
535 	if (EFI_ERROR(token.Status)) {
536 		BS->FreePool(message.Headers);
537 		return (efi_status_to_errno(token.Status));
538 	}
539 	if (response.StatusCode != HTTP_STATUS_200_OK) {
540 		BS->FreePool(message.Headers);
541 		return (EIO);
542 	}
543 	fh->size = 0;
544 	fh->is_dir = false;
545 	for (i = 0; i < message.HeaderCount; i++) {
546 		if (strcasecmp((const char *)message.Headers[i].FieldName,
547 		    "Content-Length") == 0)
548 			fh->size = strtoul((const char *)
549 			    message.Headers[i].FieldValue, NULL, 10);
550 		else if (strcasecmp((const char *)message.Headers[i].FieldName,
551 		    "Content-type") == 0) {
552 			if (strncmp((const char *)message.Headers[i].FieldValue,
553 			    "text/html", 9) == 0)
554 				fh->is_dir = true;
555 		}
556 	}
557 
558 	return (0);
559 }
560 
561 static int
562 efihttp_fs_open(const char *path, struct open_file *f)
563 {
564 	char *path_slash;
565 	int err;
566 
567 	if (!efihttp_init_done)
568 		return (ENXIO);
569 	/*
570 	 * If any path fails to open, try with a trailing slash in
571 	 * case it's a directory.
572 	 */
573 	err = _efihttp_fs_open(path, f);
574 	if (err != 0) {
575 		/*
576 		 * Work around a bug in the EFI HTTP implementation which
577 		 * causes a crash if the http instance isn't torn down
578 		 * between requests.
579 		 * See https://bugzilla.tianocore.org/show_bug.cgi?id=1917
580 		 */
581 		efihttp_dev_close(f);
582 		efihttp_dev_open(f);
583 		path_slash = malloc(strlen(path) + 2);
584 		if (path_slash == NULL)
585 			return (ENOMEM);
586 		strcpy(path_slash, path);
587 		strcat(path_slash, "/");
588 		err = _efihttp_fs_open(path_slash, f);
589 		free(path_slash);
590 	}
591 	return (err);
592 }
593 
594 static int
595 efihttp_fs_close(struct open_file *f __unused)
596 {
597 	return (0);
598 }
599 
600 static int
601 _efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
602 {
603 	EFI_HTTP_TOKEN token;
604 	EFI_HTTP_MESSAGE message;
605 	EFI_STATUS status;
606 	struct devdesc *dev;
607 	struct open_efihttp *oh;
608 	struct file_efihttp *fh;
609 	bool done;
610 	int polltime;
611 
612 	fh = (struct file_efihttp *)f->f_fsdata;
613 
614 	if (fh->size > 0 && fh->offset >= fh->size) {
615 		if (resid != NULL)
616 			*resid = size;
617 
618 		return 0;
619 	}
620 
621 	dev = (struct devdesc *)f->f_devdata;
622 	oh = (struct open_efihttp *)dev->d_opendata;
623 	done = false;
624 	status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify,
625 	    &done, &token.Event);
626 	if (EFI_ERROR(status)) {
627 		return (efi_status_to_errno(status));
628 	}
629 	token.Status = EFI_NOT_READY;
630 	token.Message = &message;
631 	message.Data.Request = NULL;
632 	message.HeaderCount = 0;
633 	message.Headers = NULL;
634 	message.BodyLength = size;
635 	message.Body = buf;
636 	status = oh->http->Response(oh->http, &token);
637 	if (status == EFI_CONNECTION_FIN) {
638 		if (resid)
639 			*resid = size;
640 		return (0);
641 	} else if (EFI_ERROR(status)) {
642 		BS->CloseEvent(token.Event);
643 		return (efi_status_to_errno(status));
644 	}
645 	polltime = 0;
646 	while (!done && polltime < EFIHTTP_POLL_TIMEOUT) {
647 		status = oh->http->Poll(oh->http);
648 		if (EFI_ERROR(status))
649 				break;
650 
651 		if (!done) {
652 			delay(100 * 1000);
653 			polltime += 100;
654 		}
655 	}
656 	BS->CloseEvent(token.Event);
657 	if (token.Status == EFI_CONNECTION_FIN) {
658 		if (resid)
659 			*resid = size;
660 		return (0);
661 	} else if (EFI_ERROR(token.Status))
662 		return (efi_status_to_errno(token.Status));
663 	if (resid)
664 		*resid = size - message.BodyLength;
665 	fh->offset += message.BodyLength;
666 	return (0);
667 }
668 
669 static int
670 efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
671 {
672 	size_t res;
673 	int err = 0;
674 
675 	while (size > 0) {
676 		err = _efihttp_fs_read(f, buf, size, &res);
677 		if (err != 0 || res == size)
678 			goto end;
679 		buf += (size - res);
680 		size = res;
681 	}
682 end:
683 	if (resid)
684 		*resid = size;
685 	return (err);
686 }
687 
688 static int
689 efihttp_fs_write(struct open_file *f __unused, const void *buf __unused,
690     size_t size __unused, size_t *resid __unused)
691 {
692 	return (EIO);
693 }
694 
695 static off_t
696 efihttp_fs_seek(struct open_file *f, off_t offset, int where)
697 {
698 	struct file_efihttp *fh;
699 	char *path;
700 	void *buf;
701 	size_t res, res2;
702 	int err;
703 
704 	fh = (struct file_efihttp *)f->f_fsdata;
705 	if (where == SEEK_SET && fh->offset == offset)
706 		return (0);
707 	if (where == SEEK_SET && fh->offset < offset) {
708 		buf = malloc(1500);
709 		if (buf == NULL)
710 			return (ENOMEM);
711 		res = offset - fh->offset;
712 		while (res > 0) {
713 			err = _efihttp_fs_read(f, buf, min(1500, res), &res2);
714 			if (err != 0) {
715 				free(buf);
716 				return (err);
717 			}
718 			res -= min(1500, res) - res2;
719 		}
720 		free(buf);
721 		return (0);
722 	} else if (where == SEEK_SET) {
723 		path = fh->path;
724 		fh->path = NULL;
725 		efihttp_fs_close(f);
726 		/*
727 		 * Work around a bug in the EFI HTTP implementation which
728 		 * causes a crash if the http instance isn't torn down
729 		 * between requests.
730 		 * See https://bugzilla.tianocore.org/show_bug.cgi?id=1917
731 		 */
732 		efihttp_dev_close(f);
733 		efihttp_dev_open(f);
734 		err = efihttp_fs_open(path, f);
735 		free(path);
736 		if (err != 0)
737 			return (err);
738 		return efihttp_fs_seek(f, offset, where);
739 	}
740 	return (EIO);
741 }
742 
743 static int
744 efihttp_fs_stat(struct open_file *f, struct stat *sb)
745 {
746 	struct file_efihttp *fh;
747 
748 	fh = (struct file_efihttp *)f->f_fsdata;
749 	memset(sb, 0, sizeof(*sb));
750 	sb->st_nlink = 1;
751 	sb->st_mode = 0777 | (fh->is_dir ? S_IFDIR : S_IFREG);
752 	sb->st_size = fh->size;
753 	return (0);
754 }
755 
756 static int
757 efihttp_fs_readdir(struct open_file *f, struct dirent *d)
758 {
759 	static char *dirbuf = NULL, *db2, *cursor;
760 	static int dirbuf_len = 0;
761 	char *end;
762 	struct file_efihttp *fh;
763 
764 	fh = (struct file_efihttp *)f->f_fsdata;
765 	if (dirbuf_len < fh->size) {
766 		db2 = realloc(dirbuf, fh->size);
767 		if (db2 == NULL) {
768 			free(dirbuf);
769 			return (ENOMEM);
770 		} else
771 			dirbuf = db2;
772 
773 		dirbuf_len = fh->size;
774 	}
775 
776 	if (fh->offset != fh->size) {
777 		efihttp_fs_seek(f, 0, SEEK_SET);
778 		efihttp_fs_read(f, dirbuf, dirbuf_len, NULL);
779 		cursor = dirbuf;
780 	}
781 
782 	cursor = strstr(cursor, "<a href=\"");
783 	if (cursor == NULL)
784 		return (ENOENT);
785 	cursor += 9;
786 	end = strchr(cursor, '"');
787 	if (*(end - 1) == '/') {
788 		end--;
789 		d->d_type = DT_DIR;
790 	} else
791 		d->d_type = DT_REG;
792 	memcpy(d->d_name, cursor, end - cursor);
793 	d->d_name[end - cursor] = '\0';
794 
795 	return (0);
796 }
797