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/types.h> 29 30 #include <netinet/in.h> 31 #include <netinet/in_systm.h> 32 33 #include <stand.h> 34 #include <bootstrap.h> 35 #include <net.h> 36 37 #include <efi.h> 38 #include <efilib.h> 39 #include <Protocol/Http.h> 40 #include <Protocol/Ip4Config2.h> 41 #include <Protocol/ServiceBinding.h> 42 43 /* Poll timeout in milliseconds */ 44 static const int EFIHTTP_POLL_TIMEOUT = 300000; 45 46 static EFI_GUID http_guid = EFI_HTTP_PROTOCOL_GUID; 47 static EFI_GUID httpsb_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID; 48 static EFI_GUID ip4config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID; 49 50 static bool efihttp_init_done = false; 51 52 struct http_devdesc; 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_open_legacy(struct open_file *f); 59 static int efihttp_dev_open_url(struct open_file *f, struct http_devdesc *hd); 60 static int efihttp_dev_close(struct open_file *f); 61 static int efihttp_parsedev(struct devdesc **, const char *, const char **); 62 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, 66 size_t *resid); 67 static int efihttp_fs_write(struct open_file *f, const void *buf, size_t size, 68 size_t *resid); 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); 72 73 struct open_efihttp { 74 EFI_HTTP_PROTOCOL *http; 75 EFI_HANDLE http_handle; 76 EFI_HANDLE dev_handle; 77 char *uri_base; 78 }; 79 80 struct file_efihttp { 81 ssize_t size; 82 off_t offset; 83 char *path; 84 bool is_dir; 85 }; 86 87 /* 88 * host == NULL for the legacy "httpN:" EFI HTTP Boot form; non-NULL for 89 * the "httpN://host/path" URL form. 90 */ 91 struct http_devdesc { 92 struct devdesc dd; 93 char *host; 94 int port; 95 }; 96 97 struct devsw efihttp_dev = { 98 .dv_name = "http", 99 .dv_type = DEVT_NET, 100 .dv_init = efihttp_dev_init, 101 .dv_strategy = efihttp_dev_strategy, 102 .dv_open = efihttp_dev_open, 103 .dv_close = efihttp_dev_close, 104 .dv_ioctl = noioctl, 105 .dv_print = NULL, 106 .dv_cleanup = nullsys, 107 .dv_parsedev = efihttp_parsedev, 108 }; 109 110 struct fs_ops efihttp_fsops = { 111 .fs_name = "efihttp", 112 .fo_open = efihttp_fs_open, 113 .fo_close = efihttp_fs_close, 114 .fo_read = efihttp_fs_read, 115 .fo_write = efihttp_fs_write, 116 .fo_seek = efihttp_fs_seek, 117 .fo_stat = efihttp_fs_stat, 118 .fo_readdir = efihttp_fs_readdir, 119 }; 120 121 static void EFIAPI 122 notify(EFI_EVENT event __unused, void *context) 123 { 124 bool *b; 125 126 b = (bool *)context; 127 *b = true; 128 } 129 130 static int 131 setup_ipv4_config2(EFI_HANDLE handle, MAC_ADDR_DEVICE_PATH *mac, 132 IPv4_DEVICE_PATH *ipv4, DNS_DEVICE_PATH *dns) 133 { 134 EFI_IP4_CONFIG2_PROTOCOL *ip4config2; 135 EFI_STATUS status; 136 137 status = BS->OpenProtocol(handle, &ip4config2_guid, 138 (void **)&ip4config2, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 139 if (EFI_ERROR(status)) 140 return (efi_status_to_errno(status)); 141 if (ipv4 != NULL) { 142 if (mac != NULL) { 143 setenv("boot.netif.hwaddr", 144 ether_sprintf((u_char *)mac->MacAddress.Addr), 1); 145 } 146 setenv("boot.netif.ip", 147 inet_ntoa(*(struct in_addr *)ipv4->LocalIpAddress.Addr), 1); 148 setenv("boot.netif.netmask", 149 intoa(*(n_long *)ipv4->SubnetMask.Addr), 1); 150 setenv("boot.netif.gateway", 151 inet_ntoa(*(struct in_addr *)ipv4->GatewayIpAddress.Addr), 152 1); 153 status = ip4config2->SetData(ip4config2, 154 Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY), 155 &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyStatic }); 156 if (EFI_ERROR(status)) 157 return (efi_status_to_errno(status)); 158 159 status = ip4config2->SetData(ip4config2, 160 Ip4Config2DataTypeManualAddress, 161 sizeof(EFI_IP4_CONFIG2_MANUAL_ADDRESS), 162 &(EFI_IP4_CONFIG2_MANUAL_ADDRESS) { 163 .Address = ipv4->LocalIpAddress, 164 .SubnetMask = ipv4->SubnetMask }); 165 if (EFI_ERROR(status)) 166 return (efi_status_to_errno(status)); 167 168 if (ipv4->GatewayIpAddress.Addr[0] != 0) { 169 status = ip4config2->SetData(ip4config2, 170 Ip4Config2DataTypeGateway, sizeof(EFI_IPv4_ADDRESS), 171 &ipv4->GatewayIpAddress); 172 if (EFI_ERROR(status)) 173 return (efi_status_to_errno(status)); 174 } 175 176 if (dns != NULL) { 177 status = ip4config2->SetData(ip4config2, 178 Ip4Config2DataTypeDnsServer, 179 sizeof(EFI_IPv4_ADDRESS), &dns->DnsServerIp); 180 if (EFI_ERROR(status)) 181 return (efi_status_to_errno(status)); 182 } 183 } else { 184 status = ip4config2->SetData(ip4config2, 185 Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY), 186 &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyDhcp }); 187 if (EFI_ERROR(status)) 188 return (efi_status_to_errno(status)); 189 } 190 191 return (0); 192 } 193 194 /* 195 * Like setup_ipv4_config2()'s static branch, but sourced from the 196 * loader-wide myip/netmask/gateip/nameip globals instead of boot-path 197 * device nodes. 198 */ 199 static int 200 setup_ipv4_config2_static(EFI_HANDLE handle, struct in_addr ip, n_long mask, 201 struct in_addr gw, struct in_addr dns) 202 { 203 EFI_IP4_CONFIG2_PROTOCOL *ip4config2; 204 EFI_IP4_CONFIG2_MANUAL_ADDRESS manual; 205 EFI_IPv4_ADDRESS addr; 206 EFI_STATUS status; 207 208 status = BS->OpenProtocol(handle, &ip4config2_guid, 209 (void **)&ip4config2, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 210 if (EFI_ERROR(status)) 211 return (efi_status_to_errno(status)); 212 213 status = ip4config2->SetData(ip4config2, 214 Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY), 215 &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyStatic }); 216 if (EFI_ERROR(status)) 217 return (efi_status_to_errno(status)); 218 219 memset(&manual, 0, sizeof(manual)); 220 memcpy(manual.Address.Addr, &ip, sizeof(manual.Address.Addr)); 221 memcpy(manual.SubnetMask.Addr, &mask, sizeof(manual.SubnetMask.Addr)); 222 status = ip4config2->SetData(ip4config2, 223 Ip4Config2DataTypeManualAddress, sizeof(manual), &manual); 224 if (EFI_ERROR(status)) 225 return (efi_status_to_errno(status)); 226 227 if (gw.s_addr != 0) { 228 memcpy(addr.Addr, &gw, sizeof(addr.Addr)); 229 status = ip4config2->SetData(ip4config2, 230 Ip4Config2DataTypeGateway, sizeof(addr), &addr); 231 if (EFI_ERROR(status)) 232 return (efi_status_to_errno(status)); 233 } 234 235 if (dns.s_addr != 0) { 236 memcpy(addr.Addr, &dns, sizeof(addr.Addr)); 237 status = ip4config2->SetData(ip4config2, 238 Ip4Config2DataTypeDnsServer, sizeof(addr), &addr); 239 if (EFI_ERROR(status)) 240 return (efi_status_to_errno(status)); 241 } 242 243 return (0); 244 } 245 246 /* 247 * DHCPs via IP4Config2 directly, not netdev's net_open(): net_open() 248 * opens the NIC's SNP EFI_OPEN_PROTOCOL_EXCLUSIVE (efinet_probe()), 249 * which disconnects the MNP/IP4/HttpDxe chain this handle needs. 250 */ 251 static int 252 efi_ip4_dhcp(EFI_HANDLE handle, struct in_addr *ip, n_long *mask, 253 struct in_addr *gw, struct in_addr *dns) 254 { 255 EFI_IP4_CONFIG2_PROTOCOL *ip4config2; 256 EFI_IP4_CONFIG2_INTERFACE_INFO *info; 257 EFI_IPv4_ADDRESS dnsaddr; 258 EFI_STATUS status; 259 UINTN sz, i, nroutes; 260 int polltime; 261 262 status = BS->OpenProtocol(handle, &ip4config2_guid, 263 (void **)&ip4config2, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 264 if (EFI_ERROR(status)) 265 return (efi_status_to_errno(status)); 266 267 status = ip4config2->SetData(ip4config2, 268 Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY), 269 &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyDhcp }); 270 if (EFI_ERROR(status)) 271 return (efi_status_to_errno(status)); 272 273 info = NULL; 274 polltime = 0; 275 for (;;) { 276 free(info); 277 info = NULL; 278 sz = 0; 279 status = ip4config2->GetData(ip4config2, 280 Ip4Config2DataTypeInterfaceInfo, &sz, NULL); 281 if (status == EFI_BUFFER_TOO_SMALL) { 282 info = malloc(sz); 283 if (info == NULL) 284 return (ENOMEM); 285 status = ip4config2->GetData(ip4config2, 286 Ip4Config2DataTypeInterfaceInfo, &sz, info); 287 } 288 if (!EFI_ERROR(status) && 289 *(uint32_t *)info->StationAddress.Addr != 0) 290 break; 291 if (polltime >= EFIHTTP_POLL_TIMEOUT) { 292 free(info); 293 return (ENXIO); 294 } 295 delay(100 * 1000); 296 polltime += 100; 297 } 298 299 memcpy(ip, info->StationAddress.Addr, sizeof(*ip)); 300 memcpy(mask, info->SubnetMask.Addr, sizeof(*mask)); 301 gw->s_addr = 0; 302 nroutes = info->RouteTableSize / sizeof(EFI_IP4_ROUTE_TABLE); 303 for (i = 0; i < nroutes; i++) { 304 if (*(uint32_t *)info->RouteTable[i].SubnetAddress.Addr == 0 && 305 *(uint32_t *)info->RouteTable[i].SubnetMask.Addr == 0) { 306 memcpy(gw, info->RouteTable[i].GatewayAddress.Addr, 307 sizeof(*gw)); 308 break; 309 } 310 } 311 free(info); 312 313 dns->s_addr = 0; 314 sz = sizeof(dnsaddr); 315 status = ip4config2->GetData(ip4config2, Ip4Config2DataTypeDnsServer, 316 &sz, &dnsaddr); 317 if (!EFI_ERROR(status) && sz >= sizeof(dnsaddr)) 318 memcpy(dns, &dnsaddr, sizeof(*dns)); 319 320 return (0); 321 } 322 323 static int 324 efihttp_dev_init(void) 325 { 326 EFI_DEVICE_PATH *imgpath, *devpath; 327 URI_DEVICE_PATH *uri; 328 EFI_HANDLE handle; 329 EFI_STATUS status; 330 int err; 331 bool found_http; 332 333 imgpath = efi_lookup_image_devpath(IH); 334 if (imgpath == NULL) 335 return (ENXIO); 336 devpath = imgpath; 337 found_http = false; 338 for (; !IsDevicePathEnd(devpath); 339 devpath = NextDevicePathNode(devpath)) { 340 if (DevicePathType(devpath) != MESSAGING_DEVICE_PATH || 341 DevicePathSubType(devpath) != MSG_URI_DP) 342 continue; 343 uri = (URI_DEVICE_PATH *)devpath; 344 if (strncmp("http", (const char *)uri->Uri, 4) == 0) 345 found_http = true; 346 } 347 if (!found_http) 348 return (ENXIO); 349 350 status = BS->LocateDevicePath(&httpsb_guid, &imgpath, &handle); 351 if (EFI_ERROR(status)) 352 return (efi_status_to_errno(status)); 353 354 err = efi_register_handles(&efihttp_dev, &handle, NULL, 1); 355 if (!err) 356 efihttp_init_done = true; 357 358 return (err); 359 } 360 361 static int 362 efihttp_dev_strategy(void *devdata __unused, int rw __unused, 363 daddr_t blk __unused, size_t size __unused, char *buf __unused, 364 size_t *rsize __unused) 365 { 366 return (EIO); 367 } 368 369 static int 370 efihttp_dev_open(struct open_file *f, ...) 371 { 372 struct http_devdesc *hd; 373 374 hd = (struct http_devdesc *)f->f_devdata; 375 if (hd->host == NULL) 376 return (efihttp_dev_open_legacy(f)); 377 return (efihttp_dev_open_url(f, hd)); 378 } 379 380 /* 381 * EFI HTTP Boot path: handles http_devdesc with host == NULL, usable only 382 * when the boot image's own device path has a URI node. 383 */ 384 static int 385 efihttp_dev_open_legacy(struct open_file *f) 386 { 387 EFI_HTTP_CONFIG_DATA config; 388 EFI_HTTPv4_ACCESS_POINT config_access; 389 DNS_DEVICE_PATH *dns; 390 EFI_DEVICE_PATH *devpath, *imgpath; 391 EFI_SERVICE_BINDING_PROTOCOL *sb; 392 IPv4_DEVICE_PATH *ipv4; 393 MAC_ADDR_DEVICE_PATH *mac; 394 URI_DEVICE_PATH *uri; 395 struct devdesc *dev; 396 struct open_efihttp *oh; 397 char *c; 398 EFI_HANDLE handle; 399 EFI_STATUS status; 400 int err, len; 401 402 if (!efihttp_init_done) 403 return (ENXIO); 404 405 imgpath = efi_lookup_image_devpath(IH); 406 if (imgpath == NULL) 407 return (ENXIO); 408 devpath = imgpath; 409 status = BS->LocateDevicePath(&httpsb_guid, &devpath, &handle); 410 if (EFI_ERROR(status)) 411 return (efi_status_to_errno(status)); 412 mac = NULL; 413 ipv4 = NULL; 414 dns = NULL; 415 uri = NULL; 416 for (; !IsDevicePathEnd(imgpath); 417 imgpath = NextDevicePathNode(imgpath)) { 418 if (DevicePathType(imgpath) != MESSAGING_DEVICE_PATH) 419 continue; 420 switch (DevicePathSubType(imgpath)) { 421 case MSG_MAC_ADDR_DP: 422 mac = (MAC_ADDR_DEVICE_PATH *)imgpath; 423 break; 424 case MSG_IPv4_DP: 425 ipv4 = (IPv4_DEVICE_PATH *)imgpath; 426 break; 427 case MSG_DNS_DP: 428 dns = (DNS_DEVICE_PATH *)imgpath; 429 break; 430 case MSG_URI_DP: 431 uri = (URI_DEVICE_PATH *)imgpath; 432 break; 433 default: 434 break; 435 } 436 } 437 438 if (uri == NULL) 439 return (ENXIO); 440 441 err = setup_ipv4_config2(handle, mac, ipv4, dns); 442 if (err) 443 return (err); 444 445 oh = calloc(1, sizeof(struct open_efihttp)); 446 if (!oh) 447 return (ENOMEM); 448 oh->dev_handle = handle; 449 dev = (struct devdesc *)f->f_devdata; 450 dev->d_opendata = oh; 451 452 status = BS->OpenProtocol(handle, &httpsb_guid, (void **)&sb, IH, NULL, 453 EFI_OPEN_PROTOCOL_GET_PROTOCOL); 454 if (EFI_ERROR(status)) { 455 err = efi_status_to_errno(status); 456 goto end; 457 } 458 459 status = sb->CreateChild(sb, &oh->http_handle); 460 if (EFI_ERROR(status)) { 461 err = efi_status_to_errno(status); 462 goto end; 463 } 464 465 status = BS->OpenProtocol(oh->http_handle, &http_guid, 466 (void **)&oh->http, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 467 if (EFI_ERROR(status)) { 468 sb->DestroyChild(sb, oh->http_handle); 469 err = efi_status_to_errno(status); 470 goto end; 471 } 472 473 config.HttpVersion = HttpVersion11; 474 config.TimeOutMillisec = 0; 475 config.LocalAddressIsIPv6 = FALSE; 476 config.AccessPoint.IPv4Node = &config_access; 477 config_access.UseDefaultAddress = TRUE; 478 config_access.LocalPort = 0; 479 status = oh->http->Configure(oh->http, &config); 480 if (EFI_ERROR(status)) { 481 sb->DestroyChild(sb, oh->http_handle); 482 err = efi_status_to_errno(status); 483 goto end; 484 } 485 486 /* 487 * Here we make attempt to construct a "base" URI by stripping 488 * the last two path components from the loaded URI under the 489 * assumption that it is something like: 490 * 491 * http://127.0.0.1/foo/boot/loader.efi 492 * 493 * hoping to arriving at: 494 * 495 * http://127.0.0.1/foo/ 496 */ 497 len = DevicePathNodeLength(&uri->Header) - sizeof(URI_DEVICE_PATH); 498 oh->uri_base = malloc(len + 1); 499 if (oh->uri_base == NULL) { 500 err = ENOMEM; 501 goto end; 502 } 503 strncpy(oh->uri_base, (const char *)uri->Uri, len); 504 oh->uri_base[len] = '\0'; 505 c = strrchr(oh->uri_base, '/'); 506 if (c != NULL) 507 *c = '\0'; 508 c = strrchr(oh->uri_base, '/'); 509 if (c != NULL && *(c + 1) != '\0') 510 *(c + 1) = '\0'; 511 512 err = 0; 513 end: 514 if (err != 0) { 515 free(dev->d_opendata); 516 dev->d_opendata = NULL; 517 } 518 return (err); 519 } 520 521 /* 522 * EFI HTTP URL form: handles http_devdesc with host != NULL, using the 523 * shared myip/netmask/gateip/nameip globals for network config -- reused 524 * if already set, otherwise populated via efi_ip4_dhcp(). No network at 525 * all is an error; nothing here retries. 526 */ 527 static int 528 efihttp_dev_open_url(struct open_file *f, struct http_devdesc *hd) 529 { 530 EFI_HTTP_CONFIG_DATA config; 531 EFI_HTTPv4_ACCESS_POINT config_access; 532 EFI_DEVICE_PATH *devpath, *trimmed; 533 EFI_HANDLE nic, handle; 534 EFI_SERVICE_BINDING_PROTOCOL *sb; 535 struct devdesc *dev; 536 struct open_efihttp *oh; 537 EFI_STATUS status; 538 struct in_addr ip, gw, dns; 539 n_long mask; 540 int err; 541 542 nic = efi_find_handle(&efinet_dev, hd->dd.d_unit); 543 if (nic == NULL) 544 return (ENXIO); 545 devpath = efi_lookup_devpath(nic); 546 if (devpath == NULL) 547 return (ENXIO); 548 trimmed = devpath; 549 status = BS->LocateDevicePath(&httpsb_guid, &trimmed, &handle); 550 if (EFI_ERROR(status)) { 551 /* 552 * Whatever chained us here (e.g. iPXE) commonly excludes and 553 * then exclusively opens the NIC's SNP for its own raw I/O 554 * -- the same thing our own efinet_probe() does -- which 555 * disconnects the Mnp/Ip4/.../HttpDxe chain. Nothing 556 * reconnects it automatically; ask explicitly. 557 */ 558 BS->ConnectController(nic, NULL, NULL, TRUE); 559 trimmed = devpath; 560 status = BS->LocateDevicePath(&httpsb_guid, &trimmed, &handle); 561 } 562 if (EFI_ERROR(status)) 563 return (efi_status_to_errno(status)); 564 565 if (myip.s_addr != 0) { 566 err = setup_ipv4_config2_static(handle, myip, netmask, 567 gateip, nameip); 568 } else { 569 err = efi_ip4_dhcp(handle, &ip, &mask, &gw, &dns); 570 if (err == 0) { 571 myip = ip; 572 netmask = mask; 573 gateip = gw; 574 nameip = dns; 575 setenv("boot.netif.ip", inet_ntoa(myip), 1); 576 setenv("boot.netif.netmask", intoa(netmask), 1); 577 setenv("boot.netif.gateway", inet_ntoa(gateip), 1); 578 } 579 } 580 if (err != 0) 581 return (err); 582 583 oh = calloc(1, sizeof(struct open_efihttp)); 584 if (oh == NULL) 585 return (ENOMEM); 586 oh->dev_handle = handle; 587 dev = (struct devdesc *)f->f_devdata; 588 dev->d_opendata = oh; 589 590 status = BS->OpenProtocol(handle, &httpsb_guid, (void **)&sb, IH, NULL, 591 EFI_OPEN_PROTOCOL_GET_PROTOCOL); 592 if (EFI_ERROR(status)) { 593 err = efi_status_to_errno(status); 594 goto end; 595 } 596 597 status = sb->CreateChild(sb, &oh->http_handle); 598 if (EFI_ERROR(status)) { 599 err = efi_status_to_errno(status); 600 goto end; 601 } 602 603 status = BS->OpenProtocol(oh->http_handle, &http_guid, 604 (void **)&oh->http, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 605 if (EFI_ERROR(status)) { 606 sb->DestroyChild(sb, oh->http_handle); 607 err = efi_status_to_errno(status); 608 goto end; 609 } 610 611 config.HttpVersion = HttpVersion11; 612 config.TimeOutMillisec = 0; 613 config.LocalAddressIsIPv6 = FALSE; 614 config.AccessPoint.IPv4Node = &config_access; 615 config_access.UseDefaultAddress = TRUE; 616 config_access.LocalPort = 0; 617 status = oh->http->Configure(oh->http, &config); 618 if (EFI_ERROR(status)) { 619 sb->DestroyChild(sb, oh->http_handle); 620 err = efi_status_to_errno(status); 621 goto end; 622 } 623 624 /* 625 * No trailing slash: every path here is already absolute, and a 626 * double slash makes EDK2's HttpDxe reject the request outright 627 * (EFI_HTTP_ERROR, not a clean 404). 628 */ 629 if (hd->port != 0) 630 asprintf(&oh->uri_base, "http://%s:%d", hd->host, hd->port); 631 else 632 asprintf(&oh->uri_base, "http://%s", hd->host); 633 if (oh->uri_base == NULL) { 634 sb->DestroyChild(sb, oh->http_handle); 635 err = ENOMEM; 636 goto end; 637 } 638 639 err = 0; 640 end: 641 if (err != 0) { 642 free(dev->d_opendata); 643 dev->d_opendata = NULL; 644 } 645 return (err); 646 } 647 648 static int 649 efihttp_dev_close(struct open_file *f) 650 { 651 EFI_SERVICE_BINDING_PROTOCOL *sb; 652 struct http_devdesc *hd; 653 struct open_efihttp *oh; 654 EFI_STATUS status; 655 656 hd = (struct http_devdesc *)f->f_devdata; 657 oh = (struct open_efihttp *)hd->dd.d_opendata; 658 status = BS->OpenProtocol(oh->dev_handle, &httpsb_guid, (void **)&sb, 659 IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 660 if (EFI_ERROR(status)) 661 return (efi_status_to_errno(status)); 662 sb->DestroyChild(sb, oh->http_handle); 663 free(oh->uri_base); 664 free(oh); 665 hd->dd.d_opendata = NULL; 666 free(hd->host); 667 hd->host = NULL; 668 return (0); 669 } 670 671 /* 672 * Recognizes both "httpN:[/path]" (legacy EFI HTTP Boot) and 673 * "httpN://host[:port][/path]" (see efihttp_dev_open_url()). Falls back 674 * to default_parsedev() for the legacy form so existing "http0:" 675 * currdev strings keep parsing the same way. 676 */ 677 static int 678 efihttp_parsedev(struct devdesc **idev, const char *devspec, 679 const char **path) 680 { 681 struct http_devdesc *dev; 682 struct devdesc *ldev; 683 const char *np, *p; 684 char *host; 685 int unit, port, err; 686 687 np = devspec + strlen(efihttp_dev.dv_name); 688 err = parse_uri(np, &unit, &host, &port, &p); 689 if (err == EINVAL) { 690 err = default_parsedev(&ldev, np, path); 691 if (err != 0) 692 return (err); 693 dev = malloc(sizeof(*dev)); 694 if (dev == NULL) { 695 free(ldev); 696 return (ENOMEM); 697 } 698 dev->dd = *ldev; 699 free(ldev); 700 dev->host = NULL; 701 dev->port = 0; 702 } else if (err != 0) { 703 return (err); 704 } else { 705 dev = malloc(sizeof(*dev)); 706 if (dev == NULL) { 707 free(host); 708 return (ENOMEM); 709 } 710 dev->dd.d_unit = unit; 711 dev->host = host; 712 dev->port = port; 713 if (path != NULL) 714 *path = p; 715 } 716 dev->dd.d_dev = &efihttp_dev; 717 *idev = &dev->dd; 718 return (0); 719 } 720 721 static int 722 _efihttp_fs_open(const char *path, struct open_file *f) 723 { 724 EFI_HTTP_CONFIG_DATA config; 725 EFI_HTTPv4_ACCESS_POINT config_access; 726 EFI_HTTP_TOKEN token; 727 EFI_HTTP_MESSAGE message; 728 EFI_HTTP_REQUEST_DATA request; 729 EFI_HTTP_RESPONSE_DATA response; 730 EFI_HTTP_HEADER headers[3]; 731 char *host, *hostp; 732 char *c; 733 struct devdesc *dev; 734 struct open_efihttp *oh; 735 struct file_efihttp *fh; 736 EFI_STATUS status; 737 UINTN i; 738 int polltime; 739 bool done; 740 741 dev = (struct devdesc *)f->f_devdata; 742 oh = (struct open_efihttp *)dev->d_opendata; 743 fh = calloc(1, sizeof(struct file_efihttp)); 744 if (fh == NULL) 745 return (ENOMEM); 746 f->f_fsdata = fh; 747 fh->path = strdup(path); 748 749 /* 750 * Reset the HTTP state. 751 * 752 * EDK II's persistent HTTP connection handling is graceless, 753 * assuming that all connections are persistent regardless of 754 * any Connection: header or HTTP version reported by the 755 * server, and failing to send requests when a more sane 756 * implementation would seem to be just reestablishing the 757 * closed connection. 758 * 759 * In the hopes of having some robustness, we indicate to the 760 * server that we will close the connection by using a 761 * Connection: close header. And then here we manually 762 * unconfigure and reconfigure the http instance to force the 763 * connection closed. 764 */ 765 memset(&config, 0, sizeof(config)); 766 memset(&config_access, 0, sizeof(config_access)); 767 config.AccessPoint.IPv4Node = &config_access; 768 status = oh->http->GetModeData(oh->http, &config); 769 if (EFI_ERROR(status)) 770 return (efi_status_to_errno(status)); 771 status = oh->http->Configure(oh->http, NULL); 772 if (EFI_ERROR(status)) 773 return (efi_status_to_errno(status)); 774 status = oh->http->Configure(oh->http, &config); 775 if (EFI_ERROR(status)) 776 return (efi_status_to_errno(status)); 777 778 /* Send the read request */ 779 done = false; 780 status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify, 781 &done, &token.Event); 782 if (EFI_ERROR(status)) 783 return (efi_status_to_errno(status)); 784 785 /* extract the host portion of the URL */ 786 host = strdup(oh->uri_base); 787 if (host == NULL) 788 return (ENOMEM); 789 hostp = host; 790 /* Remove the protocol scheme */ 791 c = strchr(host, '/'); 792 if (c != NULL && *(c + 1) == '/') 793 hostp = (c + 2); 794 795 /* Remove any path information */ 796 c = strchr(hostp, '/'); 797 if (c != NULL) 798 *c = '\0'; 799 800 token.Status = EFI_NOT_READY; 801 token.Message = &message; 802 message.Data.Request = &request; 803 message.HeaderCount = 3; 804 message.Headers = headers; 805 message.BodyLength = 0; 806 message.Body = NULL; 807 request.Method = HttpMethodGet; 808 request.Url = calloc(strlen(oh->uri_base) + strlen(path) + 1, 2); 809 headers[0].FieldName = (CHAR8 *)"Host"; 810 headers[0].FieldValue = (CHAR8 *)hostp; 811 headers[1].FieldName = (CHAR8 *)"Connection"; 812 headers[1].FieldValue = (CHAR8 *)"close"; 813 headers[2].FieldName = (CHAR8 *)"Accept"; 814 headers[2].FieldValue = (CHAR8 *)"*/*"; 815 cpy8to16(oh->uri_base, request.Url, strlen(oh->uri_base)); 816 cpy8to16(path, request.Url + strlen(oh->uri_base), strlen(path)); 817 status = oh->http->Request(oh->http, &token); 818 free(request.Url); 819 free(host); 820 if (EFI_ERROR(status)) { 821 BS->CloseEvent(token.Event); 822 return (efi_status_to_errno(status)); 823 } 824 825 polltime = 0; 826 while (!done && polltime < EFIHTTP_POLL_TIMEOUT) { 827 status = oh->http->Poll(oh->http); 828 if (EFI_ERROR(status)) 829 break; 830 831 if (!done) { 832 delay(100 * 1000); 833 polltime += 100; 834 } 835 } 836 BS->CloseEvent(token.Event); 837 if (EFI_ERROR(token.Status)) 838 return (efi_status_to_errno(token.Status)); 839 840 /* Wait for the read response */ 841 done = false; 842 status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify, 843 &done, &token.Event); 844 if (EFI_ERROR(status)) 845 return (efi_status_to_errno(status)); 846 token.Status = EFI_NOT_READY; 847 token.Message = &message; 848 message.Data.Response = &response; 849 message.HeaderCount = 0; 850 message.Headers = NULL; 851 message.BodyLength = 0; 852 message.Body = NULL; 853 response.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS; 854 status = oh->http->Response(oh->http, &token); 855 if (EFI_ERROR(status)) { 856 BS->CloseEvent(token.Event); 857 return (efi_status_to_errno(status)); 858 } 859 860 polltime = 0; 861 while (!done && polltime < EFIHTTP_POLL_TIMEOUT) { 862 status = oh->http->Poll(oh->http); 863 if (EFI_ERROR(status)) 864 break; 865 866 if (!done) { 867 delay(100 * 1000); 868 polltime += 100; 869 } 870 } 871 BS->CloseEvent(token.Event); 872 if (EFI_ERROR(token.Status)) { 873 BS->FreePool(message.Headers); 874 return (efi_status_to_errno(token.Status)); 875 } 876 if (response.StatusCode != HTTP_STATUS_200_OK) { 877 BS->FreePool(message.Headers); 878 return (EIO); 879 } 880 fh->size = 0; 881 fh->is_dir = false; 882 for (i = 0; i < message.HeaderCount; i++) { 883 if (strcasecmp((const char *)message.Headers[i].FieldName, 884 "Content-Length") == 0) 885 fh->size = strtoul((const char *) 886 message.Headers[i].FieldValue, NULL, 10); 887 else if (strcasecmp((const char *)message.Headers[i].FieldName, 888 "Content-type") == 0) { 889 if (strncmp((const char *)message.Headers[i].FieldValue, 890 "text/html", 9) == 0) 891 fh->is_dir = true; 892 } 893 } 894 895 return (0); 896 } 897 898 static int 899 efihttp_fs_open(const char *path, struct open_file *f) 900 { 901 char *path_slash; 902 int err; 903 904 /* 905 * efihttp_init_done is irrelevant here: efihttp_dev_open_legacy() 906 * already gates on it before a legacy-form open can reach this 907 * point, and the URL form never touches it at all. 908 */ 909 if (f->f_dev != &efihttp_dev) 910 return (EINVAL); 911 /* 912 * If any path fails to open, try with a trailing slash in 913 * case it's a directory. 914 */ 915 err = _efihttp_fs_open(path, f); 916 if (err != 0) { 917 /* 918 * Work around a bug in the EFI HTTP implementation which 919 * causes a crash if the http instance isn't torn down 920 * between requests. 921 * See https://bugzilla.tianocore.org/show_bug.cgi?id=1917 922 */ 923 efihttp_dev_close(f); 924 efihttp_dev_open(f); 925 path_slash = malloc(strlen(path) + 2); 926 if (path_slash == NULL) 927 return (ENOMEM); 928 strcpy(path_slash, path); 929 strcat(path_slash, "/"); 930 err = _efihttp_fs_open(path_slash, f); 931 free(path_slash); 932 } 933 return (err); 934 } 935 936 static int 937 efihttp_fs_close(struct open_file *f __unused) 938 { 939 return (0); 940 } 941 942 static int 943 _efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 944 { 945 EFI_HTTP_TOKEN token; 946 EFI_HTTP_MESSAGE message; 947 EFI_STATUS status; 948 struct devdesc *dev; 949 struct open_efihttp *oh; 950 struct file_efihttp *fh; 951 bool done; 952 int polltime; 953 954 fh = (struct file_efihttp *)f->f_fsdata; 955 956 if (fh->size > 0 && fh->offset >= fh->size) { 957 if (resid != NULL) 958 *resid = size; 959 960 return 0; 961 } 962 963 dev = (struct devdesc *)f->f_devdata; 964 oh = (struct open_efihttp *)dev->d_opendata; 965 done = false; 966 status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify, 967 &done, &token.Event); 968 if (EFI_ERROR(status)) { 969 return (efi_status_to_errno(status)); 970 } 971 token.Status = EFI_NOT_READY; 972 token.Message = &message; 973 message.Data.Request = NULL; 974 message.HeaderCount = 0; 975 message.Headers = NULL; 976 message.BodyLength = size; 977 message.Body = buf; 978 status = oh->http->Response(oh->http, &token); 979 if (status == EFI_CONNECTION_FIN) { 980 if (resid) 981 *resid = size; 982 return (0); 983 } else if (EFI_ERROR(status)) { 984 BS->CloseEvent(token.Event); 985 return (efi_status_to_errno(status)); 986 } 987 polltime = 0; 988 while (!done && polltime < EFIHTTP_POLL_TIMEOUT) { 989 status = oh->http->Poll(oh->http); 990 if (EFI_ERROR(status)) 991 break; 992 993 if (!done) { 994 delay(100 * 1000); 995 polltime += 100; 996 } 997 } 998 BS->CloseEvent(token.Event); 999 if (token.Status == EFI_CONNECTION_FIN) { 1000 if (resid) 1001 *resid = size; 1002 return (0); 1003 } else if (EFI_ERROR(token.Status)) 1004 return (efi_status_to_errno(token.Status)); 1005 if (resid) 1006 *resid = size - message.BodyLength; 1007 fh->offset += message.BodyLength; 1008 return (0); 1009 } 1010 1011 static int 1012 efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 1013 { 1014 size_t res; 1015 int err = 0; 1016 1017 while (size > 0) { 1018 err = _efihttp_fs_read(f, buf, size, &res); 1019 if (err != 0 || res == size) 1020 goto end; 1021 buf += (size - res); 1022 size = res; 1023 } 1024 end: 1025 if (resid) 1026 *resid = size; 1027 return (err); 1028 } 1029 1030 static int 1031 efihttp_fs_write(struct open_file *f __unused, const void *buf __unused, 1032 size_t size __unused, size_t *resid __unused) 1033 { 1034 return (EIO); 1035 } 1036 1037 static off_t 1038 efihttp_fs_seek(struct open_file *f, off_t offset, int where) 1039 { 1040 struct file_efihttp *fh; 1041 char *path; 1042 void *buf; 1043 size_t res, res2; 1044 int err; 1045 1046 fh = (struct file_efihttp *)f->f_fsdata; 1047 if (where == SEEK_SET && fh->offset == offset) 1048 return (0); 1049 if (where == SEEK_SET && fh->offset < offset) { 1050 buf = malloc(1500); 1051 if (buf == NULL) 1052 return (ENOMEM); 1053 res = offset - fh->offset; 1054 while (res > 0) { 1055 err = _efihttp_fs_read(f, buf, min(1500, res), &res2); 1056 if (err != 0) { 1057 free(buf); 1058 return (err); 1059 } 1060 res -= min(1500, res) - res2; 1061 } 1062 free(buf); 1063 return (0); 1064 } else if (where == SEEK_SET) { 1065 path = fh->path; 1066 fh->path = NULL; 1067 efihttp_fs_close(f); 1068 /* 1069 * Work around a bug in the EFI HTTP implementation which 1070 * causes a crash if the http instance isn't torn down 1071 * between requests. 1072 * See https://bugzilla.tianocore.org/show_bug.cgi?id=1917 1073 */ 1074 efihttp_dev_close(f); 1075 efihttp_dev_open(f); 1076 err = efihttp_fs_open(path, f); 1077 free(path); 1078 if (err != 0) 1079 return (err); 1080 return efihttp_fs_seek(f, offset, where); 1081 } 1082 return (EIO); 1083 } 1084 1085 static int 1086 efihttp_fs_stat(struct open_file *f, struct stat *sb) 1087 { 1088 struct file_efihttp *fh; 1089 1090 fh = (struct file_efihttp *)f->f_fsdata; 1091 memset(sb, 0, sizeof(*sb)); 1092 sb->st_nlink = 1; 1093 sb->st_mode = 0777 | (fh->is_dir ? S_IFDIR : S_IFREG); 1094 sb->st_size = fh->size; 1095 return (0); 1096 } 1097 1098 static int 1099 efihttp_fs_readdir(struct open_file *f, struct dirent *d) 1100 { 1101 static char *dirbuf = NULL, *db2, *cursor; 1102 static int dirbuf_len = 0; 1103 char *end; 1104 struct file_efihttp *fh; 1105 1106 fh = (struct file_efihttp *)f->f_fsdata; 1107 if (dirbuf_len < fh->size) { 1108 db2 = realloc(dirbuf, fh->size); 1109 if (db2 == NULL) { 1110 free(dirbuf); 1111 return (ENOMEM); 1112 } else 1113 dirbuf = db2; 1114 1115 dirbuf_len = fh->size; 1116 } 1117 1118 if (fh->offset != fh->size) { 1119 efihttp_fs_seek(f, 0, SEEK_SET); 1120 efihttp_fs_read(f, dirbuf, dirbuf_len, NULL); 1121 cursor = dirbuf; 1122 } 1123 1124 cursor = strstr(cursor, "<a href=\""); 1125 if (cursor == NULL) 1126 return (ENOENT); 1127 cursor += 9; 1128 end = strchr(cursor, '"'); 1129 if (*(end - 1) == '/') { 1130 end--; 1131 d->d_type = DT_DIR; 1132 } else 1133 d->d_type = DT_REG; 1134 memcpy(d->d_name, cursor, end - cursor); 1135 d->d_name[end - cursor] = '\0'; 1136 1137 return (0); 1138 } 1139