httpboot.c 20.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
/*
 * Copyright 2015 SUSE LINUX GmbH <glin@suse.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Significant portions of this code are derived from Tianocore
 * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel
 * Corporation.
 */

#include <efi.h>
#include <efilib.h>

37
#include "shim.h"
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

static UINTN
ascii_to_int (CONST CHAR8 *str)
{
    UINTN u;
    CHAR8 c;

    // skip preceeding white space
    while (*str && *str == ' ') {
        str += 1;
    }

    // convert digits
    u = 0;
    while ((c = *(str++))) {
        if (c >= '0' && c <= '9') {
            u = (u * 10) + c - '0';
        } else {
            break;
        }
    }

    return u;
}

static UINTN
convert_http_status_code (EFI_HTTP_STATUS_CODE status_code)
{
	if (status_code >= HTTP_STATUS_100_CONTINUE &&
	    status_code <  HTTP_STATUS_200_OK) {
		return (status_code - HTTP_STATUS_100_CONTINUE + 100);
	} else if (status_code >= HTTP_STATUS_200_OK &&
		   status_code <  HTTP_STATUS_300_MULTIPLE_CHIOCES) {
		return (status_code - HTTP_STATUS_200_OK + 200);
	} else if (status_code >= HTTP_STATUS_300_MULTIPLE_CHIOCES &&
		   status_code <  HTTP_STATUS_400_BAD_REQUEST) {
		return (status_code - HTTP_STATUS_300_MULTIPLE_CHIOCES + 300);
	} else if (status_code >= HTTP_STATUS_400_BAD_REQUEST &&
		   status_code <  HTTP_STATUS_500_INTERNAL_SERVER_ERROR) {
		return (status_code - HTTP_STATUS_400_BAD_REQUEST + 400);
	} else if (status_code >= HTTP_STATUS_500_INTERNAL_SERVER_ERROR) {
		return (status_code - HTTP_STATUS_500_INTERNAL_SERVER_ERROR + 500);
	}

	return 0;
}

static EFI_DEVICE_PATH *devpath;
static EFI_MAC_ADDRESS mac_addr;
static IPv4_DEVICE_PATH ip4_node;
static IPv6_DEVICE_PATH ip6_node;
static BOOLEAN is_ip6;
static CHAR8 *uri;

BOOLEAN
find_httpboot (EFI_HANDLE device)
{
	EFI_DEVICE_PATH *unpacked;
	EFI_DEVICE_PATH *Node;
	MAC_ADDR_DEVICE_PATH *MacNode;
	URI_DEVICE_PATH *UriNode;
	UINTN uri_size;
100 101
	BOOLEAN ip_found = FALSE;
	BOOLEAN ret = FALSE;
102

103
	if (uri) {
104
		FreePool(uri);
105 106
		uri = NULL;
	}
107 108 109 110 111 112 113 114 115 116 117 118 119 120

	devpath = DevicePathFromHandle(device);
	if (!devpath) {
		perror(L"Failed to get device path\n");
		return FALSE;
	}

	unpacked = UnpackDevicePath(devpath);
	if (!unpacked) {
		perror(L"Failed to unpack device path\n");
		return FALSE;
	}
	Node = unpacked;

121 122
	/* Traverse the device path to find IPv4()/.../Uri() or
	 * IPv6()/.../Uri() */
123 124 125 126 127
	while (!IsDevicePathEnd(Node)) {
		/* Save the MAC node so we can match the net card later */
		if (DevicePathType(Node) == MESSAGING_DEVICE_PATH &&
		    DevicePathSubType(Node) == MSG_MAC_ADDR_DP) {
			MacNode = (MAC_ADDR_DEVICE_PATH *)Node;
128 129 130 131 132 133 134
			CopyMem(&mac_addr, &MacNode->MacAddress,
				sizeof(EFI_MAC_ADDRESS));
		} else if (DevicePathType(Node) == MESSAGING_DEVICE_PATH &&
			   (DevicePathSubType(Node) == MSG_IPv4_DP ||
			    DevicePathSubType(Node) == MSG_IPv6_DP)) {
			/* Save the IP node so we can set up the connection */
			/* later */
135
			if (DevicePathSubType(Node) == MSG_IPv6_DP) {
136 137
				CopyMem(&ip6_node, Node,
					sizeof(IPv6_DEVICE_PATH));
138 139
				is_ip6 = TRUE;
			} else {
140 141
				CopyMem(&ip4_node, Node,
					sizeof(IPv4_DEVICE_PATH));
142 143 144
				is_ip6 = FALSE;
			}

145 146 147 148 149 150 151 152 153
			ip_found = TRUE;
		} else if (ip_found == TRUE &&
			   (DevicePathType(Node) == MESSAGING_DEVICE_PATH &&
			    DevicePathSubType(Node) == MSG_URI_DP)) {
			EFI_DEVICE_PATH *NextNode;

			/* Check if the URI node is the last node since the */
			/* RAMDISK node could be appended, and we don't need */
			/* to download the second stage loader in that case. */
154
			NextNode = NextDevicePathNode(Node);
155 156 157 158 159 160 161 162 163 164
			if (!IsDevicePathEnd(NextNode))
				goto out;

			/* Save the current URI */
			UriNode = (URI_DEVICE_PATH *)Node;
			uri_size = strlena(UriNode->Uri);
			uri = AllocatePool(uri_size + 1);
			if (!uri) {
				perror(L"Failed to allocate uri\n");
				goto out;
165
			}
166 167 168
			CopyMem(uri, UriNode->Uri, uri_size + 1);
			ret = TRUE;
			goto out;
169 170 171
		}
		Node = NextDevicePathNode(Node);
	}
172
out:
173
	FreePool(unpacked);
174
	return ret;
175 176 177 178 179 180 181 182 183 184 185 186 187 188
}

