]>
Commit | Line | Data |
---|---|---|
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 | |
39 | static UINTN | |
40 | ascii_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 | ||
63 | static UINTN | |
64 | convert_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 | ||
85 | static EFI_DEVICE_PATH *devpath; | |
86 | static EFI_MAC_ADDRESS mac_addr; | |
87 | static IPv4_DEVICE_PATH ip4_node; | |
88 | static IPv6_DEVICE_PATH ip6_node; | |
89 | static BOOLEAN is_ip6; | |
90 | static CHAR8 *uri; | |
91 | ||
92 | BOOLEAN | |
93 | find_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 | 172 | out: |
62f0afa2 | 173 | FreePool(unpacked); |
f892ac66 | 174 | return ret; |
62f0afa2 MTL |
175 | } |
176 | ||
177 | static EFI_STATUS | |
178 | generate_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 | ||
216 | static EFI_STATUS | |
217 | extract_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 | ||
247 | static EFI_HANDLE | |
248 | get_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 | ||
293 | out: | |
294 | if (unpacked) | |
295 | FreePool(unpacked); | |
296 | FreePool(buffer); | |
297 | ||
298 | return handle; | |
299 | } | |
300 | ||
301 | static BOOLEAN | |
f892ac66 | 302 | is_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 |
314 | static inline void |
315 | print_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 |
328 | static EFI_STATUS |
329 | set_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 |
368 | static BOOLEAN |
369 | is_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 | ||
381 | static inline void | |
382 | print_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 |
389 | static EFI_STATUS |
390 | set_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 | ||
429 | static VOID EFIAPI | |
430 | httpnotify (EFI_EVENT Event, VOID *Context) | |
431 | { | |
432 | *((BOOLEAN *) Context) = TRUE; | |
433 | } | |
434 | ||
435 | static EFI_STATUS | |
436 | configure_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 | ||
462 | static EFI_STATUS | |
463 | send_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 | ||
525 | error: | |
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 | ||
532 | no_event: | |
533 | if (Url) | |
534 | FreePool(Url); | |
535 | ||
f892ac66 | 536 | return efi_status; |
62f0afa2 MTL |
537 | } |
538 | ||
539 | static EFI_STATUS | |
f4173af1 | 540 | receive_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 | ||
660 | error: | |
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 | ||
667 | no_event: | |
f892ac66 | 668 | if (EFI_ERROR(efi_status) && *buffer) |
62f0afa2 MTL |
669 | FreePool(*buffer); |
670 | ||
f892ac66 | 671 | return efi_status; |
62f0afa2 MTL |
672 | } |
673 | ||
674 | static EFI_STATUS | |
675 | http_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 | ||
730 | error: | |
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 | ||
741 | EFI_STATUS | |
f4173af1 | 742 | httpboot_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 | ||
797 | error: | |
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 | } |