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