static EFI_STATUS
generate_next_uri (CONST CHAR8 *current_uri, CONST CHAR8 *next_loader,
		   CHAR8 **uri)
{
	CONST CHAR8 *ptr;
	UINTN next_len;
	UINTN path_len = 0;
	UINTN count = 0;

	if (strncmpa(current_uri, (CHAR8 *)"http://", 7) == 0) {
		ptr = current_uri + 7;
		count += 7;
189 190 191
	} else if (strncmpa(current_uri, (CHAR8 *)"https://", 8) == 0) {
		ptr = current_uri + 8;
		count += 8;
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
	} else {
		return EFI_INVALID_PARAMETER;
	}

	/* Extract the path */
	next_len = strlena(next_loader);
	while (*ptr != '\0') {
		count++;
		if (*ptr == '/')
			path_len = count;
		ptr++;
	}

	*uri = AllocatePool(sizeof(CHAR8) * (path_len + next_len + 1));
	if (!*uri)
		return EFI_OUT_OF_RESOURCES;

	CopyMem(*uri, current_uri, path_len);
	CopyMem(*uri + path_len, next_loader, next_len);
	(*uri)[path_len + next_len] = '\0';

	return EFI_SUCCESS;
}

static EFI_STATUS
extract_hostname (CONST CHAR8 *url, CHAR8 **hostname)
{
	CONST CHAR8 *ptr, *start;
	UINTN host_len = 0;

	if (strncmpa(url, (CHAR8 *)"http://", 7) == 0)
		start = url + 7;
224 225
	else if (strncmpa(url, (CHAR8 *)"https://", 8) == 0)
		start = url + 8;
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
	else
		return EFI_INVALID_PARAMETER;

	ptr = start;
	while (*ptr != '/' && *ptr != '\0') {
		host_len++;
		ptr++;
	}

	*hostname = AllocatePool(sizeof(CHAR8) * (host_len + 1));
	if (!*hostname)
		return EFI_OUT_OF_RESOURCES;

	CopyMem(*hostname, start, host_len);
	(*hostname)[host_len] = '\0';

	return EFI_SUCCESS;
}

#define SAME_MAC_ADDR(a, b) (!CompareMem(a, b, sizeof(EFI_MAC_ADDRESS)))

