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