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