static EFI_HANDLE
get_nic_handle (EFI_MAC_ADDRESS *mac)
{
	EFI_DEVICE_PATH *unpacked = NULL;
	EFI_DEVICE_PATH *Node;
	EFI_DEVICE_PATH *temp_path = NULL;
	MAC_ADDR_DEVICE_PATH *MacNode;
	EFI_HANDLE handle = NULL;
	EFI_HANDLE *buffer;
	UINTN NoHandles;
	UINTN i;
258
	EFI_STATUS efi_status;
259 260 261

	/* Get the list of handles that support the HTTP service binding
	   protocol */
262 263 264 265
	efi_status = gBS->LocateHandleBuffer(ByProtocol,
					     &EFI_HTTP_BINDING_GUID,
					     NULL, &NoHandles, &buffer);
	if (EFI_ERROR(efi_status))
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
		return NULL;

	for (i = 0; i < NoHandles; i++) {
		temp_path = DevicePathFromHandle(buffer[i]);

		/* Match the MAC address */
		unpacked = UnpackDevicePath(temp_path);
		if (!unpacked) {
			perror(L"Failed to unpack device path\n");
			continue;
		}
		Node = unpacked;
		while (!IsDevicePathEnd(Node)) {
			if (DevicePathType(Node) == MESSAGING_DEVICE_PATH &&
			    DevicePathSubType(Node) == MSG_MAC_ADDR_DP) {
				MacNode = (MAC_ADDR_DEVICE_PATH *)Node;
				if (SAME_MAC_ADDR(mac, &MacNode->MacAddress)) {
					handle = buffer[i];
					goto out;
				}
			}
			Node = NextDevicePathNode(Node);
		}
		FreePool(unpacked);
		unpacked = NULL;
	}

out:
	if (unpacked)
		FreePool(unpacked);
	FreePool(buffer);

	return handle;
}

static BOOLEAN
302
is_unspecified_ip6addr (EFI_IPv6_ADDRESS ip6)
303 304 305 306 307 308 309 310 311 312 313
{
	UINT8 i;

	for (i = 0; i<16; i++) {
		if (ip6.Addr[i] != 0)
			return FALSE;
	}

	return TRUE;
}

314 315 316 317 318 319 320 321 322 323 324 325 326 327
static inline void
print_ip6_addr(EFI_IPv6_ADDRESS ip6addr)
{
	perror(L"%x:%x:%x:%x:%x:%x:%x:%x\n",
	       ip6addr.Addr[0]  << 8 | ip6addr.Addr[1],
	       ip6addr.Addr[2]  << 8 | ip6addr.Addr[3],
	       ip6addr.Addr[4]  << 8 | ip6addr.Addr[5],
	       ip6addr.Addr[6]  << 8 | ip6addr.Addr[7],
	       ip6addr.Addr[8]  << 8 | ip6addr.Addr[9],
	       ip6addr.Addr[10] << 8 | ip6addr.Addr[11],
	       ip6addr.Addr[12] << 8 | ip6addr.Addr[13],
	       ip6addr.Addr[14] << 8 | ip6addr.Addr[15]);
}

328 329 330 331 332 333
static EFI_STATUS
set_ip6(EFI_HANDLE *nic, IPv6_DEVICE_PATH *ip6node)
{
	EFI_IP6_CONFIG_PROTOCOL *ip6cfg;
	EFI_IP6_CONFIG_MANUAL_ADDRESS ip6;
	EFI_IPv6_ADDRESS gateway;
334
	EFI_STATUS efi_status;
335

336 337 338 339
	efi_status = gBS->HandleProtocol(nic, &EFI_IP6_CONFIG_GUID,
					 (VOID **)&ip6cfg);
	if (EFI_ERROR(efi_status))
		return efi_status;
340 341 342 343

	ip6.Address = ip6node->LocalIpAddress;
	ip6.PrefixLength = ip6node->PrefixLength;
	ip6.IsAnycast = FALSE;
344 345 346 347 348 349 350 351
	efi_status = ip6cfg->SetData(ip6cfg, Ip6ConfigDataTypeManualAddress,
				     sizeof(ip6), &ip6);
	if (EFI_ERROR(efi_status)) {
		perror(L"Failed to set IPv6 Address:\nIP: ");
		print_ip6_addr(ip6.Address);
		perror(L"Prefix Length: %u\n", ip6.PrefixLength);
		return efi_status;
	}
352 353

	gateway = ip6node->GatewayIpAddress;
354
	if (is_unspecified_ip6addr(gateway))
355 356
		return EFI_SUCCESS;

357 358 359 360 361 362 363
	efi_status = ip6cfg->SetData(ip6cfg, Ip6ConfigDataTypeGateway,
				     sizeof(gateway), &gateway);
	if (EFI_ERROR(efi_status)) {
		perror(L"Failed to set IPv6 Gateway:\nIP: ");
		print_ip6_addr(gateway);
		return efi_status;
	}
364 365 366 367

	return EFI_SUCCESS;
}

