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
notify(EFI_EVENT event __unused,void * context)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
setup_ipv4_config2(EFI_HANDLE handle,MAC_ADDR_DEVICE_PATH * mac,IPv4_DEVICE_PATH * ipv4,DNS_DEVICE_PATH * dns)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
setup_ipv4_config2_static(EFI_HANDLE handle,struct in_addr ip,n_long mask,struct in_addr gw,struct in_addr dns)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
efi_ip4_dhcp(EFI_HANDLE handle,struct in_addr * ip,n_long * mask,struct in_addr * gw,struct in_addr * dns)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
efihttp_dev_init(void)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
efihttp_dev_strategy(void * devdata __unused,int rw __unused,daddr_t blk __unused,size_t size __unused,char * buf __unused,size_t * rsize __unused)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
efihttp_dev_open(struct open_file * f,...)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
efihttp_dev_open_legacy(struct open_file * f)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
efihttp_dev_open_url(struct open_file * f,struct http_devdesc * hd)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
efihttp_dev_close(struct open_file * f)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
efihttp_parsedev(struct devdesc ** idev,const char * devspec,const char ** path)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
_efihttp_fs_open(const char * path,struct open_file * f)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
efihttp_fs_open(const char * path,struct open_file * f)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
efihttp_fs_close(struct open_file * f __unused)937 efihttp_fs_close(struct open_file *f __unused)
938 {
939 return (0);
940 }
941
942 static int
_efihttp_fs_read(struct open_file * f,void * buf,size_t size,size_t * resid)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
efihttp_fs_read(struct open_file * f,void * buf,size_t size,size_t * resid)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
efihttp_fs_write(struct open_file * f __unused,const void * buf __unused,size_t size __unused,size_t * resid __unused)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
efihttp_fs_seek(struct open_file * f,off_t offset,int where)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
efihttp_fs_stat(struct open_file * f,struct stat * sb)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
efihttp_fs_readdir(struct open_file * f,struct dirent * d)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