xref: /freebsd/stand/efi/libefi/efihttp.c (revision bbee544312e730e438c57e4d5777bd0687ea6b77)
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/types.h>
29 
30 #include <netinet/in.h>
31 #include <netinet/in_systm.h>
32 
33 #include <stand.h>
34 #include <bootstrap.h>
35 #include <net.h>
36 
37 #include <efi.h>
38 #include <efilib.h>
39 #include <Protocol/Http.h>
40 #include <Protocol/Ip4Config2.h>
41 #include <Protocol/ServiceBinding.h>
42 
43 /* Poll timeout in milliseconds */
44 static const int EFIHTTP_POLL_TIMEOUT = 300000;
45 
46 static EFI_GUID http_guid = EFI_HTTP_PROTOCOL_GUID;
47 static EFI_GUID httpsb_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
48 static EFI_GUID ip4config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID;
49 
50 static bool efihttp_init_done = false;
51 
52 struct http_devdesc;
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_open_legacy(struct open_file *f);
59 static int efihttp_dev_open_url(struct open_file *f, struct http_devdesc *hd);
60 static int efihttp_dev_close(struct open_file *f);
61 static int efihttp_parsedev(struct devdesc **, const char *, const char **);
62 
63 static int efihttp_fs_open(const char *path, struct open_file *f);
64 static int efihttp_fs_close(struct open_file *f);
65 static int efihttp_fs_read(struct open_file *f, void *buf, size_t size,
66     size_t *resid);
67 static int efihttp_fs_write(struct open_file *f, const void *buf, size_t size,
68     size_t *resid);
69 static off_t efihttp_fs_seek(struct open_file *f, off_t offset, int where);
70 static int efihttp_fs_stat(struct open_file *f, struct stat *sb);
71 static int efihttp_fs_readdir(struct open_file *f, struct dirent *d);
72 
73 struct open_efihttp {
74 	EFI_HTTP_PROTOCOL *http;
75 	EFI_HANDLE	http_handle;
76 	EFI_HANDLE	dev_handle;
77 	char		*uri_base;
78 };
79 
80 struct file_efihttp {
81 	ssize_t		size;
82 	off_t		offset;
83 	char		*path;
84 	bool		is_dir;
85 };
86 
87 /*
88  * host == NULL for the legacy "httpN:" EFI HTTP Boot form; non-NULL for
89  * the "httpN://host/path" URL form.
90  */
91 struct http_devdesc {
92 	struct devdesc	dd;
93 	char		*host;
94 	int		port;
95 };
96 
97 struct devsw efihttp_dev = {
98 	.dv_name =	"http",
99 	.dv_type =	DEVT_NET,
100 	.dv_init =	efihttp_dev_init,
101 	.dv_strategy =	efihttp_dev_strategy,
102 	.dv_open =	efihttp_dev_open,
103 	.dv_close =	efihttp_dev_close,
104 	.dv_ioctl =	noioctl,
105 	.dv_print =	NULL,
106 	.dv_cleanup =	nullsys,
107 	.dv_parsedev =	efihttp_parsedev,
108 };
109 
110 struct fs_ops efihttp_fsops = {
111 	.fs_name =	"efihttp",
112 	.fo_open =	efihttp_fs_open,
113 	.fo_close =	efihttp_fs_close,
114 	.fo_read =	efihttp_fs_read,
115 	.fo_write =	efihttp_fs_write,
116 	.fo_seek =	efihttp_fs_seek,
117 	.fo_stat =	efihttp_fs_stat,
118 	.fo_readdir =	efihttp_fs_readdir,
119 };
120 
121 static void EFIAPI
122 notify(EFI_EVENT event __unused, void *context)
123 {
124 	bool *b;
125 
126 	b = (bool *)context;
127 	*b = true;
128 }
129 
130 static int
131 setup_ipv4_config2(EFI_HANDLE handle, MAC_ADDR_DEVICE_PATH *mac,
132     IPv4_DEVICE_PATH *ipv4, DNS_DEVICE_PATH *dns)
133 {
134 	EFI_IP4_CONFIG2_PROTOCOL *ip4config2;
135 	EFI_STATUS status;
136 
137 	status = BS->OpenProtocol(handle, &ip4config2_guid,
138 	    (void **)&ip4config2, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
139 	if (EFI_ERROR(status))
140 		return (efi_status_to_errno(status));
141 	if (ipv4 != NULL) {
142 		if (mac != NULL) {
143 			setenv("boot.netif.hwaddr",
144 			    ether_sprintf((u_char *)mac->MacAddress.Addr), 1);
145 		}
146 		setenv("boot.netif.ip",
147 		    inet_ntoa(*(struct in_addr *)ipv4->LocalIpAddress.Addr), 1);
148 		setenv("boot.netif.netmask",
149 		    intoa(*(n_long *)ipv4->SubnetMask.Addr), 1);
150 		setenv("boot.netif.gateway",
151 		    inet_ntoa(*(struct in_addr *)ipv4->GatewayIpAddress.Addr),
152 		    1);
153 		status = ip4config2->SetData(ip4config2,
154 		    Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY),
155 		    &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyStatic });
156 		if (EFI_ERROR(status))
157 			return (efi_status_to_errno(status));
158 
159 		status = ip4config2->SetData(ip4config2,
160 		    Ip4Config2DataTypeManualAddress,
161 		    sizeof(EFI_IP4_CONFIG2_MANUAL_ADDRESS),
162 		    &(EFI_IP4_CONFIG2_MANUAL_ADDRESS) {
163 			.Address = ipv4->LocalIpAddress,
164 			.SubnetMask = ipv4->SubnetMask });
165 		if (EFI_ERROR(status))
166 			return (efi_status_to_errno(status));
167 
168 		if (ipv4->GatewayIpAddress.Addr[0] != 0) {
169 			status = ip4config2->SetData(ip4config2,
170 			    Ip4Config2DataTypeGateway, sizeof(EFI_IPv4_ADDRESS),
171 			    &ipv4->GatewayIpAddress);
172 			if (EFI_ERROR(status))
173 				return (efi_status_to_errno(status));
174 		}
175 
176 		if (dns != NULL) {
177 			status = ip4config2->SetData(ip4config2,
178 			    Ip4Config2DataTypeDnsServer,
179 			    sizeof(EFI_IPv4_ADDRESS), &dns->DnsServerIp);
180 			if (EFI_ERROR(status))
181 				return (efi_status_to_errno(status));
182 		}
183 	} else {
184 		status = ip4config2->SetData(ip4config2,
185 		    Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY),
186 		    &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyDhcp });
187 		if (EFI_ERROR(status))
188 			return (efi_status_to_errno(status));
189 	}
190 
191 	return (0);
192 }
193 
194 /*
195  * Like setup_ipv4_config2()'s static branch, but sourced from the
196  * loader-wide myip/netmask/gateip/nameip globals instead of boot-path
197  * device nodes.
198  */
199 static int
200 setup_ipv4_config2_static(EFI_HANDLE handle, struct in_addr ip, n_long mask,
201     struct in_addr gw, struct in_addr dns)
202 {
203 	EFI_IP4_CONFIG2_PROTOCOL *ip4config2;
204 	EFI_IP4_CONFIG2_MANUAL_ADDRESS manual;
205 	EFI_IPv4_ADDRESS addr;
206 	EFI_STATUS status;
207 
208 	status = BS->OpenProtocol(handle, &ip4config2_guid,
209 	    (void **)&ip4config2, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
210 	if (EFI_ERROR(status))
211 		return (efi_status_to_errno(status));
212 
213 	status = ip4config2->SetData(ip4config2,
214 	    Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY),
215 	    &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyStatic });
216 	if (EFI_ERROR(status))
217 		return (efi_status_to_errno(status));
218 
219 	memset(&manual, 0, sizeof(manual));
220 	memcpy(manual.Address.Addr, &ip, sizeof(manual.Address.Addr));
221 	memcpy(manual.SubnetMask.Addr, &mask, sizeof(manual.SubnetMask.Addr));
222 	status = ip4config2->SetData(ip4config2,
223 	    Ip4Config2DataTypeManualAddress, sizeof(manual), &manual);
224 	if (EFI_ERROR(status))
225 		return (efi_status_to_errno(status));
226 
227 	if (gw.s_addr != 0) {
228 		memcpy(addr.Addr, &gw, sizeof(addr.Addr));
229 		status = ip4config2->SetData(ip4config2,
230 		    Ip4Config2DataTypeGateway, sizeof(addr), &addr);
231 		if (EFI_ERROR(status))
232 			return (efi_status_to_errno(status));
233 	}
234 
235 	if (dns.s_addr != 0) {
236 		memcpy(addr.Addr, &dns, sizeof(addr.Addr));
237 		status = ip4config2->SetData(ip4config2,
238 		    Ip4Config2DataTypeDnsServer, sizeof(addr), &addr);
239 		if (EFI_ERROR(status))
240 			return (efi_status_to_errno(status));
241 	}
242 
243 	return (0);
244 }
245 
246 /*
247  * DHCPs via IP4Config2 directly, not netdev's net_open(): net_open()
248  * opens the NIC's SNP EFI_OPEN_PROTOCOL_EXCLUSIVE (efinet_probe()),
249  * which disconnects the MNP/IP4/HttpDxe chain this handle needs.
250  */
251 static int
252 efi_ip4_dhcp(EFI_HANDLE handle, struct in_addr *ip, n_long *mask,
253     struct in_addr *gw, struct in_addr *dns)
254 {
255 	EFI_IP4_CONFIG2_PROTOCOL *ip4config2;
256 	EFI_IP4_CONFIG2_INTERFACE_INFO *info;
257 	EFI_IPv4_ADDRESS dnsaddr;
258 	EFI_STATUS status;
259 	UINTN sz, i, nroutes;
260 	int polltime;
261 
262 	status = BS->OpenProtocol(handle, &ip4config2_guid,
263 	    (void **)&ip4config2, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
264 	if (EFI_ERROR(status))
265 		return (efi_status_to_errno(status));
266 
267 	status = ip4config2->SetData(ip4config2,
268 	    Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY),
269 	    &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyDhcp });
270 	if (EFI_ERROR(status))
271 		return (efi_status_to_errno(status));
272 
273 	info = NULL;
274 	polltime = 0;
275 	for (;;) {
276 		free(info);
277 		info = NULL;
278 		sz = 0;
279 		status = ip4config2->GetData(ip4config2,
280 		    Ip4Config2DataTypeInterfaceInfo, &sz, NULL);
281 		if (status == EFI_BUFFER_TOO_SMALL) {
282 			info = malloc(sz);
283 			if (info == NULL)
284 				return (ENOMEM);
285 			status = ip4config2->GetData(ip4config2,
286 			    Ip4Config2DataTypeInterfaceInfo, &sz, info);
287 		}
288 		if (!EFI_ERROR(status) &&
289 		    *(uint32_t *)info->StationAddress.Addr != 0)
290 			break;
291 		if (polltime >= EFIHTTP_POLL_TIMEOUT) {
292 			free(info);
293 			return (ENXIO);
294 		}
295 		delay(100 * 1000);
296 		polltime += 100;
297 	}
298 
299 	memcpy(ip, info->StationAddress.Addr, sizeof(*ip));
300 	memcpy(mask, info->SubnetMask.Addr, sizeof(*mask));
301 	gw->s_addr = 0;
302 	nroutes = info->RouteTableSize / sizeof(EFI_IP4_ROUTE_TABLE);
303 	for (i = 0; i < nroutes; i++) {
304 		if (*(uint32_t *)info->RouteTable[i].SubnetAddress.Addr == 0 &&
305 		    *(uint32_t *)info->RouteTable[i].SubnetMask.Addr == 0) {
306 			memcpy(gw, info->RouteTable[i].GatewayAddress.Addr,
307 			    sizeof(*gw));
308 			break;
309 		}
310 	}
311 	free(info);
312 
313 	dns->s_addr = 0;
314 	sz = sizeof(dnsaddr);
315 	status = ip4config2->GetData(ip4config2, Ip4Config2DataTypeDnsServer,
316 	    &sz, &dnsaddr);
317 	if (!EFI_ERROR(status) && sz >= sizeof(dnsaddr))
318 		memcpy(dns, &dnsaddr, sizeof(*dns));
319 
320 	return (0);
321 }
322 
323 static int
324 efihttp_dev_init(void)
325 {
326 	EFI_DEVICE_PATH *imgpath, *devpath;
327 	URI_DEVICE_PATH *uri;
328 	EFI_HANDLE handle;
329 	EFI_STATUS status;
330 	int err;
331 	bool found_http;
332 
333 	imgpath = efi_lookup_image_devpath(IH);
334 	if (imgpath == NULL)
335 		return (ENXIO);
336 	devpath = imgpath;
337 	found_http = false;
338 	for (; !IsDevicePathEnd(devpath);
339 	    devpath = NextDevicePathNode(devpath)) {
340 		if (DevicePathType(devpath) != MESSAGING_DEVICE_PATH ||
341 		    DevicePathSubType(devpath) != MSG_URI_DP)
342 			continue;
343 		uri = (URI_DEVICE_PATH *)devpath;
344 		if (strncmp("http", (const char *)uri->Uri, 4) == 0)
345 			found_http = true;
346 	}
347 	if (!found_http)
348 		return (ENXIO);
349 
350 	status = BS->LocateDevicePath(&httpsb_guid, &imgpath, &handle);
351 	if (EFI_ERROR(status))
352 		return (efi_status_to_errno(status));
353 
354 	err = efi_register_handles(&efihttp_dev, &handle, NULL, 1);
355 	if (!err)
356 		efihttp_init_done = true;
357 
358 	return (err);
359 }
360 
361 static int
362 efihttp_dev_strategy(void *devdata __unused, int rw __unused,
363     daddr_t blk __unused, size_t size __unused, char *buf __unused,
364     size_t *rsize __unused)
365 {
366 	return (EIO);
367 }
368 
369 static int
370 efihttp_dev_open(struct open_file *f, ...)
371 {
372 	struct http_devdesc *hd;
373 
374 	hd = (struct http_devdesc *)f->f_devdata;
375 	if (hd->host == NULL)
376 		return (efihttp_dev_open_legacy(f));
377 	return (efihttp_dev_open_url(f, hd));
378 }
379 
380 /*
381  * EFI HTTP Boot path: handles http_devdesc with host == NULL, usable only
382  * when the boot image's own device path has a URI node.
383  */
384 static int
385 efihttp_dev_open_legacy(struct open_file *f)
386 {
387 	EFI_HTTP_CONFIG_DATA config;
388 	EFI_HTTPv4_ACCESS_POINT config_access;
389 	DNS_DEVICE_PATH *dns;
390 	EFI_DEVICE_PATH *devpath, *imgpath;
391 	EFI_SERVICE_BINDING_PROTOCOL *sb;
392 	IPv4_DEVICE_PATH *ipv4;
393 	MAC_ADDR_DEVICE_PATH *mac;
394 	URI_DEVICE_PATH *uri;
395 	struct devdesc *dev;
396 	struct open_efihttp *oh;
397 	char *c;
398 	EFI_HANDLE handle;
399 	EFI_STATUS status;
400 	int err, len;
401 
402 	if (!efihttp_init_done)
403 		return (ENXIO);
404 
405 	imgpath = efi_lookup_image_devpath(IH);
406 	if (imgpath == NULL)
407 		return (ENXIO);
408 	devpath = imgpath;
409 	status = BS->LocateDevicePath(&httpsb_guid, &devpath, &handle);
410 	if (EFI_ERROR(status))
411 		return (efi_status_to_errno(status));
412 	mac = NULL;
413 	ipv4 = NULL;
414 	dns = NULL;
415 	uri = NULL;
416 	for (; !IsDevicePathEnd(imgpath);
417 	    imgpath = NextDevicePathNode(imgpath)) {
418 		if (DevicePathType(imgpath) != MESSAGING_DEVICE_PATH)
419 			continue;
420 		switch (DevicePathSubType(imgpath)) {
421 		case MSG_MAC_ADDR_DP:
422 			mac = (MAC_ADDR_DEVICE_PATH *)imgpath;
423 			break;
424 		case MSG_IPv4_DP:
425 			ipv4 = (IPv4_DEVICE_PATH *)imgpath;
426 			break;
427 		case MSG_DNS_DP:
428 			dns = (DNS_DEVICE_PATH *)imgpath;
429 			break;
430 		case MSG_URI_DP:
431 			uri = (URI_DEVICE_PATH *)imgpath;
432 			break;
433 		default:
434 			break;
435 		}
436 	}
437 
438 	if (uri == NULL)
439 		return (ENXIO);
440 
441 	err = setup_ipv4_config2(handle, mac, ipv4, dns);
442 	if (err)
443 		return (err);
444 
445 	oh = calloc(1, sizeof(struct open_efihttp));
446 	if (!oh)
447 		return (ENOMEM);
448 	oh->dev_handle = handle;
449 	dev = (struct devdesc *)f->f_devdata;
450 	dev->d_opendata = oh;
451 
452 	status = BS->OpenProtocol(handle, &httpsb_guid, (void **)&sb, IH, NULL,
453 	    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
454 	if (EFI_ERROR(status)) {
455 		err = efi_status_to_errno(status);
456 		goto end;
457 	}
458 
459 	status = sb->CreateChild(sb, &oh->http_handle);
460 	if (EFI_ERROR(status)) {
461 		err = efi_status_to_errno(status);
462 		goto end;
463 	}
464 
465 	status = BS->OpenProtocol(oh->http_handle, &http_guid,
466 	    (void **)&oh->http, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
467 	if (EFI_ERROR(status)) {
468 		sb->DestroyChild(sb, oh->http_handle);
469 		err = efi_status_to_errno(status);
470 		goto end;
471 	}
472 
473 	config.HttpVersion = HttpVersion11;
474 	config.TimeOutMillisec = 0;
475 	config.LocalAddressIsIPv6 = FALSE;
476 	config.AccessPoint.IPv4Node = &config_access;
477 	config_access.UseDefaultAddress = TRUE;
478 	config_access.LocalPort = 0;
479 	status = oh->http->Configure(oh->http, &config);
480 	if (EFI_ERROR(status)) {
481 		sb->DestroyChild(sb, oh->http_handle);
482 		err = efi_status_to_errno(status);
483 		goto end;
484 	}
485 
486 	/*
487 	 * Here we make attempt to construct a "base" URI by stripping
488 	 * the last two path components from the loaded URI under the
489 	 * assumption that it is something like:
490 	 *
491 	 * http://127.0.0.1/foo/boot/loader.efi
492 	 *
493 	 * hoping to arriving at:
494 	 *
495 	 * http://127.0.0.1/foo/
496 	 */
497 	len = DevicePathNodeLength(&uri->Header) - sizeof(URI_DEVICE_PATH);
498 	oh->uri_base = malloc(len + 1);
499 	if (oh->uri_base == NULL) {
500 		err = ENOMEM;
501 		goto end;
502 	}
503 	strncpy(oh->uri_base, (const char *)uri->Uri, len);
504 	oh->uri_base[len] = '\0';
505 	c = strrchr(oh->uri_base, '/');
506 	if (c != NULL)
507 		*c = '\0';
508 	c = strrchr(oh->uri_base, '/');
509 	if (c != NULL && *(c + 1) != '\0')
510 		*(c + 1) = '\0';
511 
512 	err = 0;
513 end:
514 	if (err != 0) {
515 		free(dev->d_opendata);
516 		dev->d_opendata = NULL;
517 	}
518 	return (err);
519 }
520 
521 /*
522  * EFI HTTP URL form: handles http_devdesc with host != NULL, using the
523  * shared myip/netmask/gateip/nameip globals for network config -- reused
524  * if already set, otherwise populated via efi_ip4_dhcp(). No network at
525  * all is an error; nothing here retries.
526  */
527 static int
528 efihttp_dev_open_url(struct open_file *f, struct http_devdesc *hd)
529 {
530 	EFI_HTTP_CONFIG_DATA config;
531 	EFI_HTTPv4_ACCESS_POINT config_access;
532 	EFI_DEVICE_PATH *devpath, *trimmed;
533 	EFI_HANDLE nic, handle;
534 	EFI_SERVICE_BINDING_PROTOCOL *sb;
535 	struct devdesc *dev;
536 	struct open_efihttp *oh;
537 	EFI_STATUS status;
538 	struct in_addr ip, gw, dns;
539 	n_long mask;
540 	int err;
541 
542 	nic = efi_find_handle(&efinet_dev, hd->dd.d_unit);
543 	if (nic == NULL)
544 		return (ENXIO);
545 	devpath = efi_lookup_devpath(nic);
546 	if (devpath == NULL)
547 		return (ENXIO);
548 	trimmed = devpath;
549 	status = BS->LocateDevicePath(&httpsb_guid, &trimmed, &handle);
550 	if (EFI_ERROR(status)) {
551 		/*
552 		 * Whatever chained us here (e.g. iPXE) commonly excludes and
553 		 * then exclusively opens the NIC's SNP for its own raw I/O
554 		 * -- the same thing our own efinet_probe() does -- which
555 		 * disconnects the Mnp/Ip4/.../HttpDxe chain. Nothing
556 		 * reconnects it automatically; ask explicitly.
557 		 */
558 		BS->ConnectController(nic, NULL, NULL, TRUE);
559 		trimmed = devpath;
560 		status = BS->LocateDevicePath(&httpsb_guid, &trimmed, &handle);
561 	}
562 	if (EFI_ERROR(status))
563 		return (efi_status_to_errno(status));
564 
565 	if (myip.s_addr != 0) {
566 		err = setup_ipv4_config2_static(handle, myip, netmask,
567 		    gateip, nameip);
568 	} else {
569 		err = efi_ip4_dhcp(handle, &ip, &mask, &gw, &dns);
570 		if (err == 0) {
571 			myip = ip;
572 			netmask = mask;
573 			gateip = gw;
574 			nameip = dns;
575 			setenv("boot.netif.ip", inet_ntoa(myip), 1);
576 			setenv("boot.netif.netmask", intoa(netmask), 1);
577 			setenv("boot.netif.gateway", inet_ntoa(gateip), 1);
578 		}
579 	}
580 	if (err != 0)
581 		return (err);
582 
583 	oh = calloc(1, sizeof(struct open_efihttp));
584 	if (oh == NULL)
585 		return (ENOMEM);
586 	oh->dev_handle = handle;
587 	dev = (struct devdesc *)f->f_devdata;
588 	dev->d_opendata = oh;
589 
590 	status = BS->OpenProtocol(handle, &httpsb_guid, (void **)&sb, IH, NULL,
591 	    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
592 	if (EFI_ERROR(status)) {
593 		err = efi_status_to_errno(status);
594 		goto end;
595 	}
596 
597 	status = sb->CreateChild(sb, &oh->http_handle);
598 	if (EFI_ERROR(status)) {
599 		err = efi_status_to_errno(status);
600 		goto end;
601 	}
602 
603 	status = BS->OpenProtocol(oh->http_handle, &http_guid,
604 	    (void **)&oh->http, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
605 	if (EFI_ERROR(status)) {
606 		sb->DestroyChild(sb, oh->http_handle);
607 		err = efi_status_to_errno(status);
608 		goto end;
609 	}
610 
611 	config.HttpVersion = HttpVersion11;
612 	config.TimeOutMillisec = 0;
613 	config.LocalAddressIsIPv6 = FALSE;
614 	config.AccessPoint.IPv4Node = &config_access;
615 	config_access.UseDefaultAddress = TRUE;
616 	config_access.LocalPort = 0;
617 	status = oh->http->Configure(oh->http, &config);
618 	if (EFI_ERROR(status)) {
619 		sb->DestroyChild(sb, oh->http_handle);
620 		err = efi_status_to_errno(status);
621 		goto end;
622 	}
623 
624 	/*
625 	 * No trailing slash: every path here is already absolute, and a
626 	 * double slash makes EDK2's HttpDxe reject the request outright
627 	 * (EFI_HTTP_ERROR, not a clean 404).
628 	 */
629 	if (hd->port != 0)
630 		asprintf(&oh->uri_base, "http://%s:%d", hd->host, hd->port);
631 	else
632 		asprintf(&oh->uri_base, "http://%s", hd->host);
633 	if (oh->uri_base == NULL) {
634 		sb->DestroyChild(sb, oh->http_handle);
635 		err = ENOMEM;
636 		goto end;
637 	}
638 
639 	err = 0;
640 end:
641 	if (err != 0) {
642 		free(dev->d_opendata);
643 		dev->d_opendata = NULL;
644 	}
645 	return (err);
646 }
647 
648 static int
649 efihttp_dev_close(struct open_file *f)
650 {
651 	EFI_SERVICE_BINDING_PROTOCOL *sb;
652 	struct http_devdesc *hd;
653 	struct open_efihttp *oh;
654 	EFI_STATUS status;
655 
656 	hd = (struct http_devdesc *)f->f_devdata;
657 	oh = (struct open_efihttp *)hd->dd.d_opendata;
658 	status = BS->OpenProtocol(oh->dev_handle, &httpsb_guid, (void **)&sb,
659 	    IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
660 	if (EFI_ERROR(status))
661 		return (efi_status_to_errno(status));
662 	sb->DestroyChild(sb, oh->http_handle);
663 	free(oh->uri_base);
664 	free(oh);
665 	hd->dd.d_opendata = NULL;
666 	free(hd->host);
667 	hd->host = NULL;
668 	return (0);
669 }
670 
671 /*
672  * Recognizes both "httpN:[/path]" (legacy EFI HTTP Boot) and
673  * "httpN://host[:port][/path]" (see efihttp_dev_open_url()). Falls back
674  * to default_parsedev() for the legacy form so existing "http0:"
675  * currdev strings keep parsing the same way.
676  */
677 static int
678 efihttp_parsedev(struct devdesc **idev, const char *devspec,
679     const char **path)
680 {
681 	struct http_devdesc *dev;
682 	struct devdesc *ldev;
683 	const char *np, *p;
684 	char *host;
685 	int unit, port, err;
686 
687 	np = devspec + strlen(efihttp_dev.dv_name);
688 	err = parse_uri(np, &unit, &host, &port, &p);
689 	if (err == EINVAL) {
690 		err = default_parsedev(&ldev, np, path);
691 		if (err != 0)
692 			return (err);
693 		dev = malloc(sizeof(*dev));
694 		if (dev == NULL) {
695 			free(ldev);
696 			return (ENOMEM);
697 		}
698 		dev->dd = *ldev;
699 		free(ldev);
700 		dev->host = NULL;
701 		dev->port = 0;
702 	} else if (err != 0) {
703 		return (err);
704 	} else {
705 		dev = malloc(sizeof(*dev));
706 		if (dev == NULL) {
707 			free(host);
708 			return (ENOMEM);
709 		}
710 		dev->dd.d_unit = unit;
711 		dev->host = host;
712 		dev->port = port;
713 		if (path != NULL)
714 			*path = p;
715 	}
716 	dev->dd.d_dev = &efihttp_dev;
717 	*idev = &dev->dd;
718 	return (0);
719 }
720 
721 static int
722 _efihttp_fs_open(const char *path, struct open_file *f)
723 {
724 	EFI_HTTP_CONFIG_DATA config;
725 	EFI_HTTPv4_ACCESS_POINT config_access;
726 	EFI_HTTP_TOKEN token;
727 	EFI_HTTP_MESSAGE message;
728 	EFI_HTTP_REQUEST_DATA request;
729 	EFI_HTTP_RESPONSE_DATA response;
730 	EFI_HTTP_HEADER headers[3];
731 	char *host, *hostp;
732 	char *c;
733 	struct devdesc *dev;
734 	struct open_efihttp *oh;
735 	struct file_efihttp *fh;
736 	EFI_STATUS status;
737 	UINTN i;
738 	int polltime;
739 	bool done;
740 
741 	dev = (struct devdesc *)f->f_devdata;
742 	oh = (struct open_efihttp *)dev->d_opendata;
743 	fh = calloc(1, sizeof(struct file_efihttp));
744 	if (fh == NULL)
745 		return (ENOMEM);
746 	f->f_fsdata = fh;
747 	fh->path = strdup(path);
748 
749 	/*
750 	 * Reset the HTTP state.
751 	 *
752 	 * EDK II's persistent HTTP connection handling is graceless,
753 	 * assuming that all connections are persistent regardless of
754 	 * any Connection: header or HTTP version reported by the
755 	 * server, and failing to send requests when a more sane
756 	 * implementation would seem to be just reestablishing the
757 	 * closed connection.
758 	 *
759 	 * In the hopes of having some robustness, we indicate to the
760 	 * server that we will close the connection by using a
761 	 * Connection: close header. And then here we manually
762 	 * unconfigure and reconfigure the http instance to force the
763 	 * connection closed.
764 	 */
765 	memset(&config, 0, sizeof(config));
766 	memset(&config_access, 0, sizeof(config_access));
767 	config.AccessPoint.IPv4Node = &config_access;
768 	status = oh->http->GetModeData(oh->http, &config);
769 	if (EFI_ERROR(status))
770 		return (efi_status_to_errno(status));
771 	status = oh->http->Configure(oh->http, NULL);
772 	if (EFI_ERROR(status))
773 		return (efi_status_to_errno(status));
774 	status = oh->http->Configure(oh->http, &config);
775 	if (EFI_ERROR(status))
776 		return (efi_status_to_errno(status));
777 
778 	/* Send the read request */
779 	done = false;
780 	status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify,
781 	    &done, &token.Event);
782 	if (EFI_ERROR(status))
783 		return (efi_status_to_errno(status));
784 
785 	/* extract the host portion of the URL */
786 	host = strdup(oh->uri_base);
787 	if (host == NULL)
788 		return (ENOMEM);
789 	hostp = host;
790 	/* Remove the protocol scheme */
791 	c = strchr(host, '/');
792 	if (c != NULL && *(c + 1) == '/')
793 		hostp = (c + 2);
794 
795 	/* Remove any path information */
796 	c = strchr(hostp, '/');
797 	if (c != NULL)
798 		*c = '\0';
799 
800 	token.Status = EFI_NOT_READY;
801 	token.Message = &message;
802 	message.Data.Request = &request;
803 	message.HeaderCount = 3;
804 	message.Headers = headers;
805 	message.BodyLength = 0;
806 	message.Body = NULL;
807 	request.Method = HttpMethodGet;
808 	request.Url = calloc(strlen(oh->uri_base) + strlen(path) + 1, 2);
809 	headers[0].FieldName = (CHAR8 *)"Host";
810 	headers[0].FieldValue = (CHAR8 *)hostp;
811 	headers[1].FieldName = (CHAR8 *)"Connection";
812 	headers[1].FieldValue = (CHAR8 *)"close";
813 	headers[2].FieldName = (CHAR8 *)"Accept";
814 	headers[2].FieldValue = (CHAR8 *)"*/*";
815 	cpy8to16(oh->uri_base, request.Url, strlen(oh->uri_base));
816 	cpy8to16(path, request.Url + strlen(oh->uri_base), strlen(path));
817 	status = oh->http->Request(oh->http, &token);
818 	free(request.Url);
819 	free(host);
820 	if (EFI_ERROR(status)) {
821 		BS->CloseEvent(token.Event);
822 		return (efi_status_to_errno(status));
823 	}
824 
825 	polltime = 0;
826 	while (!done && polltime < EFIHTTP_POLL_TIMEOUT) {
827 		status = oh->http->Poll(oh->http);
828 		if (EFI_ERROR(status))
829 			break;
830 
831 		if (!done) {
832 			delay(100 * 1000);
833 			polltime += 100;
834 		}
835 	}
836 	BS->CloseEvent(token.Event);
837 	if (EFI_ERROR(token.Status))
838 		return (efi_status_to_errno(token.Status));
839 
840 	/* Wait for the read response */
841 	done = false;
842 	status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify,
843 	    &done, &token.Event);
844 	if (EFI_ERROR(status))
845 		return (efi_status_to_errno(status));
846 	token.Status = EFI_NOT_READY;
847 	token.Message = &message;
848 	message.Data.Response = &response;
849 	message.HeaderCount = 0;
850 	message.Headers = NULL;
851 	message.BodyLength = 0;
852 	message.Body = NULL;
853 	response.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS;
854 	status = oh->http->Response(oh->http, &token);
855 	if (EFI_ERROR(status)) {
856 		BS->CloseEvent(token.Event);
857 		return (efi_status_to_errno(status));
858 	}
859 
860 	polltime = 0;
861 	while (!done && polltime < EFIHTTP_POLL_TIMEOUT) {
862 		status = oh->http->Poll(oh->http);
863 		if (EFI_ERROR(status))
864 			break;
865 
866 		if (!done) {
867 			delay(100 * 1000);
868 			polltime += 100;
869 		}
870 	}
871 	BS->CloseEvent(token.Event);
872 	if (EFI_ERROR(token.Status)) {
873 		BS->FreePool(message.Headers);
874 		return (efi_status_to_errno(token.Status));
875 	}
876 	if (response.StatusCode != HTTP_STATUS_200_OK) {
877 		BS->FreePool(message.Headers);
878 		return (EIO);
879 	}
880 	fh->size = 0;
881 	fh->is_dir = false;
882 	for (i = 0; i < message.HeaderCount; i++) {
883 		if (strcasecmp((const char *)message.Headers[i].FieldName,
884 		    "Content-Length") == 0)
885 			fh->size = strtoul((const char *)
886 			    message.Headers[i].FieldValue, NULL, 10);
887 		else if (strcasecmp((const char *)message.Headers[i].FieldName,
888 		    "Content-type") == 0) {
889 			if (strncmp((const char *)message.Headers[i].FieldValue,
890 			    "text/html", 9) == 0)
891 				fh->is_dir = true;
892 		}
893 	}
894 
895 	return (0);
896 }
897 
898 static int
899 efihttp_fs_open(const char *path, struct open_file *f)
900 {
901 	char *path_slash;
902 	int err;
903 
904 	/*
905 	 * efihttp_init_done is irrelevant here: efihttp_dev_open_legacy()
906 	 * already gates on it before a legacy-form open can reach this
907 	 * point, and the URL form never touches it at all.
908 	 */
909 	if (f->f_dev != &efihttp_dev)
910 		return (EINVAL);
911 	/*
912 	 * If any path fails to open, try with a trailing slash in
913 	 * case it's a directory.
914 	 */
915 	err = _efihttp_fs_open(path, f);
916 	if (err != 0) {
917 		/*
918 		 * Work around a bug in the EFI HTTP implementation which
919 		 * causes a crash if the http instance isn't torn down
920 		 * between requests.
921 		 * See https://bugzilla.tianocore.org/show_bug.cgi?id=1917
922 		 */
923 		efihttp_dev_close(f);
924 		efihttp_dev_open(f);
925 		path_slash = malloc(strlen(path) + 2);
926 		if (path_slash == NULL)
927 			return (ENOMEM);
928 		strcpy(path_slash, path);
929 		strcat(path_slash, "/");
930 		err = _efihttp_fs_open(path_slash, f);
931 		free(path_slash);
932 	}
933 	return (err);
934 }
935 
936 static int
937 efihttp_fs_close(struct open_file *f __unused)
938 {
939 	return (0);
940 }
941 
942 static int
943 _efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
944 {
945 	EFI_HTTP_TOKEN token;
946 	EFI_HTTP_MESSAGE message;
947 	EFI_STATUS status;
948 	struct devdesc *dev;
949 	struct open_efihttp *oh;
950 	struct file_efihttp *fh;
951 	bool done;
952 	int polltime;
953 
954 	fh = (struct file_efihttp *)f->f_fsdata;
955 
956 	if (fh->size > 0 && fh->offset >= fh->size) {
957 		if (resid != NULL)
958 			*resid = size;
959 
960 		return 0;
961 	}
962 
963 	dev = (struct devdesc *)f->f_devdata;
964 	oh = (struct open_efihttp *)dev->d_opendata;
965 	done = false;
966 	status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify,
967 	    &done, &token.Event);
968 	if (EFI_ERROR(status)) {
969 		return (efi_status_to_errno(status));
970 	}
971 	token.Status = EFI_NOT_READY;
972 	token.Message = &message;
973 	message.Data.Request = NULL;
974 	message.HeaderCount = 0;
975 	message.Headers = NULL;
976 	message.BodyLength = size;
977 	message.Body = buf;
978 	status = oh->http->Response(oh->http, &token);
979 	if (status == EFI_CONNECTION_FIN) {
980 		if (resid)
981 			*resid = size;
982 		return (0);
983 	} else if (EFI_ERROR(status)) {
984 		BS->CloseEvent(token.Event);
985 		return (efi_status_to_errno(status));
986 	}
987 	polltime = 0;
988 	while (!done && polltime < EFIHTTP_POLL_TIMEOUT) {
989 		status = oh->http->Poll(oh->http);
990 		if (EFI_ERROR(status))
991 				break;
992 
993 		if (!done) {
994 			delay(100 * 1000);
995 			polltime += 100;
996 		}
997 	}
998 	BS->CloseEvent(token.Event);
999 	if (token.Status == EFI_CONNECTION_FIN) {
1000 		if (resid)
1001 			*resid = size;
1002 		return (0);
1003 	} else if (EFI_ERROR(token.Status))
1004 		return (efi_status_to_errno(token.Status));
1005 	if (resid)
1006 		*resid = size - message.BodyLength;
1007 	fh->offset += message.BodyLength;
1008 	return (0);
1009 }
1010 
1011 static int
1012 efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
1013 {
1014 	size_t res;
1015 	int err = 0;
1016 
1017 	while (size > 0) {
1018 		err = _efihttp_fs_read(f, buf, size, &res);
1019 		if (err != 0 || res == size)
1020 			goto end;
1021 		buf += (size - res);
1022 		size = res;
1023 	}
1024 end:
1025 	if (resid)
1026 		*resid = size;
1027 	return (err);
1028 }
1029 
1030 static int
1031 efihttp_fs_write(struct open_file *f __unused, const void *buf __unused,
1032     size_t size __unused, size_t *resid __unused)
1033 {
1034 	return (EIO);
1035 }
1036 
1037 static off_t
1038 efihttp_fs_seek(struct open_file *f, off_t offset, int where)
1039 {
1040 	struct file_efihttp *fh;
1041 	char *path;
1042 	void *buf;
1043 	size_t res, res2;
1044 	int err;
1045 
1046 	fh = (struct file_efihttp *)f->f_fsdata;
1047 	if (where == SEEK_SET && fh->offset == offset)
1048 		return (0);
1049 	if (where == SEEK_SET && fh->offset < offset) {
1050 		buf = malloc(1500);
1051 		if (buf == NULL)
1052 			return (ENOMEM);
1053 		res = offset - fh->offset;
1054 		while (res > 0) {
1055 			err = _efihttp_fs_read(f, buf, min(1500, res), &res2);
1056 			if (err != 0) {
1057 				free(buf);
1058 				return (err);
1059 			}
1060 			res -= min(1500, res) - res2;
1061 		}
1062 		free(buf);
1063 		return (0);
1064 	} else if (where == SEEK_SET) {
1065 		path = fh->path;
1066 		fh->path = NULL;
1067 		efihttp_fs_close(f);
1068 		/*
1069 		 * Work around a bug in the EFI HTTP implementation which
1070 		 * causes a crash if the http instance isn't torn down
1071 		 * between requests.
1072 		 * See https://bugzilla.tianocore.org/show_bug.cgi?id=1917
1073 		 */
1074 		efihttp_dev_close(f);
1075 		efihttp_dev_open(f);
1076 		err = efihttp_fs_open(path, f);
1077 		free(path);
1078 		if (err != 0)
1079 			return (err);
1080 		return efihttp_fs_seek(f, offset, where);
1081 	}
1082 	return (EIO);
1083 }
1084 
1085 static int
1086 efihttp_fs_stat(struct open_file *f, struct stat *sb)
1087 {
1088 	struct file_efihttp *fh;
1089 
1090 	fh = (struct file_efihttp *)f->f_fsdata;
1091 	memset(sb, 0, sizeof(*sb));
1092 	sb->st_nlink = 1;
1093 	sb->st_mode = 0777 | (fh->is_dir ? S_IFDIR : S_IFREG);
1094 	sb->st_size = fh->size;
1095 	return (0);
1096 }
1097 
1098 static int
1099 efihttp_fs_readdir(struct open_file *f, struct dirent *d)
1100 {
1101 	static char *dirbuf = NULL, *db2, *cursor;
1102 	static int dirbuf_len = 0;
1103 	char *end;
1104 	struct file_efihttp *fh;
1105 
1106 	fh = (struct file_efihttp *)f->f_fsdata;
1107 	if (dirbuf_len < fh->size) {
1108 		db2 = realloc(dirbuf, fh->size);
1109 		if (db2 == NULL) {
1110 			free(dirbuf);
1111 			return (ENOMEM);
1112 		} else
1113 			dirbuf = db2;
1114 
1115 		dirbuf_len = fh->size;
1116 	}
1117 
1118 	if (fh->offset != fh->size) {
1119 		efihttp_fs_seek(f, 0, SEEK_SET);
1120 		efihttp_fs_read(f, dirbuf, dirbuf_len, NULL);
1121 		cursor = dirbuf;
1122 	}
1123 
1124 	cursor = strstr(cursor, "<a href=\"");
1125 	if (cursor == NULL)
1126 		return (ENOENT);
1127 	cursor += 9;
1128 	end = strchr(cursor, '"');
1129 	if (*(end - 1) == '/') {
1130 		end--;
1131 		d->d_type = DT_DIR;
1132 	} else
1133 		d->d_type = DT_REG;
1134 	memcpy(d->d_name, cursor, end - cursor);
1135 	d->d_name[end - cursor] = '\0';
1136 
1137 	return (0);
1138 }
1139