368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
static BOOLEAN
is_unspecified_ip4addr (EFI_IPv4_ADDRESS ip4)
{
	UINT8 i;

	for (i = 0; i<4; i++) {
		if (ip4.Addr[i] != 0)
			return FALSE;
	}

	return TRUE;
}

static inline void
print_ip4_addr(EFI_IPv4_ADDRESS ip4addr)
{
	perror(L"%u.%u.%u.%u\n",
	       ip4addr.Addr[0], ip4addr.Addr[1],
	       ip4addr.Addr[2], ip4addr.Addr[3]);
}

389 390 391 392 393 394
static EFI_STATUS
set_ip4(EFI_HANDLE *nic, IPv4_DEVICE_PATH *ip4node)
{
	EFI_IP4_CONFIG2_PROTOCOL *ip4cfg2;
	EFI_IP4_CONFIG2_MANUAL_ADDRESS ip4;
	EFI_IPv4_ADDRESS gateway;
395
	EFI_STATUS efi_status;
396

397 398 399 400
	efi_status = gBS->HandleProtocol(nic, &EFI_IP4_CONFIG2_GUID,
					 (VOID **)&ip4cfg2);
	if (EFI_ERROR(efi_status))
		return efi_status;
401 402 403

	ip4.Address = ip4node->LocalIpAddress;
	ip4.SubnetMask = ip4node->SubnetMask;
404 405 406 407 408 409 410 411 412
	efi_status = ip4cfg2->SetData(ip4cfg2, Ip4Config2DataTypeManualAddress,
				      sizeof(ip4), &ip4);
	if (EFI_ERROR(efi_status)) {
		perror(L"Failed to Set IPv4 Address:\nIP: ");
		print_ip4_addr(ip4.Address);
		perror(L"Mask: ");
		print_ip4_addr(ip4.SubnetMask);
		return efi_status;
	}
413 414

	gateway = ip4node->GatewayIpAddress;
415 416 417 418 419 420 421 422 423 424
	if (is_unspecified_ip4addr(gateway))
		return EFI_SUCCESS;

	efi_status = ip4cfg2->SetData(ip4cfg2, Ip4Config2DataTypeGateway,
				      sizeof(gateway), &gateway);
	if (EFI_ERROR(efi_status)) {
		perror(L"Failed to Set IPv4 Gateway:\nGateway: ");
		print_ip4_addr(gateway);
		return efi_status;
	}
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458

	return EFI_SUCCESS;
}

static VOID EFIAPI
httpnotify (EFI_EVENT Event, VOID *Context)
{
	*((BOOLEAN *) Context) = TRUE;
}

static EFI_STATUS
configure_http (EFI_HTTP_PROTOCOL *http, BOOLEAN is_ip6)
{
	EFI_HTTP_CONFIG_DATA http_mode;
	EFI_HTTPv4_ACCESS_POINT ip4node;
	EFI_HTTPv6_ACCESS_POINT ip6node;

	/* Configure HTTP */
	ZeroMem(&http_mode, sizeof(http_mode));
	http_mode.HttpVersion = HttpVersion11;
	/* use the default time out */
	http_mode.TimeOutMillisec = 0;

	if (!is_ip6) {
		http_mode.LocalAddressIsIPv6 = FALSE;
		ZeroMem(&ip4node, sizeof(ip4node));
		ip4node.UseDefaultAddress = TRUE;
		http_mode.AccessPoint.IPv4Node = &ip4node;
	} else {
		http_mode.LocalAddressIsIPv6 = TRUE;
		ZeroMem(&ip6node, sizeof(ip6node));
		http_mode.AccessPoint.IPv6Node = &ip6node;
	}

459
	return http->Configure(http, &http_mode);
460 461 462 463 464 465 466 467 468 469 470
}

