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