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