static EFI_STATUS
send_http_request (EFI_HTTP_PROTOCOL *http, CHAR8 *hostname, CHAR8 *uri)
{
	EFI_HTTP_TOKEN tx_token;
	EFI_HTTP_MESSAGE tx_message;
	EFI_HTTP_REQUEST_DATA request;
	EFI_HTTP_HEADER headers[3];
	BOOLEAN request_done;
	CHAR16 *Url = NULL;
471
	EFI_STATUS efi_status;
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
	EFI_STATUS event_status;

	/* Convert the ascii string to the UCS2 string */
	Url = PoolPrint(L"%a", uri);
	if (!Url)
		return EFI_OUT_OF_RESOURCES;

	request.Method = HttpMethodGet;
	request.Url = Url;

	/* Prepare the HTTP headers */
	headers[0].FieldName = (CHAR8 *)"Host";
	headers[0].FieldValue = hostname;
	headers[1].FieldName = (CHAR8 *)"Accept";
	headers[1].FieldValue = (CHAR8 *)"*/*";
	headers[2].FieldName = (CHAR8 *)"User-Agent";
	headers[2].FieldValue = (CHAR8 *)"UefiHttpBoot/1.0";

	tx_message.Data.Request = &request;
	tx_message.HeaderCount = 3;
	tx_message.Headers = headers;
	tx_message.BodyLength = 0;
	tx_message.Body = NULL;

	tx_token.Status = EFI_NOT_READY;
	tx_token.Message = &tx_message;
	tx_token.Event = NULL;
	request_done = FALSE;
500 501 502 503 504 505
	efi_status = gBS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
				      httpnotify, &request_done,
				      &tx_token.Event);
	if (EFI_ERROR(efi_status)) {
		perror(L"Failed to Create Event for HTTP request: %r\n",
		       efi_status);
506 507 508 509
		goto no_event;
	}

	/* Send out the request */
510 511 512
	efi_status = http->Request(http, &tx_token);
	if (EFI_ERROR(efi_status)) {
		perror(L"HTTP request failed: %r\n", efi_status);
513 514 515 516 517
		goto error;
	}

	/* Wait for the response */
	while (!request_done)
518
		http->Poll(http);
519 520 521

	if (EFI_ERROR(tx_token.Status)) {
		perror(L"HTTP request: %r\n", tx_token.Status);
522
		efi_status = tx_token.Status;
523 524 525
	}

error:
526
	event_status = gBS->CloseEvent(tx_token.Event);
527 528 529 530 531 532 533 534 535
	if (EFI_ERROR(event_status)) {
		perror(L"Failed to close Event for HTTP request: %r\n",
		       event_status);
	}

no_event:
	if (Url)
		FreePool(Url);

536
	return efi_status;
537 538 539
}

static EFI_STATUS
540
receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size)
541 542 543 544 545 546 547 548
{
	EFI_HTTP_TOKEN rx_token;
	EFI_HTTP_MESSAGE rx_message;
	EFI_HTTP_RESPONSE_DATA response;
	EFI_HTTP_STATUS_CODE http_status;
	BOOLEAN response_done;
	UINTN i, downloaded;
	CHAR8 rx_buffer[9216];
549
	EFI_STATUS efi_status;
550 551 552 553 554 555 556 557 558 559 560 561 562 563
	EFI_STATUS event_status;

	/* Initialize the rx message and buffer */
	response.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS;
	rx_message.Data.Response = &response;
	rx_message.HeaderCount = 0;
	rx_message.Headers = 0;
	rx_message.BodyLength = sizeof(rx_buffer);
	rx_message.Body = rx_buffer;

	rx_token.Status = EFI_NOT_READY;
	rx_token.Message = &rx_message;
	rx_token.Event = NULL;
	response_done = FALSE;
564 565 566 567 568 569
	efi_status = gBS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
				      httpnotify, &response_done,
				      &rx_token.Event);
	if (EFI_ERROR(efi_status)) {
		perror(L"Failed to Create Event for HTTP response: %r\n",
		       efi_status);
570 571 572 573
		goto no_event;
	}

	/* Notify the firmware to receive the HTTP messages */
