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