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