574 575 576
	efi_status = http->Response(http, &rx_token);
	if (EFI_ERROR(efi_status)) {
		perror(L"HTTP response failed: %r\n", efi_status);
577 578 579 580 581
		goto error;
	}

	/* Wait for the response */
	while (!response_done)
582
		http->Poll(http);
583 584 585

	if (EFI_ERROR(rx_token.Status)) {
		perror(L"HTTP response: %r\n", rx_token.Status);
586
		efi_status = rx_token.Status;
587 588 589 590 591 592 593 594
		goto error;
	}

	/* Check the HTTP status code */
	http_status = rx_token.Message->Data.Response->StatusCode;
	if (http_status != HTTP_STATUS_200_OK) {
		perror(L"HTTP Status Code: %d\n",
		       convert_http_status_code(http_status));
595
		efi_status = EFI_ABORTED;
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
		goto error;
	}

	/* Check the length of the file */
	for (i = 0; i < rx_message.HeaderCount; i++) {
		if (!strcmpa(rx_message.Headers[i].FieldName, (CHAR8 *)"Content-Length")) {
			*buf_size = ascii_to_int(rx_message.Headers[i].FieldValue);
		}
	}

	if (*buf_size == 0) {
		perror(L"Failed to get Content-Lenght\n");
		goto error;
	}

	*buffer = AllocatePool(*buf_size);
	if (!*buffer) {
		perror(L"Failed to allocate new rx buffer\n");
		goto error;
	}

	downloaded = rx_message.BodyLength;

	CopyMem(*buffer, rx_buffer, downloaded);

	/* Retreive the rest of the message */
	while (downloaded < *buf_size) {
		if (rx_message.Headers) {
			FreePool(rx_message.Headers);
		}
		rx_message.Headers = NULL;
		rx_message.HeaderCount = 0;
		rx_message.Data.Response = NULL;
		rx_message.BodyLength = sizeof(rx_buffer);
		rx_message.Body = rx_buffer;

		rx_token.Status = EFI_NOT_READY;
		response_done = FALSE;

635 636 637
		efi_status = http->Response(http, &rx_token);
		if (EFI_ERROR(efi_status)) {
			perror(L"HTTP response failed: %r\n", efi_status);
638 639 640 641
			goto error;
		}

		while (!response_done)
642
			http->Poll(http);
643 644 645

		if (EFI_ERROR(rx_token.Status)) {
			perror(L"HTTP response: %r\n", rx_token.Status);
646
			efi_status = rx_token.Status;
647 648 649 650
			goto error;
		}

		if (rx_message.BodyLength + downloaded > *buf_size) {
651
			efi_status = EFI_BAD_BUFFER_SIZE;
652 653 654 655 656 657 658 659 660
			goto error;
		}

		CopyMem(*buffer + downloaded, rx_buffer, rx_message.BodyLength);

		downloaded += rx_message.BodyLength;
	}

error:
661
	event_status = gBS->CloseEvent(rx_token.Event);
662 663 664 665 666 667
	if (EFI_ERROR(event_status)) {
		perror(L"Failed to close Event for HTTP response: %r\n",
		       event_status);
	}

no_event:
668
	if (EFI_ERROR(efi_status) && *buffer)
669 670
		FreePool(*buffer);

671
	return efi_status;
672 673 674 675 676
}

static EFI_STATUS
http_fetch (EFI_HANDLE image, EFI_HANDLE device,
	    CHAR8 *hostname, CHAR8 *uri, BOOLEAN is_ip6,
677
	    VOID **buffer, UINT64 *buf_size)
