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