2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019 Intel Corporation
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/types.h>
35 #include <netinet/in.h>
36 #include <netinet/in_systm.h>
44 #include <Protocol/Http.h>
45 #include <Protocol/Ip4Config2.h>
46 #include <Protocol/ServiceBinding.h>
48 /* Poll timeout in milliseconds */
49 static const int EFIHTTP_POLL_TIMEOUT = 300000;
51 static EFI_GUID http_guid = EFI_HTTP_PROTOCOL_GUID;
52 static EFI_GUID httpsb_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
53 static EFI_GUID ip4config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID;
55 static bool efihttp_init_done = false;
57 static int efihttp_dev_init(void);
58 static int efihttp_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size,
59 char *buf, size_t *rsize);
60 static int efihttp_dev_open(struct open_file *f, ...);
61 static int efihttp_dev_close(struct open_file *f);
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,
67 static int efihttp_fs_write(struct open_file *f, const void *buf, size_t size,
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);
74 EFI_HTTP_PROTOCOL *http;
75 EFI_HANDLE http_handle;
76 EFI_HANDLE dev_handle;
87 struct devsw efihttp_dev = {
90 .dv_init = efihttp_dev_init,
91 .dv_strategy = efihttp_dev_strategy,
92 .dv_open = efihttp_dev_open,
93 .dv_close = efihttp_dev_close,
99 struct fs_ops efihttp_fsops = {
100 .fs_name = "efihttp",
101 .fo_open = efihttp_fs_open,
102 .fo_close = efihttp_fs_close,
103 .fo_read = efihttp_fs_read,
104 .fo_write = efihttp_fs_write,
105 .fo_seek = efihttp_fs_seek,
106 .fo_stat = efihttp_fs_stat,
107 .fo_readdir = efihttp_fs_readdir,
111 notify(EFI_EVENT event __unused, void *context)
120 setup_ipv4_config2(EFI_HANDLE handle, MAC_ADDR_DEVICE_PATH *mac,
121 IPv4_DEVICE_PATH *ipv4, DNS_DEVICE_PATH *dns)
123 EFI_IP4_CONFIG2_PROTOCOL *ip4config2;
126 status = BS->OpenProtocol(handle, &ip4config2_guid,
127 (void **)&ip4config2, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
128 if (EFI_ERROR(status))
129 return (efi_status_to_errno(status));
132 setenv("boot.netif.hwaddr",
133 ether_sprintf((u_char *)mac->MacAddress.Addr), 1);
135 setenv("boot.netif.ip",
136 inet_ntoa(*(struct in_addr *)ipv4->LocalIpAddress.Addr), 1);
137 setenv("boot.netif.netmask",
138 intoa(*(n_long *)ipv4->SubnetMask.Addr), 1);
139 setenv("boot.netif.gateway",
140 inet_ntoa(*(struct in_addr *)ipv4->GatewayIpAddress.Addr),
142 status = ip4config2->SetData(ip4config2,
143 Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY),
144 &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyStatic });
145 if (EFI_ERROR(status))
146 return (efi_status_to_errno(status));
148 status = ip4config2->SetData(ip4config2,
149 Ip4Config2DataTypeManualAddress,
150 sizeof(EFI_IP4_CONFIG2_MANUAL_ADDRESS),
151 &(EFI_IP4_CONFIG2_MANUAL_ADDRESS) {
152 .Address = ipv4->LocalIpAddress,
153 .SubnetMask = ipv4->SubnetMask });
154 if (EFI_ERROR(status))
155 return (efi_status_to_errno(status));
157 if (ipv4->GatewayIpAddress.Addr[0] != 0) {
158 status = ip4config2->SetData(ip4config2,
159 Ip4Config2DataTypeGateway, sizeof(EFI_IPv4_ADDRESS),
160 &ipv4->GatewayIpAddress);
161 if (EFI_ERROR(status))
162 return (efi_status_to_errno(status));
166 status = ip4config2->SetData(ip4config2,
167 Ip4Config2DataTypeDnsServer,
168 sizeof(EFI_IPv4_ADDRESS), &dns->DnsServerIp);
169 if (EFI_ERROR(status))
170 return (efi_status_to_errno(status));
173 status = ip4config2->SetData(ip4config2,
174 Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY),
175 &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyDhcp });
176 if (EFI_ERROR(status))
177 return (efi_status_to_errno(status));
184 efihttp_dev_init(void)
186 EFI_DEVICE_PATH *imgpath, *devpath;
187 URI_DEVICE_PATH *uri;
193 imgpath = efi_lookup_image_devpath(IH);
198 for (; !IsDevicePathEnd(devpath);
199 devpath = NextDevicePathNode(devpath)) {
200 if (DevicePathType(devpath) != MESSAGING_DEVICE_PATH ||
201 DevicePathSubType(devpath) != MSG_URI_DP)
203 uri = (URI_DEVICE_PATH *)devpath;
204 if (strncmp("http", (const char *)uri->Uri, 4) == 0)
210 status = BS->LocateDevicePath(&httpsb_guid, &imgpath, &handle);
211 if (EFI_ERROR(status))
212 return (efi_status_to_errno(status));
214 err = efi_register_handles(&efihttp_dev, &handle, NULL, 1);
216 efihttp_init_done = true;
222 efihttp_dev_strategy(void *devdata __unused, int rw __unused,
223 daddr_t blk __unused, size_t size __unused, char *buf __unused,
224 size_t *rsize __unused)
230 efihttp_dev_open(struct open_file *f, ...)
232 EFI_HTTP_CONFIG_DATA config;
233 EFI_HTTPv4_ACCESS_POINT config_access;
234 DNS_DEVICE_PATH *dns;
235 EFI_DEVICE_PATH *devpath, *imgpath;
236 EFI_SERVICE_BINDING_PROTOCOL *sb;
237 IPv4_DEVICE_PATH *ipv4;
238 MAC_ADDR_DEVICE_PATH *mac;
239 URI_DEVICE_PATH *uri;
241 struct open_efihttp *oh;
247 if (!efihttp_init_done)
250 imgpath = efi_lookup_image_devpath(IH);
254 status = BS->LocateDevicePath(&httpsb_guid, &devpath, &handle);
255 if (EFI_ERROR(status))
256 return (efi_status_to_errno(status));
261 for (; !IsDevicePathEnd(imgpath);
262 imgpath = NextDevicePathNode(imgpath)) {
263 if (DevicePathType(imgpath) != MESSAGING_DEVICE_PATH)
265 switch (DevicePathSubType(imgpath)) {
266 case MSG_MAC_ADDR_DP:
267 mac = (MAC_ADDR_DEVICE_PATH *)imgpath;
270 ipv4 = (IPv4_DEVICE_PATH *)imgpath;
273 dns = (DNS_DEVICE_PATH *)imgpath;
276 uri = (URI_DEVICE_PATH *)imgpath;
286 err = setup_ipv4_config2(handle, mac, ipv4, dns);
290 oh = calloc(1, sizeof(struct open_efihttp));
293 oh->dev_handle = handle;
294 dev = (struct devdesc *)f->f_devdata;
295 dev->d_opendata = oh;
297 status = BS->OpenProtocol(handle, &httpsb_guid, (void **)&sb, IH, NULL,
298 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
299 if (EFI_ERROR(status)) {
300 err = efi_status_to_errno(status);
304 status = sb->CreateChild(sb, &oh->http_handle);
305 if (EFI_ERROR(status)) {
306 err = efi_status_to_errno(status);
310 status = BS->OpenProtocol(oh->http_handle, &http_guid,
311 (void **)&oh->http, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
312 if (EFI_ERROR(status)) {
313 sb->DestroyChild(sb, oh->http_handle);
314 err = efi_status_to_errno(status);
318 config.HttpVersion = HttpVersion11;
319 config.TimeOutMillisec = 0;
320 config.LocalAddressIsIPv6 = FALSE;
321 config.AccessPoint.IPv4Node = &config_access;
322 config_access.UseDefaultAddress = TRUE;
323 config_access.LocalPort = 0;
324 status = oh->http->Configure(oh->http, &config);
325 if (EFI_ERROR(status)) {
326 sb->DestroyChild(sb, oh->http_handle);
327 err = efi_status_to_errno(status);
332 * Here we make attempt to construct a "base" URI by stripping
333 * the last two path components from the loaded URI under the
334 * assumption that it is something like:
336 * http://127.0.0.1/foo/boot/loader.efi
338 * hoping to arriving at:
340 * http://127.0.0.1/foo/
342 len = DevicePathNodeLength(&uri->Header) - sizeof(URI_DEVICE_PATH);
343 oh->uri_base = malloc(len + 1);
344 if (oh->uri_base == NULL) {
348 strncpy(oh->uri_base, (const char *)uri->Uri, len);
349 oh->uri_base[len] = '\0';
350 c = strrchr(oh->uri_base, '/');
353 c = strrchr(oh->uri_base, '/');
354 if (c != NULL && *(c + 1) != '\0')
360 free(dev->d_opendata);
361 dev->d_opendata = NULL;
367 efihttp_dev_close(struct open_file *f)
369 EFI_SERVICE_BINDING_PROTOCOL *sb;
371 struct open_efihttp *oh;
374 dev = (struct devdesc *)f->f_devdata;
375 oh = (struct open_efihttp *)dev->d_opendata;
376 status = BS->OpenProtocol(oh->dev_handle, &httpsb_guid, (void **)&sb,
377 IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
378 if (EFI_ERROR(status))
379 return (efi_status_to_errno(status));
380 sb->DestroyChild(sb, oh->http_handle);
383 dev->d_opendata = NULL;
388 _efihttp_fs_open(const char *path, struct open_file *f)
390 EFI_HTTP_CONFIG_DATA config;
391 EFI_HTTPv4_ACCESS_POINT config_access;
392 EFI_HTTP_TOKEN token;
393 EFI_HTTP_MESSAGE message;
394 EFI_HTTP_REQUEST_DATA request;
395 EFI_HTTP_RESPONSE_DATA response;
396 EFI_HTTP_HEADER headers[3];
400 struct open_efihttp *oh;
401 struct file_efihttp *fh;
407 dev = (struct devdesc *)f->f_devdata;
408 oh = (struct open_efihttp *)dev->d_opendata;
409 fh = calloc(1, sizeof(struct file_efihttp));
413 fh->path = strdup(path);
416 * Reset the HTTP state.
418 * EDK II's persistent HTTP connection handling is graceless,
419 * assuming that all connections are persistent regardless of
420 * any Connection: header or HTTP version reported by the
421 * server, and failing to send requests when a more sane
422 * implementation would seem to be just reestablishing the
425 * In the hopes of having some robustness, we indicate to the
426 * server that we will close the connection by using a
427 * Connection: close header. And then here we manually
428 * unconfigure and reconfigure the http instance to force the
431 memset(&config, 0, sizeof(config));
432 memset(&config_access, 0, sizeof(config_access));
433 config.AccessPoint.IPv4Node = &config_access;
434 status = oh->http->GetModeData(oh->http, &config);
435 if (EFI_ERROR(status))
436 return (efi_status_to_errno(status));
437 status = oh->http->Configure(oh->http, NULL);
438 if (EFI_ERROR(status))
439 return (efi_status_to_errno(status));
440 status = oh->http->Configure(oh->http, &config);
441 if (EFI_ERROR(status))
442 return (efi_status_to_errno(status));
444 /* Send the read request */
446 status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify,
447 &done, &token.Event);
448 if (EFI_ERROR(status))
449 return (efi_status_to_errno(status));
451 /* extract the host portion of the URL */
452 host = strdup(oh->uri_base);
456 /* Remove the protocol scheme */
457 c = strchr(host, '/');
458 if (c != NULL && *(c + 1) == '/')
461 /* Remove any path information */
462 c = strchr(hostp, '/');
466 token.Status = EFI_NOT_READY;
467 token.Message = &message;
468 message.Data.Request = &request;
469 message.HeaderCount = 3;
470 message.Headers = headers;
471 message.BodyLength = 0;
473 request.Method = HttpMethodGet;
474 request.Url = calloc(strlen(oh->uri_base) + strlen(path) + 1, 2);
475 headers[0].FieldName = (CHAR8 *)"Host";
476 headers[0].FieldValue = (CHAR8 *)hostp;
477 headers[1].FieldName = (CHAR8 *)"Connection";
478 headers[1].FieldValue = (CHAR8 *)"close";
479 headers[2].FieldName = (CHAR8 *)"Accept";
480 headers[2].FieldValue = (CHAR8 *)"*/*";
481 cpy8to16(oh->uri_base, request.Url, strlen(oh->uri_base));
482 cpy8to16(path, request.Url + strlen(oh->uri_base), strlen(path));
483 status = oh->http->Request(oh->http, &token);
486 if (EFI_ERROR(status)) {
487 BS->CloseEvent(token.Event);
488 return (efi_status_to_errno(status));
492 while (!done && polltime < EFIHTTP_POLL_TIMEOUT) {
493 status = oh->http->Poll(oh->http);
494 if (EFI_ERROR(status))
502 BS->CloseEvent(token.Event);
503 if (EFI_ERROR(token.Status))
504 return (efi_status_to_errno(token.Status));
506 /* Wait for the read response */
508 status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify,
509 &done, &token.Event);
510 if (EFI_ERROR(status))
511 return (efi_status_to_errno(status));
512 token.Status = EFI_NOT_READY;
513 token.Message = &message;
514 message.Data.Response = &response;
515 message.HeaderCount = 0;
516 message.Headers = NULL;
517 message.BodyLength = 0;
519 response.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS;
520 status = oh->http->Response(oh->http, &token);
521 if (EFI_ERROR(status)) {
522 BS->CloseEvent(token.Event);
523 return (efi_status_to_errno(status));
527 while (!done && polltime < EFIHTTP_POLL_TIMEOUT) {
528 status = oh->http->Poll(oh->http);
529 if (EFI_ERROR(status))
537 BS->CloseEvent(token.Event);
538 if (EFI_ERROR(token.Status)) {
539 BS->FreePool(message.Headers);
540 return (efi_status_to_errno(token.Status));
542 if (response.StatusCode != HTTP_STATUS_200_OK) {
543 BS->FreePool(message.Headers);
548 for (i = 0; i < message.HeaderCount; i++) {
549 if (strcasecmp((const char *)message.Headers[i].FieldName,
550 "Content-Length") == 0)
551 fh->size = strtoul((const char *)
552 message.Headers[i].FieldValue, NULL, 10);
553 else if (strcasecmp((const char *)message.Headers[i].FieldName,
554 "Content-type") == 0) {
555 if (strncmp((const char *)message.Headers[i].FieldValue,
556 "text/html", 9) == 0)
565 efihttp_fs_open(const char *path, struct open_file *f)
570 if (!efihttp_init_done)
573 * If any path fails to open, try with a trailing slash in
574 * case it's a directory.
576 err = _efihttp_fs_open(path, f);
578 path_slash = malloc(strlen(path) + 2);
579 if (path_slash == NULL)
581 strcpy(path_slash, path);
582 strcat(path_slash, "/");
583 err = _efihttp_fs_open(path_slash, f);
590 efihttp_fs_close(struct open_file *f __unused)
596 _efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
598 EFI_HTTP_TOKEN token;
599 EFI_HTTP_MESSAGE message;
602 struct open_efihttp *oh;
603 struct file_efihttp *fh;
607 fh = (struct file_efihttp *)f->f_fsdata;
609 if (fh->size > 0 && fh->offset >= fh->size) {
616 dev = (struct devdesc *)f->f_devdata;
617 oh = (struct open_efihttp *)dev->d_opendata;
619 status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify,
620 &done, &token.Event);
621 if (EFI_ERROR(status)) {
622 return (efi_status_to_errno(status));
624 token.Status = EFI_NOT_READY;
625 token.Message = &message;
626 message.Data.Request = NULL;
627 message.HeaderCount = 0;
628 message.Headers = NULL;
629 message.BodyLength = size;
631 status = oh->http->Response(oh->http, &token);
632 if (status == EFI_CONNECTION_FIN) {
636 } else if (EFI_ERROR(status)) {
637 BS->CloseEvent(token.Event);
638 return (efi_status_to_errno(status));
641 while (!done && polltime < EFIHTTP_POLL_TIMEOUT) {
642 status = oh->http->Poll(oh->http);
643 if (EFI_ERROR(status))
651 BS->CloseEvent(token.Event);
652 if (token.Status == EFI_CONNECTION_FIN) {
656 } else if (EFI_ERROR(token.Status))
657 return (efi_status_to_errno(token.Status));
659 *resid = size - message.BodyLength;
660 fh->offset += message.BodyLength;
665 efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
671 err = _efihttp_fs_read(f, buf, size, &res);
672 if (err != 0 || res == size)
684 efihttp_fs_write(struct open_file *f __unused, const void *buf __unused,
685 size_t size __unused, size_t *resid __unused)
691 efihttp_fs_seek(struct open_file *f, off_t offset, int where)
693 struct file_efihttp *fh;
699 fh = (struct file_efihttp *)f->f_fsdata;
700 if (where == SEEK_SET && fh->offset == offset)
702 if (where == SEEK_SET && fh->offset < offset) {
704 res = offset - fh->offset;
706 err = _efihttp_fs_read(f, buf, min(1500, res), &res2);
711 res -= min(1500, res) - res2;
715 } else if (where == SEEK_SET) {
719 err = efihttp_fs_open(path, f);
723 return efihttp_fs_seek(f, offset, where);
729 efihttp_fs_stat(struct open_file *f, struct stat *sb)
731 struct file_efihttp *fh;
733 fh = (struct file_efihttp *)f->f_fsdata;
734 memset(sb, 0, sizeof(*sb));
736 sb->st_mode = 0777 | (fh->is_dir ? S_IFDIR : S_IFREG);
737 sb->st_size = fh->size;
742 efihttp_fs_readdir(struct open_file *f, struct dirent *d)
744 static char *dirbuf = NULL, *db2, *cursor;
745 static int dirbuf_len = 0;
747 struct file_efihttp *fh;
749 fh = (struct file_efihttp *)f->f_fsdata;
750 if (dirbuf_len < fh->size) {
751 db2 = realloc(dirbuf, fh->size);
758 dirbuf_len = fh->size;
761 if (fh->offset != fh->size) {
762 efihttp_fs_seek(f, 0, SEEK_SET);
763 efihttp_fs_read(f, dirbuf, dirbuf_len, NULL);
767 cursor = strstr(cursor, "<a href=\"");
771 end = strchr(cursor, '"');
772 if (*(end - 1) == '/') {
777 memcpy(d->d_name, cursor, end - cursor);
778 d->d_name[end - cursor] = '\0';