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 * $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 = nullsys, 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 } 801