]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/HttpBootDxe/HttpBootSupport.c
NetworkPkg: Update cache management in HTTP boot driver.
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootSupport.c
CommitLineData
d933e70a
JW
1/** @file\r
2 Support functions implementation for UEFI HTTP boot driver.\r
3\r
4Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>\r
5This program and the accompanying materials are licensed and made available under \r
6the terms and conditions of the BSD License that accompanies this distribution. \r
7The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php. \r
9 \r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "HttpBootDxe.h"\r
16\r
17\r
18/**\r
19 Get the Nic handle using any child handle in the IPv4 stack.\r
20\r
21 @param[in] ControllerHandle Pointer to child handle over IPv4.\r
22\r
23 @return NicHandle The pointer to the Nic handle.\r
24 @return NULL Can't find the Nic handle.\r
25\r
26**/\r
27EFI_HANDLE\r
28HttpBootGetNicByIp4Children (\r
29 IN EFI_HANDLE ControllerHandle\r
30 )\r
31{\r
32 EFI_HANDLE NicHandle;\r
33\r
34 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid);\r
35 if (NicHandle == NULL) {\r
36 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);\r
37 if (NicHandle == NULL) {\r
38 return NULL;\r
39 }\r
40 }\r
41\r
42 return NicHandle;\r
43}\r
44\r
45\r
46/**\r
47 This function is to convert UINTN to ASCII string with the required formatting.\r
48\r
49 @param[in] Number Numeric value to be converted.\r
50 @param[in] Buffer The pointer to the buffer for ASCII string.\r
51 @param[in] Length The length of the required format.\r
52\r
53**/\r
54VOID\r
55HttpBootUintnToAscDecWithFormat (\r
56 IN UINTN Number,\r
57 IN UINT8 *Buffer,\r
58 IN INTN Length\r
59 )\r
60{\r
61 UINTN Remainder;\r
62\r
63 while (Length > 0) {\r
64 Length--;\r
65 Remainder = Number % 10;\r
66 Number /= 10;\r
67 Buffer[Length] = (UINT8) ('0' + Remainder);\r
68 }\r
69}\r
70\r
71/**\r
72 This function is to display the IPv4 address.\r
73\r
74 @param[in] Ip The pointer to the IPv4 address.\r
75\r
76**/\r
77VOID\r
78HttpBootShowIp4Addr (\r
79 IN EFI_IPv4_ADDRESS *Ip\r
80 )\r
81{\r
82 UINTN Index;\r
83\r
84 for (Index = 0; Index < 4; Index++) {\r
85 AsciiPrint ("%d", Ip->Addr[Index]);\r
86 if (Index < 3) {\r
87 AsciiPrint (".");\r
88 }\r
89 }\r
90}\r
91\r
92/**\r
93 Create a HTTP_IO_HEADER to hold the HTTP header items.\r
94\r
95 @param[in] MaxHeaderCount The maximun number of HTTP header in this holder.\r
96\r
97 @return A pointer of the HTTP header holder or NULL if failed.\r
98 \r
99**/\r
100HTTP_IO_HEADER *\r
101HttpBootCreateHeader (\r
102 UINTN MaxHeaderCount\r
103)\r
104{\r
105 HTTP_IO_HEADER *HttpIoHeader;\r
106\r
107 if (MaxHeaderCount == 0) {\r
108 return NULL;\r
109 }\r
110\r
111 HttpIoHeader = AllocateZeroPool (sizeof (HTTP_IO_HEADER) + MaxHeaderCount * sizeof (EFI_HTTP_HEADER));\r
112 if (HttpIoHeader == NULL) {\r
113 return NULL;\r
114 }\r
115\r
116 HttpIoHeader->MaxHeaderCount = MaxHeaderCount;\r
117 HttpIoHeader->Headers = (EFI_HTTP_HEADER *) (HttpIoHeader + 1);\r
118\r
119 return HttpIoHeader;\r
120}\r
121\r
122/**\r
123 Destroy the HTTP_IO_HEADER and release the resouces. \r
124\r
125 @param[in] HttpIoHeader Point to the HTTP header holder to be destroyed.\r
126\r
127**/\r
128VOID\r
129HttpBootFreeHeader (\r
130 IN HTTP_IO_HEADER *HttpIoHeader\r
131 )\r
132{\r
133 UINTN Index;\r
134 \r
135 if (HttpIoHeader != NULL) {\r
136 if (HttpIoHeader->HeaderCount != 0) {\r
137 for (Index = 0; Index < HttpIoHeader->HeaderCount; Index++) {\r
138 FreePool (HttpIoHeader->Headers[Index].FieldName);\r
139 FreePool (HttpIoHeader->Headers[Index].FieldValue);\r
140 }\r
141 }\r
142 FreePool (HttpIoHeader);\r
143 }\r
144}\r
145\r
146/**\r
147 Find a specified header field according to the field name.\r
148\r
149 @param[in] HeaderCount Number of HTTP header structures in Headers list. \r
150 @param[in] Headers Array containing list of HTTP headers.\r
151 @param[in] FieldName Null terminated string which describes a field name. \r
152\r
153 @return Pointer to the found header or NULL.\r
154\r
155**/\r
156EFI_HTTP_HEADER *\r
157HttpBootFindHeader (\r
158 IN UINTN HeaderCount,\r
159 IN EFI_HTTP_HEADER *Headers,\r
160 IN CHAR8 *FieldName\r
161 )\r
162{\r
163 UINTN Index;\r
164 \r
165 if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) {\r
166 return NULL;\r
167 }\r
168\r
169 for (Index = 0; Index < HeaderCount; Index++){\r
170 //\r
171 // Field names are case-insensitive (RFC 2616).\r
172 //\r
173 if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {\r
174 return &Headers[Index];\r
175 }\r
176 }\r
177 return NULL;\r
178}\r
179\r
180/**\r
181 Set or update a HTTP header with the field name and corresponding value.\r
182\r
183 @param[in] HttpIoHeader Point to the HTTP header holder.\r
184 @param[in] FieldName Null terminated string which describes a field name. \r
185 @param[in] FieldValue Null terminated string which describes the corresponding field value.\r
186\r
187 @retval EFI_SUCCESS The HTTP header has been set or updated.\r
188 @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
189 @retval EFI_OUT_OF_RESOURCES Insufficient resource to complete the operation.\r
190 @retval Other Unexpected error happened.\r
191 \r
192**/\r
193EFI_STATUS\r
194HttpBootSetHeader (\r
195 IN HTTP_IO_HEADER *HttpIoHeader,\r
196 IN CHAR8 *FieldName,\r
197 IN CHAR8 *FieldValue\r
198 )\r
199{\r
200 EFI_HTTP_HEADER *Header;\r
201 UINTN StrSize;\r
202 CHAR8 *NewFieldValue;\r
203 \r
204 if (HttpIoHeader == NULL || FieldName == NULL || FieldValue == NULL) {\r
205 return EFI_INVALID_PARAMETER;\r
206 }\r
207\r
208 Header = HttpBootFindHeader (HttpIoHeader->HeaderCount, HttpIoHeader->Headers, FieldName);\r
209 if (Header == NULL) {\r
210 //\r
211 // Add a new header.\r
212 //\r
213 if (HttpIoHeader->HeaderCount >= HttpIoHeader->MaxHeaderCount) {\r
214 return EFI_OUT_OF_RESOURCES;\r
215 }\r
216 Header = &HttpIoHeader->Headers[HttpIoHeader->HeaderCount];\r
217\r
218 StrSize = AsciiStrSize (FieldName);\r
219 Header->FieldName = AllocatePool (StrSize);\r
220 if (Header->FieldName == NULL) {\r
221 return EFI_OUT_OF_RESOURCES;\r
222 }\r
223 CopyMem (Header->FieldName, FieldName, StrSize);\r
224 Header->FieldName[StrSize -1] = '\0';\r
225\r
226 StrSize = AsciiStrSize (FieldValue);\r
227 Header->FieldValue = AllocatePool (StrSize);\r
228 if (Header->FieldValue == NULL) {\r
229 FreePool (Header->FieldName);\r
230 return EFI_OUT_OF_RESOURCES;\r
231 }\r
232 CopyMem (Header->FieldValue, FieldValue, StrSize);\r
233 Header->FieldValue[StrSize -1] = '\0';\r
234\r
235 HttpIoHeader->HeaderCount++;\r
236 } else {\r
237 //\r
238 // Update an existing one.\r
239 //\r
240 StrSize = AsciiStrSize (FieldValue);\r
241 NewFieldValue = AllocatePool (StrSize);\r
242 if (NewFieldValue == NULL) {\r
243 return EFI_OUT_OF_RESOURCES;\r
244 }\r
245 CopyMem (NewFieldValue, FieldValue, StrSize);\r
246 NewFieldValue[StrSize -1] = '\0';\r
247 \r
248 if (Header->FieldValue != NULL) {\r
249 FreePool (Header->FieldValue);\r
250 }\r
251 Header->FieldValue = NewFieldValue;\r
252 }\r
253\r
254 return EFI_SUCCESS;\r
255}\r
256\r
257/**\r
258 Notify the callback function when an event is triggered.\r
259\r
260 @param[in] Event The triggered event.\r
261 @param[in] Context The opaque parameter to the function.\r
262\r
263**/\r
264VOID\r
265EFIAPI\r
266HttpIoCommonNotify (\r
267 IN EFI_EVENT Event,\r
268 IN VOID *Context\r
269 )\r
270{\r
271 *((BOOLEAN *) Context) = TRUE;\r
272}\r
273\r
274/**\r
275 Create a HTTP_IO to access the HTTP service. It will create and configure\r
276 a HTTP child handle.\r
277\r
278 @param[in] Image The handle of the driver image.\r
279 @param[in] Controller The handle of the controller.\r
280 @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.\r
281 @param[in] ConfigData The HTTP_IO configuration data.\r
282 @param[out] HttpIo The HTTP_IO.\r
283 \r
284 @retval EFI_SUCCESS The HTTP_IO is created and configured.\r
285 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
286 @retval EFI_UNSUPPORTED One or more of the control options are not\r
287 supported in the implementation.\r
288 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
289 @retval Others Failed to create the HTTP_IO or configure it.\r
290\r
291**/\r
292EFI_STATUS\r
293HttpIoCreateIo (\r
294 IN EFI_HANDLE Image,\r
295 IN EFI_HANDLE Controller,\r
296 IN UINT8 IpVersion,\r
297 IN HTTP_IO_CONFIG_DATA *ConfigData,\r
298 OUT HTTP_IO *HttpIo\r
299 )\r
300{\r
301 EFI_STATUS Status;\r
302 EFI_HTTP_CONFIG_DATA HttpConfigData;\r
303 EFI_HTTPv4_ACCESS_POINT Http4AccessPoint;\r
304 EFI_HTTP_PROTOCOL *Http;\r
305 EFI_EVENT Event;\r
306 \r
307 if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (HttpIo == NULL)) {\r
308 return EFI_INVALID_PARAMETER;\r
309 }\r
310\r
311 if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) {\r
312 return EFI_UNSUPPORTED;\r
313 }\r
314\r
315 ZeroMem (HttpIo, sizeof (HTTP_IO));\r
316 \r
317 //\r
318 // Create the HTTP child instance and get the HTTP protocol.\r
319 // \r
320 Status = NetLibCreateServiceChild (\r
321 Controller,\r
322 Image,\r
323 &gEfiHttpServiceBindingProtocolGuid,\r
324 &HttpIo->Handle\r
325 );\r
326 if (EFI_ERROR (Status)) {\r
327 return Status;\r
328 }\r
329\r
330 Status = gBS->OpenProtocol (\r
331 HttpIo->Handle,\r
332 &gEfiHttpProtocolGuid,\r
333 (VOID **) &Http,\r
334 Image,\r
335 Controller,\r
336 EFI_OPEN_PROTOCOL_BY_DRIVER\r
337 );\r
338 if (EFI_ERROR (Status) || (Http == NULL)) {\r
339 goto ON_ERROR;\r
340 }\r
341\r
342 //\r
343 // Init the configuration data and configure the HTTP child.\r
344 //\r
345 HttpIo->Image = Image;\r
346 HttpIo->Controller = Controller;\r
347 HttpIo->IpVersion = IpVersion;\r
348 HttpIo->Http = Http;\r
349\r
350 ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA));\r
351 HttpConfigData.HttpVersion = HttpVersion11;\r
352 HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut;\r
353 if (HttpIo->IpVersion == IP_VERSION_4) {\r
354 HttpConfigData.LocalAddressIsIPv6 = FALSE;\r
355 \r
356 Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress;\r
357 Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort;\r
358 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp);\r
359 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask);\r
360 HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint; \r
361 } else {\r
362 ASSERT (FALSE);\r
363 }\r
364 \r
365 Status = Http->Configure (Http, &HttpConfigData);\r
366 if (EFI_ERROR (Status)) {\r
367 goto ON_ERROR;\r
368 }\r
369\r
370 //\r
371 // Create events for variuos asynchronous operations.\r
372 //\r
373 Status = gBS->CreateEvent (\r
374 EVT_NOTIFY_SIGNAL,\r
375 TPL_NOTIFY,\r
376 HttpIoCommonNotify,\r
377 &HttpIo->IsTxDone,\r
378 &Event\r
379 );\r
380 if (EFI_ERROR (Status)) {\r
381 goto ON_ERROR;\r
382 }\r
383 HttpIo->ReqToken.Event = Event;\r
384 HttpIo->ReqToken.Message = &HttpIo->ReqMessage;\r
385\r
386 Status = gBS->CreateEvent (\r
387 EVT_NOTIFY_SIGNAL,\r
388 TPL_NOTIFY,\r
389 HttpIoCommonNotify,\r
390 &HttpIo->IsRxDone,\r
391 &Event\r
392 );\r
393 if (EFI_ERROR (Status)) {\r
394 goto ON_ERROR;\r
395 }\r
396 HttpIo->RspToken.Event = Event;\r
397 HttpIo->RspToken.Message = &HttpIo->RspMessage;\r
398\r
399 return EFI_SUCCESS;\r
400 \r
401ON_ERROR:\r
402 HttpIoDestroyIo (HttpIo);\r
403\r
404 return Status;\r
405}\r
406\r
407/**\r
408 Destroy the HTTP_IO and release the resouces. \r
409\r
410 @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed.\r
411\r
412**/\r
413VOID\r
414HttpIoDestroyIo (\r
415 IN HTTP_IO *HttpIo\r
416 )\r
417{\r
418 EFI_HTTP_PROTOCOL *Http;\r
419 EFI_EVENT Event;\r
420\r
421 if (HttpIo == NULL) {\r
422 return;\r
423 }\r
424\r
425 Event = HttpIo->ReqToken.Event;\r
426 if (Event != NULL) {\r
427 gBS->CloseEvent (Event);\r
428 }\r
429\r
430 Event = HttpIo->RspToken.Event;\r
431 if (Event != NULL) {\r
432 gBS->CloseEvent (Event);\r
433 }\r
434 \r
435 Http = HttpIo->Http;\r
436 if (Http != NULL) {\r
437 Http->Configure (Http, NULL);\r
438 gBS->CloseProtocol (\r
439 HttpIo->Handle,\r
440 &gEfiHttpProtocolGuid,\r
441 HttpIo->Image,\r
442 HttpIo->Controller\r
443 );\r
444 }\r
445\r
446 NetLibDestroyServiceChild (\r
447 HttpIo->Controller,\r
448 HttpIo->Image,\r
449 &gEfiHttpServiceBindingProtocolGuid,\r
450 HttpIo->Handle\r
451 );\r
452}\r
453\r
454/**\r
455 Synchronously send a HTTP REQUEST message to the server.\r
456 \r
457 @param[in] HttpIo The HttpIo wrapping the HTTP service.\r
458 @param[in] Request A pointer to storage such data as URL and HTTP method.\r
459 @param[in] HeaderCount Number of HTTP header structures in Headers list. \r
460 @param[in] Headers Array containing list of HTTP headers.\r
461 @param[in] BodyLength Length in bytes of the HTTP body.\r
462 @param[in] Body Body associated with the HTTP request. \r
463 \r
464 @retval EFI_SUCCESS The HTTP request is trasmitted.\r
465 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
466 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
467 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.\r
468 @retval Others Other errors as indicated.\r
469\r
470**/\r
471EFI_STATUS\r
472HttpIoSendRequest (\r
473 IN HTTP_IO *HttpIo,\r
474 IN EFI_HTTP_REQUEST_DATA *Request,\r
475 IN UINTN HeaderCount,\r
476 IN EFI_HTTP_HEADER *Headers,\r
477 IN UINTN BodyLength,\r
478 IN VOID *Body\r
479 )\r
480{\r
481 EFI_STATUS Status;\r
482 EFI_HTTP_PROTOCOL *Http;\r
483\r
484 if (HttpIo == NULL || HttpIo->Http == NULL) {\r
485 return EFI_INVALID_PARAMETER;\r
486 }\r
487\r
488 HttpIo->ReqToken.Status = EFI_NOT_READY;\r
489 HttpIo->ReqToken.Message->Data.Request = Request;\r
490 HttpIo->ReqToken.Message->HeaderCount = HeaderCount;\r
491 HttpIo->ReqToken.Message->Headers = Headers;\r
492 HttpIo->ReqToken.Message->BodyLength = BodyLength;\r
493 HttpIo->ReqToken.Message->Body = Body;\r
494\r
495 //\r
496 // Queue the request token to HTTP instances.\r
497 //\r
498 Http = HttpIo->Http;\r
499 HttpIo->IsTxDone = FALSE;\r
500 Status = Http->Request (\r
501 Http,\r
502 &HttpIo->ReqToken\r
503 );\r
504 if (EFI_ERROR (Status)) {\r
505 return Status;\r
506 }\r
507\r
508 //\r
509 // Poll the network until transmit finish.\r
510 //\r
511 while (!HttpIo->IsTxDone) {\r
512 Http->Poll (Http);\r
513 }\r
514\r
515 return HttpIo->ReqToken.Status;\r
516}\r
517\r
518/**\r
519 Synchronously receive a HTTP RESPONSE message from the server.\r
520 \r
521 @param[in] HttpIo The HttpIo wrapping the HTTP service.\r
522 @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).\r
523 FALSE to continue receive the previous response message.\r
524 @param[out] ResponseData Point to a wrapper of the received response data.\r
525 \r
526 @retval EFI_SUCCESS The HTTP resopnse is received.\r
527 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
528 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
529 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.\r
530 @retval Others Other errors as indicated.\r
531\r
532**/\r
533EFI_STATUS\r
534HttpIoRecvResponse (\r
535 IN HTTP_IO *HttpIo,\r
536 IN BOOLEAN RecvMsgHeader,\r
537 OUT HTTP_IO_RESOPNSE_DATA *ResponseData\r
538 )\r
539{\r
540 EFI_STATUS Status;\r
541 EFI_HTTP_PROTOCOL *Http;\r
542\r
543 if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {\r
544 return EFI_INVALID_PARAMETER;\r
545 }\r
546\r
547 //\r
548 // Queue the response token to HTTP instances.\r
549 //\r
550 HttpIo->RspToken.Status = EFI_NOT_READY;\r
551 if (RecvMsgHeader) {\r
552 HttpIo->RspToken.Message->Data.Response = &ResponseData->Response;\r
553 } else {\r
554 HttpIo->RspToken.Message->Data.Response = NULL;\r
555 }\r
556 HttpIo->RspToken.Message->HeaderCount = 0;\r
557 HttpIo->RspToken.Message->Headers = NULL;\r
558 HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength;\r
559 HttpIo->RspToken.Message->Body = ResponseData->Body;\r
560\r
561 Http = HttpIo->Http;\r
562 HttpIo->IsRxDone = FALSE;\r
563 Status = Http->Response (\r
564 Http,\r
565 &HttpIo->RspToken\r
566 );\r
567 \r
568 if (EFI_ERROR (Status)) {\r
569 return Status;\r
570 }\r
571\r
572 //\r
573 // Poll the network until transmit finish.\r
574 //\r
575 while (!HttpIo->IsRxDone) {\r
576 Http->Poll (Http);\r
577 }\r
578\r
579 //\r
580 // Store the received data into the wrapper.\r
581 //\r
582 Status = HttpIo->ReqToken.Status;\r
583 if (!EFI_ERROR (Status)) {\r
584 ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;\r
585 ResponseData->Headers = HttpIo->RspToken.Message->Headers;\r
586 ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength;\r
587 }\r
588\r
589 return Status;\r
590}\r