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