]> git.proxmox.com Git - efi-boot-shim.git/blame - httpboot.c
Uninstall shim protocols before re-installing them
[efi-boot-shim.git] / httpboot.c
CommitLineData
3d79bcb2
GCPL
1/*
2 * Copyright 2015 SUSE LINUX GmbH <glin@suse.com>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * 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
14 * distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27 * OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Significant portions of this code are derived from Tianocore
30 * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel
31 * Corporation.
32 */
33
34#include <efi.h>
35#include <efilib.h>
36#include "str.h"
37#include "Http.h"
38#include "Ip4Config2.h"
39#include "Ip6Config.h"
40
3d79bcb2
GCPL
41#define perror(fmt, ...) ({ \
42 UINTN __perror_ret = 0; \
43 if (!in_protocol) \
44 __perror_ret = Print((fmt), ##__VA_ARGS__); \
45 __perror_ret; \
46 })
47
48static UINTN
49ascii_to_int (CONST CHAR8 *str)
50{
51 UINTN u;
52 CHAR8 c;
53
54 // skip preceeding white space
55 while (*str && *str == ' ') {
56 str += 1;
57 }
58
59 // convert digits
60 u = 0;
61 while ((c = *(str++))) {
62 if (c >= '0' && c <= '9') {
63 u = (u * 10) + c - '0';
64 } else {
65 break;
66 }
67 }
68
69 return u;
70}
71
72static UINTN
73convert_http_status_code (EFI_HTTP_STATUS_CODE status_code)
74{
75 if (status_code >= HTTP_STATUS_100_CONTINUE &&
76 status_code < HTTP_STATUS_200_OK) {
77 return (status_code - HTTP_STATUS_100_CONTINUE + 100);
78 } else if (status_code >= HTTP_STATUS_200_OK &&
79 status_code < HTTP_STATUS_300_MULTIPLE_CHIOCES) {
80 return (status_code - HTTP_STATUS_200_OK + 200);
81 } else if (status_code >= HTTP_STATUS_300_MULTIPLE_CHIOCES &&
82 status_code < HTTP_STATUS_400_BAD_REQUEST) {
83 return (status_code - HTTP_STATUS_300_MULTIPLE_CHIOCES + 300);
84 } else if (status_code >= HTTP_STATUS_400_BAD_REQUEST &&
85 status_code < HTTP_STATUS_500_INTERNAL_SERVER_ERROR) {
86 return (status_code - HTTP_STATUS_400_BAD_REQUEST + 400);
87 } else if (status_code >= HTTP_STATUS_500_INTERNAL_SERVER_ERROR) {
88 return (status_code - HTTP_STATUS_500_INTERNAL_SERVER_ERROR + 500);
89 }
90
91 return 0;
92}
93
94static EFI_DEVICE_PATH *devpath;
95static EFI_MAC_ADDRESS mac_addr;
96static IPv4_DEVICE_PATH ip4_node;
97static IPv6_DEVICE_PATH ip6_node;
98static BOOLEAN is_ip6;
99static CHAR8 *uri;
100
101BOOLEAN
102find_httpboot (EFI_HANDLE device)
103{
104 EFI_DEVICE_PATH *unpacked;
105 EFI_DEVICE_PATH *Node;
106 EFI_DEVICE_PATH *NextNode;
107 MAC_ADDR_DEVICE_PATH *MacNode;
108 URI_DEVICE_PATH *UriNode;
109 UINTN uri_size;
110
7d745e49 111 if (uri) {
3d79bcb2 112 FreePool(uri);
7d745e49
LZ
113 uri = NULL;
114 }
3d79bcb2
GCPL
115
116 devpath = DevicePathFromHandle(device);
117 if (!devpath) {
118 perror(L"Failed to get device path\n");
119 return FALSE;
120 }
121
122 unpacked = UnpackDevicePath(devpath);
123 if (!unpacked) {
124 perror(L"Failed to unpack device path\n");
125 return FALSE;
126 }
127 Node = unpacked;
128
129 /* Traverse the device path to find IPv4()/Uri() or IPv6()/Uri() */
130 while (!IsDevicePathEnd(Node)) {
131 /* Save the MAC node so we can match the net card later */
132 if (DevicePathType(Node) == MESSAGING_DEVICE_PATH &&
133 DevicePathSubType(Node) == MSG_MAC_ADDR_DP) {
134 MacNode = (MAC_ADDR_DEVICE_PATH *)Node;
135 CopyMem(&mac_addr, &MacNode->MacAddress, sizeof(EFI_MAC_ADDRESS));
136 }
137
138 if (DevicePathType(Node) == MESSAGING_DEVICE_PATH &&
139 (DevicePathSubType(Node) == MSG_IPv4_DP ||
140 DevicePathSubType(Node) == MSG_IPv6_DP)) {
141 /* Save the IP node so we can set up the connection later */
142 if (DevicePathSubType(Node) == MSG_IPv6_DP) {
143 CopyMem(&ip6_node, Node, sizeof(IPv6_DEVICE_PATH));
144 is_ip6 = TRUE;
145 } else {
146 CopyMem(&ip4_node, Node, sizeof(IPv4_DEVICE_PATH));
147 is_ip6 = FALSE;
148 }
149
150 Node = NextDevicePathNode(Node);
151 NextNode = NextDevicePathNode(Node);
152 if (DevicePathType(Node) == MESSAGING_DEVICE_PATH &&
153 DevicePathSubType(Node) == MSG_URI_DP &&
154 IsDevicePathEnd(NextNode)) {
155 /* Save the current URI */
156 UriNode = (URI_DEVICE_PATH *)Node;
157 uri_size = strlena(UriNode->Uri);
158 uri = AllocatePool(uri_size + 1);
159 if (!uri) {
160 perror(L"Failed to allocate uri\n");
161 return FALSE;
162 }
163 CopyMem(uri, UriNode->Uri, uri_size + 1);
164 FreePool(unpacked);
165 return TRUE;
166 }
167 }
168 Node = NextDevicePathNode(Node);
169 }
170
171 FreePool(unpacked);
172 return FALSE;
173}
174
175static EFI_STATUS
176generate_next_uri (CONST CHAR8 *current_uri, CONST CHAR8 *next_loader,
177 CHAR8 **uri)
178{
179 CONST CHAR8 *ptr;
180 UINTN next_len;
181 UINTN path_len = 0;
182 UINTN count = 0;
183
184 if (strncmpa(current_uri, (CHAR8 *)"http://", 7) == 0) {
185 ptr = current_uri + 7;
186 count += 7;
3ee08dde
GL
187 } else if (strncmpa(current_uri, (CHAR8 *)"https://", 8) == 0) {
188 ptr = current_uri + 8;
189 count += 8;
3d79bcb2
GCPL
190 } else {
191 return EFI_INVALID_PARAMETER;
192 }
193
194 /* Extract the path */
195 next_len = strlena(next_loader);
196 while (*ptr != '\0') {
197 count++;
198 if (*ptr == '/')
199 path_len = count;
200 ptr++;
201 }
202
203 *uri = AllocatePool(sizeof(CHAR8) * (path_len + next_len + 1));
204 if (!*uri)
205 return EFI_OUT_OF_RESOURCES;
206
207 CopyMem(*uri, current_uri, path_len);
208 CopyMem(*uri + path_len, next_loader, next_len);
209 (*uri)[path_len + next_len] = '\0';
210
211 return EFI_SUCCESS;
212}
213
214static EFI_STATUS
215extract_hostname (CONST CHAR8 *url, CHAR8 **hostname)
216{
217 CONST CHAR8 *ptr, *start;
218 UINTN host_len = 0;
219
220 if (strncmpa(url, (CHAR8 *)"http://", 7) == 0)
221 start = url + 7;
3ee08dde
GL
222 else if (strncmpa(url, (CHAR8 *)"https://", 8) == 0)
223 start = url + 8;
3d79bcb2
GCPL
224 else
225 return EFI_INVALID_PARAMETER;
226
227 ptr = start;
228 while (*ptr != '/' && *ptr != '\0') {
229 host_len++;
230 ptr++;
231 }
232
233 *hostname = AllocatePool(sizeof(CHAR8) * (host_len + 1));
234 if (!*hostname)
235 return EFI_OUT_OF_RESOURCES;
236
237 CopyMem(*hostname, start, host_len);
238 (*hostname)[host_len] = '\0';
239
240 return EFI_SUCCESS;
241}
242
243#define SAME_MAC_ADDR(a, b) (!CompareMem(a, b, sizeof(EFI_MAC_ADDRESS)))
244
245static EFI_HANDLE
246get_nic_handle (EFI_MAC_ADDRESS *mac)
247{
248 EFI_GUID http_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
249 EFI_DEVICE_PATH *unpacked = NULL;
250 EFI_DEVICE_PATH *Node;
251 EFI_DEVICE_PATH *temp_path = NULL;
252 MAC_ADDR_DEVICE_PATH *MacNode;
253 EFI_HANDLE handle = NULL;
254 EFI_HANDLE *buffer;
255 UINTN NoHandles;
256 UINTN i;
257 EFI_STATUS status;
258
259 /* Get the list of handles that support the HTTP service binding
260 protocol */
261 status = uefi_call_wrapper(BS->LocateHandleBuffer, 5,
262 ByProtocol,
263 &http_binding_guid,
264 NULL,
265 &NoHandles,
266 &buffer);
267 if (EFI_ERROR(status))
268 return NULL;
269
270 for (i = 0; i < NoHandles; i++) {
271 temp_path = DevicePathFromHandle(buffer[i]);
272
273 /* Match the MAC address */
274 unpacked = UnpackDevicePath(temp_path);
275 if (!unpacked) {
276 perror(L"Failed to unpack device path\n");
277 continue;
278 }
279 Node = unpacked;
280 while (!IsDevicePathEnd(Node)) {
281 if (DevicePathType(Node) == MESSAGING_DEVICE_PATH &&
282 DevicePathSubType(Node) == MSG_MAC_ADDR_DP) {
283 MacNode = (MAC_ADDR_DEVICE_PATH *)Node;
284 if (SAME_MAC_ADDR(mac, &MacNode->MacAddress)) {
285 handle = buffer[i];
286 goto out;
287 }
288 }
289 Node = NextDevicePathNode(Node);
290 }
291 FreePool(unpacked);
292 unpacked = NULL;
293 }
294
295out:
296 if (unpacked)
297 FreePool(unpacked);
298 FreePool(buffer);
299
300 return handle;
301}
302
303static BOOLEAN
304is_unspecified_addr (EFI_IPv6_ADDRESS ip6)
305{
306 UINT8 i;
307
308 for (i = 0; i<16; i++) {
309 if (ip6.Addr[i] != 0)
310 return FALSE;
311 }
312
313 return TRUE;
314}
315
316static EFI_STATUS
317set_ip6(EFI_HANDLE *nic, IPv6_DEVICE_PATH *ip6node)
318{
319 EFI_GUID ip6_config_guid = EFI_IP6_CONFIG_PROTOCOL_GUID;
320 EFI_IP6_CONFIG_PROTOCOL *ip6cfg;
321 EFI_IP6_CONFIG_MANUAL_ADDRESS ip6;
322 EFI_IPv6_ADDRESS gateway;
323 EFI_STATUS status;
324
325 status = uefi_call_wrapper(BS->HandleProtocol, 3,
326 nic,
327 &ip6_config_guid,
328 (VOID **)&ip6cfg);
329 if (EFI_ERROR (status))
330 return status;
331
332 ip6.Address = ip6node->LocalIpAddress;
333 ip6.PrefixLength = ip6node->PrefixLength;
334 ip6.IsAnycast = FALSE;
335 status = uefi_call_wrapper(ip6cfg->SetData, 4,
336 ip6cfg,
337 Ip6ConfigDataTypeManualAddress,
338 sizeof(ip6),
339 &ip6);
340 if (EFI_ERROR (status))
341 return status;
342
343 gateway = ip6node->GatewayIpAddress;
344 if (is_unspecified_addr(gateway))
345 return EFI_SUCCESS;
346
347 status = uefi_call_wrapper(ip6cfg->SetData, 4,
348 ip6cfg,
349 Ip6ConfigDataTypeGateway,
350 sizeof(gateway),
351 &gateway);
352 if (EFI_ERROR (status))
353 return status;
354
355 return EFI_SUCCESS;
356}
357
358static EFI_STATUS
359set_ip4(EFI_HANDLE *nic, IPv4_DEVICE_PATH *ip4node)
360{
361 EFI_GUID ip4_config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID;
362 EFI_IP4_CONFIG2_PROTOCOL *ip4cfg2;
363 EFI_IP4_CONFIG2_MANUAL_ADDRESS ip4;
364 EFI_IPv4_ADDRESS gateway;
365 EFI_STATUS status;
366
367 status = uefi_call_wrapper(BS->HandleProtocol, 3,
368 nic,
369 &ip4_config2_guid,
370 (VOID **)&ip4cfg2);
371 if (EFI_ERROR (status))
372 return status;
373
374 ip4.Address = ip4node->LocalIpAddress;
375 ip4.SubnetMask = ip4node->SubnetMask;
376 status = uefi_call_wrapper(ip4cfg2->SetData, 4,
377 ip4cfg2,
378 Ip4Config2DataTypeManualAddress,
379 sizeof(ip4),
380 &ip4);
381 if (EFI_ERROR (status))
382 return status;
383
384 gateway = ip4node->GatewayIpAddress;
385 status = uefi_call_wrapper(ip4cfg2->SetData, 4,
386 ip4cfg2,
387 Ip4Config2DataTypeGateway,
388 sizeof(gateway),
389 &gateway);
390 if (EFI_ERROR (status))
391 return status;
392
393 return EFI_SUCCESS;
394}
395
396static VOID EFIAPI
397httpnotify (EFI_EVENT Event, VOID *Context)
398{
399 *((BOOLEAN *) Context) = TRUE;
400}
401
402static EFI_STATUS
403configure_http (EFI_HTTP_PROTOCOL *http, BOOLEAN is_ip6)
404{
405 EFI_HTTP_CONFIG_DATA http_mode;
406 EFI_HTTPv4_ACCESS_POINT ip4node;
407 EFI_HTTPv6_ACCESS_POINT ip6node;
408
409 /* Configure HTTP */
410 ZeroMem(&http_mode, sizeof(http_mode));
411 http_mode.HttpVersion = HttpVersion11;
412 /* use the default time out */
413 http_mode.TimeOutMillisec = 0;
414
415 if (!is_ip6) {
416 http_mode.LocalAddressIsIPv6 = FALSE;
417 ZeroMem(&ip4node, sizeof(ip4node));
418 ip4node.UseDefaultAddress = TRUE;
419 http_mode.AccessPoint.IPv4Node = &ip4node;
420 } else {
421 http_mode.LocalAddressIsIPv6 = TRUE;
422 ZeroMem(&ip6node, sizeof(ip6node));
423 http_mode.AccessPoint.IPv6Node = &ip6node;
424 }
425
426 return uefi_call_wrapper(http->Configure, 2, http, &http_mode);
427}
428
429static EFI_STATUS
430send_http_request (EFI_HTTP_PROTOCOL *http, CHAR8 *hostname, CHAR8 *uri)
431{
432 EFI_HTTP_TOKEN tx_token;
433 EFI_HTTP_MESSAGE tx_message;
434 EFI_HTTP_REQUEST_DATA request;
435 EFI_HTTP_HEADER headers[3];
436 BOOLEAN request_done;
437 CHAR16 *Url = NULL;
438 EFI_STATUS status;
439 EFI_STATUS event_status;
440
441 /* Convert the ascii string to the UCS2 string */
442 Url = PoolPrint(L"%a", uri);
443 if (!Url)
444 return EFI_OUT_OF_RESOURCES;
445
446 request.Method = HttpMethodGet;
447 request.Url = Url;
448
449 /* Prepare the HTTP headers */
450 headers[0].FieldName = (CHAR8 *)"Host";
451 headers[0].FieldValue = hostname;
452 headers[1].FieldName = (CHAR8 *)"Accept";
453 headers[1].FieldValue = (CHAR8 *)"*/*";
454 headers[2].FieldName = (CHAR8 *)"User-Agent";
455 headers[2].FieldValue = (CHAR8 *)"UefiHttpBoot/1.0";
456
457 tx_message.Data.Request = &request;
458 tx_message.HeaderCount = 3;
459 tx_message.Headers = headers;
460 tx_message.BodyLength = 0;
461 tx_message.Body = NULL;
462
463 tx_token.Status = EFI_NOT_READY;
464 tx_token.Message = &tx_message;
465 tx_token.Event = NULL;
466 request_done = FALSE;
467 status = uefi_call_wrapper(BS->CreateEvent, 5,
468 EVT_NOTIFY_SIGNAL,
469 TPL_NOTIFY,
470 httpnotify,
471 &request_done,
472 &tx_token.Event);
473 if (EFI_ERROR(status)) {
474 perror(L"Failed to Create Event for HTTP request: %r\n", status);
475 goto no_event;
476 }
477
478 /* Send out the request */
479 status = uefi_call_wrapper(http->Request, 2, http, &tx_token);
480 if (EFI_ERROR(status)) {
481 perror(L"HTTP request failed: %r\n", status);
482 goto error;
483 }
484
485 /* Wait for the response */
486 while (!request_done)
487 uefi_call_wrapper(http->Poll, 1, http);
488
489 if (EFI_ERROR(tx_token.Status)) {
490 perror(L"HTTP request: %r\n", tx_token.Status);
491 status = tx_token.Status;
492 }
493
494error:
495 event_status = uefi_call_wrapper(BS->CloseEvent, 1, tx_token.Event);
496 if (EFI_ERROR(event_status)) {
497 perror(L"Failed to close Event for HTTP request: %r\n",
498 event_status);
499 }
500
501no_event:
502 if (Url)
503 FreePool(Url);
504
505 return status;
506}
507
508static EFI_STATUS
d00ea555 509receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size)
3d79bcb2
GCPL
510{
511 EFI_HTTP_TOKEN rx_token;
512 EFI_HTTP_MESSAGE rx_message;
513 EFI_HTTP_RESPONSE_DATA response;
514 EFI_HTTP_STATUS_CODE http_status;
515 BOOLEAN response_done;
516 UINTN i, downloaded;
517 CHAR8 rx_buffer[9216];
518 EFI_STATUS status;
519 EFI_STATUS event_status;
520
521 /* Initialize the rx message and buffer */
522 response.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS;
523 rx_message.Data.Response = &response;
524 rx_message.HeaderCount = 0;
525 rx_message.Headers = 0;
526 rx_message.BodyLength = sizeof(rx_buffer);
527 rx_message.Body = rx_buffer;
528
529 rx_token.Status = EFI_NOT_READY;
530 rx_token.Message = &rx_message;
531 rx_token.Event = NULL;
532 response_done = FALSE;
533 status = uefi_call_wrapper(BS->CreateEvent, 5,
534 EVT_NOTIFY_SIGNAL,
535 TPL_NOTIFY,
536 httpnotify,
537 &response_done,
538 &rx_token.Event);
539 if (EFI_ERROR(status)) {
540 perror(L"Failed to Create Event for HTTP response: %r\n", status);
541 goto no_event;
542 }
543
544 /* Notify the firmware to receive the HTTP messages */
545 status = uefi_call_wrapper(http->Response, 2, http, &rx_token);
546 if (EFI_ERROR(status)) {
547 perror(L"HTTP response failed: %r\n", status);
548 goto error;
549 }
550
551 /* Wait for the response */
552 while (!response_done)
553 uefi_call_wrapper(http->Poll, 1, http);
554
555 if (EFI_ERROR(rx_token.Status)) {
556 perror(L"HTTP response: %r\n", rx_token.Status);
557 status = rx_token.Status;
558 goto error;
559 }
560
561 /* Check the HTTP status code */
562 http_status = rx_token.Message->Data.Response->StatusCode;
563 if (http_status != HTTP_STATUS_200_OK) {
564 perror(L"HTTP Status Code: %d\n",
565 convert_http_status_code(http_status));
566 status = EFI_ABORTED;
567 goto error;
568 }
569
570 /* Check the length of the file */
571 for (i = 0; i < rx_message.HeaderCount; i++) {
572 if (!strcmpa(rx_message.Headers[i].FieldName, (CHAR8 *)"Content-Length")) {
573 *buf_size = ascii_to_int(rx_message.Headers[i].FieldValue);
574 }
575 }
576
577 if (*buf_size == 0) {
578 perror(L"Failed to get Content-Lenght\n");
579 goto error;
580 }
581
582 *buffer = AllocatePool(*buf_size);
583 if (!*buffer) {
584 perror(L"Failed to allocate new rx buffer\n");
585 goto error;
586 }
587
588 downloaded = rx_message.BodyLength;
589
590 CopyMem(*buffer, rx_buffer, downloaded);
591
592 /* Retreive the rest of the message */
593 while (downloaded < *buf_size) {
594 if (rx_message.Headers) {
595 FreePool(rx_message.Headers);
596 }
597 rx_message.Headers = NULL;
598 rx_message.HeaderCount = 0;
599 rx_message.Data.Response = NULL;
600 rx_message.BodyLength = sizeof(rx_buffer);
601 rx_message.Body = rx_buffer;
602
603 rx_token.Status = EFI_NOT_READY;
604 response_done = FALSE;
605
606 status = uefi_call_wrapper(http->Response, 2, http, &rx_token);
607 if (EFI_ERROR(status)) {
608 perror(L"HTTP response failed: %r\n", status);
609 goto error;
610 }
611
612 while (!response_done)
613 uefi_call_wrapper(http->Poll, 1, http);
614
615 if (EFI_ERROR(rx_token.Status)) {
616 perror(L"HTTP response: %r\n", rx_token.Status);
617 status = rx_token.Status;
618 goto error;
619 }
620
621 if (rx_message.BodyLength + downloaded > *buf_size) {
622 status = EFI_BAD_BUFFER_SIZE;
623 goto error;
624 }
625
626 CopyMem(*buffer + downloaded, rx_buffer, rx_message.BodyLength);
627
628 downloaded += rx_message.BodyLength;
629 }
630
631error:
632 event_status = uefi_call_wrapper(BS->CloseEvent, 1, rx_token.Event);
633 if (EFI_ERROR(event_status)) {
634 perror(L"Failed to close Event for HTTP response: %r\n",
635 event_status);
636 }
637
638no_event:
639 if (EFI_ERROR(status) && *buffer)
640 FreePool(*buffer);
641
642 return status;
643}
644
645static EFI_STATUS
646http_fetch (EFI_HANDLE image, EFI_HANDLE device,
647 CHAR8 *hostname, CHAR8 *uri, BOOLEAN is_ip6,
d00ea555 648 VOID **buffer, UINT64 *buf_size)
3d79bcb2
GCPL
649{
650 EFI_GUID http_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
651 EFI_GUID http_protocol_guid = EFI_HTTP_PROTOCOL_GUID;
652 EFI_SERVICE_BINDING *service;
653 EFI_HANDLE http_handle;
654 EFI_HTTP_PROTOCOL *http;
655 EFI_STATUS status;
656 EFI_STATUS child_status;
657
658 *buffer = NULL;
659 *buf_size = 0;
660
661 /* Open HTTP Service Binding Protocol */
662 status = uefi_call_wrapper(BS->OpenProtocol, 6, device,
663 &http_binding_guid, (VOID **)&service,
664 image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
665 if (EFI_ERROR (status))
666 return status;
667
668 /* Create the ChildHandle from the Service Binding */
669 /* Set the handle to NULL to request a new handle */
670 http_handle = NULL;
671 status = uefi_call_wrapper(service->CreateChild, 2, service,
672 &http_handle);
673 if (EFI_ERROR (status))
674 return status;
675
676 /* Get the http protocol */
677 status = uefi_call_wrapper(BS->HandleProtocol, 3, http_handle,
678 &http_protocol_guid, (VOID **)&http);
679 if (EFI_ERROR (status)) {
680 perror(L"Failed to get http\n");
681 goto error;
682 }
683
684 status = configure_http(http, is_ip6);
685 if (EFI_ERROR (status)) {
686 perror(L"Failed to configure http: %r\n", status);
687 goto error;
688 }
689
690 status = send_http_request(http, hostname, uri);
691 if (EFI_ERROR(status)) {
692 perror(L"Failed to send HTTP request: %r\n", status);
693 goto error;
694 }
695
696 status = receive_http_response(http, buffer, buf_size);
697 if (EFI_ERROR(status)) {
698 perror(L"Failed to receive HTTP response: %r\n", status);
699 goto error;
700 }
701
702error:
703 child_status = uefi_call_wrapper(service->DestroyChild, 2, service,
704 http_handle);
705
706 if (EFI_ERROR(status)) {
707 return status;
708 } else if (EFI_ERROR(child_status)) {
709 return child_status;
710 }
711
712 return EFI_SUCCESS;
713}
714
715EFI_STATUS
83c62ff5 716httpboot_fetch_buffer (EFI_HANDLE image, VOID **buffer, UINT64 *buf_size)
3d79bcb2
GCPL
717{
718 EFI_STATUS status;
719 EFI_HANDLE nic;
720 CHAR8 *next_loader = NULL;
721 CHAR8 *next_uri = NULL;
722 CHAR8 *hostname = NULL;
723
724 if (!uri)
725 return EFI_NOT_READY;
726
727 next_loader = translate_slashes(DEFAULT_LOADER_CHAR);
728
729 /* Create the URI for the next loader based on the original URI */
730 status = generate_next_uri(uri, next_loader, &next_uri);
731 if (EFI_ERROR (status)) {
732 perror(L"Next URI: %a, %r\n", next_uri, status);
733 goto error;
734 }
735
736 /* Extract the hostname (or IP) from URI */
737 status = extract_hostname(uri, &hostname);
738 if (EFI_ERROR (status)) {
739 perror(L"hostname: %a, %r\n", hostname, status);
740 goto error;
741 }
742
743 /* Get the handle that associates with the NIC we are using and
744 also supports the HTTP service binding protocol */
745 nic = get_nic_handle(&mac_addr);
746 if (!nic) {
747 goto error;
748 }
749
750 /* UEFI stops DHCP after fetching the image and stores the related
751 information in the device path node. We have to set up the
752 connection on our own for the further operations. */
753 if (!is_ip6)
754 status = set_ip4(nic, &ip4_node);
755 else
756 status = set_ip6(nic, &ip6_node);
757 if (EFI_ERROR (status)) {
758 perror(L"Failed to set IP for HTTPBoot: %r\n", status);
759 goto error;
760 }
761
762 /* Use HTTP protocl to fetch the remote file */
763 status = http_fetch (image, nic, hostname, next_uri, is_ip6,
764 buffer, buf_size);
765 if (EFI_ERROR (status)) {
766 perror(L"Failed to fetch image: %r\n", status);
767 goto error;
768 }
769
770error:
771 FreePool(uri);
772 uri = NULL;
773 if (next_uri)
774 FreePool(next_uri);
775 if (hostname)
776 FreePool(hostname);
777
778 return status;
779}