]> git.proxmox.com Git - efi-boot-shim.git/blame - httpboot.c
dbx: generate our own UUID
[efi-boot-shim.git] / httpboot.c
CommitLineData
031e5cce
SM
1// SPDX-License-Identifier: BSD-2-Clause-Patent
2
62f0afa2
MTL
3/*
4 * Copyright 2015 SUSE LINUX GmbH <glin@suse.com>
5 *
62f0afa2
MTL
6 * Significant portions of this code are derived from Tianocore
7 * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel
8 * Corporation.
9 */
f892ac66 10#include "shim.h"
62f0afa2
MTL
11
12static UINTN
13ascii_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
36static UINTN
37convert_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
58static EFI_DEVICE_PATH *devpath;
59static EFI_MAC_ADDRESS mac_addr;
60static IPv4_DEVICE_PATH ip4_node;
61static IPv6_DEVICE_PATH ip6_node;
62static BOOLEAN is_ip6;
63static CHAR8 *uri;
64
65BOOLEAN
66find_httpboot (EFI_HANDLE device)
67{
68 EFI_DEVICE_PATH *unpacked;
69 EFI_DEVICE_PATH *Node;
62f0afa2
MTL
70 MAC_ADDR_DEVICE_PATH *MacNode;
71 URI_DEVICE_PATH *UriNode;
72 UINTN uri_size;
f892ac66
MTL
73 BOOLEAN ip_found = FALSE;
74 BOOLEAN ret = FALSE;
62f0afa2 75
f4173af1 76 if (uri) {
62f0afa2 77 FreePool(uri);
f4173af1
MTL
78 uri = NULL;
79 }
62f0afa2
MTL
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
f892ac66
MTL
94 /* Traverse the device path to find IPv4()/.../Uri() or
95 * IPv6()/.../Uri() */
62f0afa2
MTL
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;
f892ac66
MTL
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 */
62f0afa2 108 if (DevicePathSubType(Node) == MSG_IPv6_DP) {
f892ac66
MTL
109 CopyMem(&ip6_node, Node,
110 sizeof(IPv6_DEVICE_PATH));
62f0afa2
MTL
111 is_ip6 = TRUE;
112 } else {
f892ac66
MTL
113 CopyMem(&ip4_node, Node,
114 sizeof(IPv4_DEVICE_PATH));
62f0afa2
MTL
115 is_ip6 = FALSE;
116 }
117
f892ac66
MTL
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. */
62f0afa2 127 NextNode = NextDevicePathNode(Node);
f892ac66
MTL
128 if (!IsDevicePathEnd(NextNode))
129 goto out;
130
131 /* Save the current URI */
132 UriNode = (URI_DEVICE_PATH *)Node;
031e5cce 133 uri_size = strlen(UriNode->Uri);
f892ac66
MTL
134 uri = AllocatePool(uri_size + 1);
135 if (!uri) {
136 perror(L"Failed to allocate uri\n");
137 goto out;
62f0afa2 138 }
f892ac66
MTL
139 CopyMem(uri, UriNode->Uri, uri_size + 1);
140 ret = TRUE;
141 goto out;
62f0afa2
MTL
142 }
143 Node = NextDevicePathNode(Node);
144 }
f892ac66 145out:
62f0afa2 146 FreePool(unpacked);
f892ac66 147 return ret;
62f0afa2
MTL
148}
149
150static EFI_STATUS
151generate_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
031e5cce 159 if (strncmp(current_uri, (CHAR8 *)"http://", 7) == 0) {
62f0afa2
MTL
160 ptr = current_uri + 7;
161 count += 7;
031e5cce 162 } else if (strncmp(current_uri, (CHAR8 *)"https://", 8) == 0) {
f4173af1
MTL
163 ptr = current_uri + 8;
164 count += 8;
62f0afa2
MTL
165 } else {
166 return EFI_INVALID_PARAMETER;
167 }
168
169 /* Extract the path */
031e5cce 170 next_len = strlen(next_loader);
62f0afa2
MTL
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
8529e0f7
SM
182 CopyMem(*uri, (void *)current_uri, path_len);
183 CopyMem(*uri + path_len, (void *)next_loader, next_len);
62f0afa2
MTL
184 (*uri)[path_len + next_len] = '\0';
185
186 return EFI_SUCCESS;
187}
188
189static EFI_STATUS
190extract_hostname (CONST CHAR8 *url, CHAR8 **hostname)
191{
192 CONST CHAR8 *ptr, *start;
193 UINTN host_len = 0;
194
031e5cce 195 if (strncmp(url, (CHAR8 *)"http://", 7) == 0)
62f0afa2 196 start = url + 7;
031e5cce 197 else if (strncmp(url, (CHAR8 *)"https://", 8) == 0)
f4173af1 198 start = url + 8;
62f0afa2
MTL
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
8529e0f7 212 CopyMem(*hostname, (void *)start, host_len);
62f0afa2
MTL
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
220static EFI_HANDLE
221get_nic_handle (EFI_MAC_ADDRESS *mac)
222{
62f0afa2
MTL
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;
f892ac66 231 EFI_STATUS efi_status;
62f0afa2
MTL
232
233 /* Get the list of handles that support the HTTP service binding
234 protocol */
8529e0f7
SM
235 efi_status = BS->LocateHandleBuffer(ByProtocol,
236 &EFI_HTTP_BINDING_GUID,
237 NULL, &NoHandles, &buffer);
f892ac66 238 if (EFI_ERROR(efi_status))
62f0afa2
MTL
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
266out:
267 if (unpacked)
268 FreePool(unpacked);
269 FreePool(buffer);
270
271 return handle;
272}
273
274static BOOLEAN
f892ac66 275is_unspecified_ip6addr (EFI_IPv6_ADDRESS ip6)
62f0afa2
MTL
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
f892ac66
MTL
287static inline void
288print_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
62f0afa2
MTL
301static EFI_STATUS
302set_ip6(EFI_HANDLE *nic, IPv6_DEVICE_PATH *ip6node)
303{
62f0afa2
MTL
304 EFI_IP6_CONFIG_PROTOCOL *ip6cfg;
305 EFI_IP6_CONFIG_MANUAL_ADDRESS ip6;
306 EFI_IPv6_ADDRESS gateway;
f892ac66 307 EFI_STATUS efi_status;
62f0afa2 308
8529e0f7
SM
309 efi_status = BS->HandleProtocol(nic, &EFI_IP6_CONFIG_GUID,
310 (VOID **)&ip6cfg);
f892ac66
MTL
311 if (EFI_ERROR(efi_status))
312 return efi_status;
62f0afa2
MTL
313
314 ip6.Address = ip6node->LocalIpAddress;
315 ip6.PrefixLength = ip6node->PrefixLength;
316 ip6.IsAnycast = FALSE;
f892ac66
MTL
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 }
62f0afa2
MTL
325
326 gateway = ip6node->GatewayIpAddress;
f892ac66 327 if (is_unspecified_ip6addr(gateway))
62f0afa2
MTL
328 return EFI_SUCCESS;
329
f892ac66
MTL
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 }
62f0afa2
MTL
337
338 return EFI_SUCCESS;
339}
340
f892ac66
MTL
341static BOOLEAN
342is_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
354static inline void
355print_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
62f0afa2
MTL
362static EFI_STATUS
363set_ip4(EFI_HANDLE *nic, IPv4_DEVICE_PATH *ip4node)
364{
62f0afa2
MTL
365 EFI_IP4_CONFIG2_PROTOCOL *ip4cfg2;
366 EFI_IP4_CONFIG2_MANUAL_ADDRESS ip4;
367 EFI_IPv4_ADDRESS gateway;
f892ac66 368 EFI_STATUS efi_status;
62f0afa2 369
8529e0f7
SM
370 efi_status = BS->HandleProtocol(nic, &EFI_IP4_CONFIG2_GUID,
371 (VOID **)&ip4cfg2);
f892ac66
MTL
372 if (EFI_ERROR(efi_status))
373 return efi_status;
62f0afa2
MTL
374
375 ip4.Address = ip4node->LocalIpAddress;
376 ip4.SubnetMask = ip4node->SubnetMask;
f892ac66
MTL
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 }
62f0afa2
MTL
386
387 gateway = ip4node->GatewayIpAddress;
f892ac66
MTL
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 }
62f0afa2
MTL
398
399 return EFI_SUCCESS;
400}
401
402static VOID EFIAPI
031e5cce 403httpnotify (EFI_EVENT Event UNUSED, VOID *Context)
62f0afa2
MTL
404{
405 *((BOOLEAN *) Context) = TRUE;
406}
407
408static EFI_STATUS
409configure_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
f892ac66 432 return http->Configure(http, &http_mode);
62f0afa2
MTL
433}
434
435static EFI_STATUS
436send_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;
f892ac66 444 EFI_STATUS efi_status;
62f0afa2
MTL
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;
8529e0f7
SM
473 efi_status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
474 httpnotify, &request_done,
475 &tx_token.Event);
f892ac66
MTL
476 if (EFI_ERROR(efi_status)) {
477 perror(L"Failed to Create Event for HTTP request: %r\n",
478 efi_status);
62f0afa2
MTL
479 goto no_event;
480 }
481
482 /* Send out the request */
f892ac66
MTL
483 efi_status = http->Request(http, &tx_token);
484 if (EFI_ERROR(efi_status)) {
485 perror(L"HTTP request failed: %r\n", efi_status);
62f0afa2
MTL
486 goto error;
487 }
488
489 /* Wait for the response */
490 while (!request_done)
f892ac66 491 http->Poll(http);
62f0afa2
MTL
492
493 if (EFI_ERROR(tx_token.Status)) {
494 perror(L"HTTP request: %r\n", tx_token.Status);
f892ac66 495 efi_status = tx_token.Status;
62f0afa2
MTL
496 }
497
498error:
8529e0f7 499 event_status = BS->CloseEvent(tx_token.Event);
62f0afa2
MTL
500 if (EFI_ERROR(event_status)) {
501 perror(L"Failed to close Event for HTTP request: %r\n",
502 event_status);
503 }
504
505no_event:
506 if (Url)
507 FreePool(Url);
508
f892ac66 509 return efi_status;
62f0afa2
MTL
510}
511
512static EFI_STATUS
f4173af1 513receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size)
62f0afa2
MTL
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];
f892ac66 522 EFI_STATUS efi_status;
62f0afa2
MTL
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;
8529e0f7
SM
537 efi_status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
538 httpnotify, &response_done,
539 &rx_token.Event);
f892ac66
MTL
540 if (EFI_ERROR(efi_status)) {
541 perror(L"Failed to Create Event for HTTP response: %r\n",
542 efi_status);
62f0afa2
MTL
543 goto no_event;
544 }
545
546 /* Notify the firmware to receive the HTTP messages */
f892ac66
MTL
547 efi_status = http->Response(http, &rx_token);
548 if (EFI_ERROR(efi_status)) {
549 perror(L"HTTP response failed: %r\n", efi_status);
62f0afa2
MTL
550 goto error;
551 }
552
553 /* Wait for the response */
554 while (!response_done)
f892ac66 555 http->Poll(http);
62f0afa2
MTL
556
557 if (EFI_ERROR(rx_token.Status)) {
558 perror(L"HTTP response: %r\n", rx_token.Status);
f892ac66 559 efi_status = rx_token.Status;
62f0afa2
MTL
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));
f892ac66 568 efi_status = EFI_ABORTED;
62f0afa2
MTL
569 goto error;
570 }
571
572 /* Check the length of the file */
573 for (i = 0; i < rx_message.HeaderCount; i++) {
8529e0f7
SM
574 if (!strcasecmp(rx_message.Headers[i].FieldName,
575 (CHAR8 *)"Content-Length")) {
62f0afa2
MTL
576 *buf_size = ascii_to_int(rx_message.Headers[i].FieldValue);
577 }
578 }
579
580 if (*buf_size == 0) {
fd2d9f03
SM
581 perror(L"Failed to get Content-Length\n");
582 goto error;
583 }
584
585 if (*buf_size < rx_message.BodyLength) {
586 efi_status = EFI_BAD_BUFFER_SIZE;
587 perror(L"Invalid Content-Length\n");
62f0afa2
MTL
588 goto error;
589 }
590
591 *buffer = AllocatePool(*buf_size);
592 if (!*buffer) {
593 perror(L"Failed to allocate new rx buffer\n");
594 goto error;
595 }
596
597 downloaded = rx_message.BodyLength;
598
599 CopyMem(*buffer, rx_buffer, downloaded);
600
601 /* Retreive the rest of the message */
602 while (downloaded < *buf_size) {
603 if (rx_message.Headers) {
604 FreePool(rx_message.Headers);
605 }
606 rx_message.Headers = NULL;
607 rx_message.HeaderCount = 0;
608 rx_message.Data.Response = NULL;
609 rx_message.BodyLength = sizeof(rx_buffer);
610 rx_message.Body = rx_buffer;
611
612 rx_token.Status = EFI_NOT_READY;
613 response_done = FALSE;
614
f892ac66
MTL
615 efi_status = http->Response(http, &rx_token);
616 if (EFI_ERROR(efi_status)) {
617 perror(L"HTTP response failed: %r\n", efi_status);
62f0afa2
MTL
618 goto error;
619 }
620
621 while (!response_done)
f892ac66 622 http->Poll(http);
62f0afa2
MTL
623
624 if (EFI_ERROR(rx_token.Status)) {
625 perror(L"HTTP response: %r\n", rx_token.Status);
f892ac66 626 efi_status = rx_token.Status;
62f0afa2
MTL
627 goto error;
628 }
629
630 if (rx_message.BodyLength + downloaded > *buf_size) {
f892ac66 631 efi_status = EFI_BAD_BUFFER_SIZE;
62f0afa2
MTL
632 goto error;
633 }
634
635 CopyMem(*buffer + downloaded, rx_buffer, rx_message.BodyLength);
636
637 downloaded += rx_message.BodyLength;
638 }
639
640error:
8529e0f7 641 event_status = BS->CloseEvent(rx_token.Event);
62f0afa2
MTL
642 if (EFI_ERROR(event_status)) {
643 perror(L"Failed to close Event for HTTP response: %r\n",
644 event_status);
645 }
646
647no_event:
f892ac66 648 if (EFI_ERROR(efi_status) && *buffer)
62f0afa2
MTL
649 FreePool(*buffer);
650
f892ac66 651 return efi_status;
62f0afa2
MTL
652}
653
654static EFI_STATUS
655http_fetch (EFI_HANDLE image, EFI_HANDLE device,
656 CHAR8 *hostname, CHAR8 *uri, BOOLEAN is_ip6,
f4173af1 657 VOID **buffer, UINT64 *buf_size)
62f0afa2 658{
62f0afa2
MTL
659 EFI_SERVICE_BINDING *service;
660 EFI_HANDLE http_handle;
661 EFI_HTTP_PROTOCOL *http;
f892ac66 662 EFI_STATUS efi_status;
62f0afa2
MTL
663 EFI_STATUS child_status;
664
665 *buffer = NULL;
666 *buf_size = 0;
667
668 /* Open HTTP Service Binding Protocol */
8529e0f7
SM
669 efi_status = BS->OpenProtocol(device, &EFI_HTTP_BINDING_GUID,
670 (VOID **) &service, image, NULL,
671 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
f892ac66
MTL
672 if (EFI_ERROR(efi_status))
673 return efi_status;
62f0afa2
MTL
674
675 /* Create the ChildHandle from the Service Binding */
676 /* Set the handle to NULL to request a new handle */
677 http_handle = NULL;
f892ac66
MTL
678 efi_status = service->CreateChild(service, &http_handle);
679 if (EFI_ERROR(efi_status)) {
680 perror(L"Failed to create the ChildHandle\n");
681 return efi_status;
682 }
62f0afa2
MTL
683
684 /* Get the http protocol */
8529e0f7
SM
685 efi_status = BS->HandleProtocol(http_handle, &EFI_HTTP_PROTOCOL_GUID,
686 (VOID **) &http);
f892ac66 687 if (EFI_ERROR(efi_status)) {
62f0afa2
MTL
688 perror(L"Failed to get http\n");
689 goto error;
690 }
691
f892ac66
MTL
692 efi_status = configure_http(http, is_ip6);
693 if (EFI_ERROR(efi_status)) {
694 perror(L"Failed to configure http: %r\n", efi_status);
62f0afa2
MTL
695 goto error;
696 }
697
f892ac66
MTL
698 efi_status = send_http_request(http, hostname, uri);
699 if (EFI_ERROR(efi_status)) {
700 perror(L"Failed to send HTTP request: %r\n", efi_status);
62f0afa2
MTL
701 goto error;
702 }
703
f892ac66
MTL
704 efi_status = receive_http_response(http, buffer, buf_size);
705 if (EFI_ERROR(efi_status)) {
706 perror(L"Failed to receive HTTP response: %r\n", efi_status);
62f0afa2
MTL
707 goto error;
708 }
709
710error:
f892ac66
MTL
711 child_status = service->DestroyChild(service, http_handle);
712 if (EFI_ERROR(efi_status)) {
713 return efi_status;
62f0afa2
MTL
714 } else if (EFI_ERROR(child_status)) {
715 return child_status;
716 }
717
718 return EFI_SUCCESS;
719}
720
721EFI_STATUS
fd2d9f03
SM
722httpboot_fetch_buffer (EFI_HANDLE image, VOID **buffer, UINT64 *buf_size,
723 CHAR8 *name)
62f0afa2 724{
f892ac66 725 EFI_STATUS efi_status;
62f0afa2 726 EFI_HANDLE nic;
fd2d9f03 727 CHAR8 *next_loader;
62f0afa2
MTL
728 CHAR8 *next_uri = NULL;
729 CHAR8 *hostname = NULL;
730
731 if (!uri)
732 return EFI_NOT_READY;
733
fd2d9f03
SM
734 next_loader = (CHAR8 *)AllocatePool((strlen(name) + 1) * sizeof (CHAR8));
735 translate_slashes(next_loader, name);
62f0afa2
MTL
736
737 /* Create the URI for the next loader based on the original URI */
f892ac66
MTL
738 efi_status = generate_next_uri(uri, next_loader, &next_uri);
739 if (EFI_ERROR(efi_status)) {
740 perror(L"Next URI: %a, %r\n", next_uri, efi_status);
62f0afa2
MTL
741 goto error;
742 }
743
744 /* Extract the hostname (or IP) from URI */
f892ac66
MTL
745 efi_status = extract_hostname(uri, &hostname);
746 if (EFI_ERROR(efi_status)) {
747 perror(L"hostname: %a, %r\n", hostname, efi_status);
62f0afa2
MTL
748 goto error;
749 }
750
751 /* Get the handle that associates with the NIC we are using and
752 also supports the HTTP service binding protocol */
753 nic = get_nic_handle(&mac_addr);
754 if (!nic) {
f892ac66 755 efi_status = EFI_NOT_FOUND;
62f0afa2
MTL
756 goto error;
757 }
758
759 /* UEFI stops DHCP after fetching the image and stores the related
760 information in the device path node. We have to set up the
761 connection on our own for the further operations. */
762 if (!is_ip6)
f892ac66 763 efi_status = set_ip4(nic, &ip4_node);
62f0afa2 764 else
f892ac66
MTL
765 efi_status = set_ip6(nic, &ip6_node);
766 if (EFI_ERROR(efi_status)) {
767 perror(L"Failed to set IP for HTTPBoot: %r\n", efi_status);
62f0afa2
MTL
768 goto error;
769 }
770
771 /* Use HTTP protocl to fetch the remote file */
f892ac66
MTL
772 efi_status = http_fetch (image, nic, hostname, next_uri, is_ip6,
773 buffer, buf_size);
774 if (EFI_ERROR(efi_status)) {
775 perror(L"Failed to fetch image: %r\n", efi_status);
62f0afa2
MTL
776 goto error;
777 }
778
779error:
780 FreePool(uri);
781 uri = NULL;
782 if (next_uri)
783 FreePool(next_uri);
784 if (hostname)
785 FreePool(hostname);
786
f892ac66 787 return efi_status;
62f0afa2 788}