678 679 680 681
{
	EFI_SERVICE_BINDING *service;
	EFI_HANDLE http_handle;
	EFI_HTTP_PROTOCOL *http;
682
	EFI_STATUS efi_status;
683 684 685 686 687 688
	EFI_STATUS child_status;

	*buffer = NULL;
	*buf_size = 0;

	/* Open HTTP Service Binding Protocol */
689 690 691 692 693
	efi_status = gBS->OpenProtocol(device, &EFI_HTTP_BINDING_GUID,
				       (VOID **) &service, image, NULL,
				       EFI_OPEN_PROTOCOL_GET_PROTOCOL);
	if (EFI_ERROR(efi_status))
		return efi_status;
694 695 696 697

	/* Create the ChildHandle from the Service Binding */
	/* Set the handle to NULL to request a new handle */
	http_handle = NULL;
698 699 700 701 702
	efi_status = service->CreateChild(service, &http_handle);
	if (EFI_ERROR(efi_status)) {
		perror(L"Failed to create the ChildHandle\n");
		return efi_status;
	}
703 704

	/* Get the http protocol */
705 706 707
	efi_status = gBS->HandleProtocol(http_handle, &EFI_HTTP_PROTOCOL_GUID,
					 (VOID **) &http);
	if (EFI_ERROR(efi_status)) {
708 709 710 711
		perror(L"Failed to get http\n");
		goto error;
	}

712 713 714
	efi_status = configure_http(http, is_ip6);
	if (EFI_ERROR(efi_status)) {
		perror(L"Failed to configure http: %r\n", efi_status);
715 716 717
		goto error;
	}

718 719 720
	efi_status = send_http_request(http, hostname, uri);
	if (EFI_ERROR(efi_status)) {
		perror(L"Failed to send HTTP request: %r\n", efi_status);
721 722 723
		goto error;
	}

724 725 726
	efi_status = receive_http_response(http, buffer, buf_size);
	if (EFI_ERROR(efi_status)) {
		perror(L"Failed to receive HTTP response: %r\n", efi_status);
727 728 729 730
		goto error;
	}

error:
731 732 733
	child_status = service->DestroyChild(service, http_handle);
	if (EFI_ERROR(efi_status)) {
		return efi_status;
734 735 736 737 738 739 740 741
	} else if (EFI_ERROR(child_status)) {
		return child_status;
	}

	return EFI_SUCCESS;
}

EFI_STATUS
742
httpboot_fetch_buffer (EFI_HANDLE image, VOID **buffer, UINT64 *buf_size)
743
{
744
	EFI_STATUS efi_status;
745 746 747 748 749 750 751 752 753 754 755
	EFI_HANDLE nic;
	CHAR8 *next_loader = NULL;
	CHAR8 *next_uri = NULL;
	CHAR8 *hostname = NULL;

	if (!uri)
		return EFI_NOT_READY;

	next_loader = translate_slashes(DEFAULT_LOADER_CHAR);

	/* Create the URI for the next loader based on the original URI */
756 757 758
	efi_status = generate_next_uri(uri, next_loader, &next_uri);
	if (EFI_ERROR(efi_status)) {
		perror(L"Next URI: %a, %r\n", next_uri, efi_status);
759 760 761 762
		goto error;
	}

	/* Extract the hostname (or IP) from URI */
763 764 765
	efi_status = extract_hostname(uri, &hostname);
	if (EFI_ERROR(efi_status)) {
		perror(L"hostname: %a, %r\n", hostname, efi_status);
766 767 768 769 770 771 772
		goto error;
	}

	/* Get the handle that associates with the NIC we are using and
	   also supports the HTTP service binding protocol */
	nic = get_nic_handle(&mac_addr);
	if (!nic) {
773
		efi_status = EFI_NOT_FOUND;
774 775 776 777 778 779 780
		goto error;
	}

	/* UEFI stops DHCP after fetching the image and stores the related
	   information in the device path node. We have to set up the
	   connection on our own for the further operations. */
	if (!is_ip6)
781
		efi_status = set_ip4(nic, &ip4_node);
782
	else
783 784 785
		efi_status = set_ip6(nic, &ip6_node);
	if (EFI_ERROR(efi_status)) {
		perror(L"Failed to set IP for HTTPBoot: %r\n", efi_status);
786 787 788 789
		goto error;
	}

	/* Use HTTP protocl to fetch the remote file */
790 791 792 793
	efi_status = http_fetch (image, nic, hostname, next_uri, is_ip6,
				 buffer, buf_size);
	if (EFI_ERROR(efi_status)) {
		perror(L"Failed to fetch image: %r\n", efi_status);
794 795 796 797 798 799 800 801 802 803 804
		goto error;
	}

error:
	FreePool(uri);
	uri = NULL;
	if (next_uri)
		FreePool(next_uri);
	if (hostname)
		FreePool(hostname);

805
	return efi_status;
806
}