49be59b8c8e3d244f5d1b0f5cd23e24443a20555
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootDxe.c
1 /** @file
2 Driver Binding functions implementation for UEFI HTTP boot.
3
4 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials are licensed and made available under
6 the terms and conditions of the BSD License that accompanies this distribution.
7 The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php.
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "HttpBootDxe.h"
16
17 ///
18 /// Driver Binding Protocol instance
19 ///
20 EFI_DRIVER_BINDING_PROTOCOL gHttpBootIp4DxeDriverBinding = {
21 HttpBootIp4DxeDriverBindingSupported,
22 HttpBootIp4DxeDriverBindingStart,
23 HttpBootIp4DxeDriverBindingStop,
24 HTTP_BOOT_DXE_VERSION,
25 NULL,
26 NULL
27 };
28
29 /**
30 Destroy the HTTP child based on IPv4 stack.
31
32 @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL.
33 @param[in] Private Pointer to HTTP_BOOT_PRIVATE_DATA.
34
35 **/
36 VOID
37 HttpBootDestroyIp4Children (
38 IN EFI_DRIVER_BINDING_PROTOCOL *This,
39 IN HTTP_BOOT_PRIVATE_DATA *Private
40 )
41 {
42 ASSERT (This != NULL);
43 ASSERT (Private != NULL);
44 ASSERT (Private->UsingIpv6 == FALSE);
45
46 if (Private->Dhcp4Child != NULL) {
47 gBS->CloseProtocol (
48 Private->Dhcp4Child,
49 &gEfiDhcp4ProtocolGuid,
50 This->DriverBindingHandle,
51 Private->Controller
52 );
53
54 NetLibDestroyServiceChild (
55 Private->Controller,
56 This->DriverBindingHandle,
57 &gEfiDhcp4ServiceBindingProtocolGuid,
58 Private->Dhcp4Child
59 );
60 }
61
62 if (Private->HttpCreated) {
63 HttpIoDestroyIo (&Private->HttpIo);
64 Private->HttpCreated = FALSE;
65 }
66
67 gBS->CloseProtocol (
68 Private->Controller,
69 &gEfiCallerIdGuid,
70 This->DriverBindingHandle,
71 Private->ChildHandle
72 );
73
74 gBS->UninstallMultipleProtocolInterfaces (
75 Private->ChildHandle,
76 &gEfiLoadFileProtocolGuid,
77 &Private->LoadFile,
78 &gEfiDevicePathProtocolGuid,
79 Private->DevicePath,
80 NULL
81 );
82
83 if (Private->DevicePath != NULL) {
84 FreePool (Private->DevicePath);
85 Private->DevicePath = NULL;
86 }
87 }
88
89 /**
90 Tests to see if this driver supports a given controller. If a child device is provided,
91 it further tests to see if this driver supports creating a handle for the specified child device.
92
93 This function checks to see if the driver specified by This supports the device specified by
94 ControllerHandle. Drivers will typically use the device path attached to
95 ControllerHandle and/or the services from the bus I/O abstraction attached to
96 ControllerHandle to determine if the driver supports ControllerHandle. This function
97 may be called many times during platform initialization. In order to reduce boot times, the tests
98 performed by this function must be very small, and take as little time as possible to execute. This
99 function must not change the state of any hardware devices, and this function must be aware that the
100 device specified by ControllerHandle may already be managed by the same driver or a
101 different driver. This function must match its calls to AllocatePages() with FreePages(),
102 AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
103 Because ControllerHandle may have been previously started by the same driver, if a protocol is
104 already in the opened state, then it must not be closed with CloseProtocol(). This is required
105 to guarantee the state of ControllerHandle is not modified by this function.
106
107 @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
108 @param[in] ControllerHandle The handle of the controller to test. This handle
109 must support a protocol interface that supplies
110 an I/O abstraction to the driver.
111 @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
112 parameter is ignored by device drivers, and is optional for bus
113 drivers. For bus drivers, if this parameter is not NULL, then
114 the bus driver must determine if the bus controller specified
115 by ControllerHandle and the child controller specified
116 by RemainingDevicePath are both supported by this
117 bus driver.
118
119 @retval EFI_SUCCESS The device specified by ControllerHandle and
120 RemainingDevicePath is supported by the driver specified by This.
121 @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
122 RemainingDevicePath is already being managed by the driver
123 specified by This.
124 @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
125 RemainingDevicePath is already being managed by a different
126 driver or an application that requires exclusive access.
127 Currently not implemented.
128 @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
129 RemainingDevicePath is not supported by the driver specified by This.
130 **/
131 EFI_STATUS
132 EFIAPI
133 HttpBootIp4DxeDriverBindingSupported (
134 IN EFI_DRIVER_BINDING_PROTOCOL *This,
135 IN EFI_HANDLE ControllerHandle,
136 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
137 )
138 {
139 EFI_STATUS Status;
140
141 //
142 // Try to open the DHCP4, HTTP4 and Device Path protocol.
143 //
144 Status = gBS->OpenProtocol (
145 ControllerHandle,
146 &gEfiDhcp4ServiceBindingProtocolGuid,
147 NULL,
148 This->DriverBindingHandle,
149 ControllerHandle,
150 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
151 );
152 if (EFI_ERROR (Status)) {
153 return Status;
154 }
155
156 Status = gBS->OpenProtocol (
157 ControllerHandle,
158 &gEfiHttpServiceBindingProtocolGuid,
159 NULL,
160 This->DriverBindingHandle,
161 ControllerHandle,
162 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
163 );
164 if (EFI_ERROR (Status)) {
165 return Status;
166 }
167
168 Status = gBS->OpenProtocol (
169 ControllerHandle,
170 &gEfiDevicePathProtocolGuid,
171 NULL,
172 This->DriverBindingHandle,
173 ControllerHandle,
174 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
175 );
176
177 return Status;
178 }
179
180
181 /**
182 Starts a device controller or a bus controller.
183
184 The Start() function is designed to be invoked from the EFI boot service ConnectController().
185 As a result, much of the error checking on the parameters to Start() has been moved into this
186 common boot service. It is legal to call Start() from other locations,
187 but the following calling restrictions must be followed, or the system behavior will not be deterministic.
188 1. ControllerHandle must be a valid EFI_HANDLE.
189 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
190 EFI_DEVICE_PATH_PROTOCOL.
191 3. Prior to calling Start(), the Supported() function for the driver specified by This must
192 have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
193
194 @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
195 @param[in] ControllerHandle The handle of the controller to start. This handle
196 must support a protocol interface that supplies
197 an I/O abstraction to the driver.
198 @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
199 parameter is ignored by device drivers, and is optional for bus
200 drivers. For a bus driver, if this parameter is NULL, then handles
201 for all the children of Controller are created by this driver.
202 If this parameter is not NULL and the first Device Path Node is
203 not the End of Device Path Node, then only the handle for the
204 child device specified by the first Device Path Node of
205 RemainingDevicePath is created by this driver.
206 If the first Device Path Node of RemainingDevicePath is
207 the End of Device Path Node, no child handle is created by this
208 driver.
209
210 @retval EFI_SUCCESS The device was started.
211 @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
212 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
213 @retval Others The driver failded to start the device.
214
215 **/
216 EFI_STATUS
217 EFIAPI
218 HttpBootIp4DxeDriverBindingStart (
219 IN EFI_DRIVER_BINDING_PROTOCOL *This,
220 IN EFI_HANDLE ControllerHandle,
221 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
222 )
223 {
224 EFI_STATUS Status;
225 HTTP_BOOT_PRIVATE_DATA *Private;
226 EFI_DEV_PATH *Node;
227 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
228 UINT32 *Id;
229
230 Status = gBS->OpenProtocol (
231 ControllerHandle,
232 &gEfiCallerIdGuid,
233 (VOID **) &Id,
234 This->DriverBindingHandle,
235 ControllerHandle,
236 EFI_OPEN_PROTOCOL_GET_PROTOCOL
237 );
238 if (!EFI_ERROR (Status)) {
239 return EFI_ALREADY_STARTED;
240 }
241
242 //
243 // Initialize the private data structure.
244 //
245 Private = AllocateZeroPool (sizeof (HTTP_BOOT_PRIVATE_DATA));
246 if (Private == NULL) {
247 return EFI_OUT_OF_RESOURCES;
248 }
249 Private->Signature = HTTP_BOOT_PRIVATE_DATA_SIGNATURE;
250 Private->Controller = ControllerHandle;
251 Private->Image = This->ImageHandle;
252 Private->UsingIpv6 = FALSE;
253 InitializeListHead (&Private->CacheList);
254
255 //
256 // Create DHCP child instance.
257 //
258 Status = NetLibCreateServiceChild (
259 ControllerHandle,
260 This->DriverBindingHandle,
261 &gEfiDhcp4ServiceBindingProtocolGuid,
262 &Private->Dhcp4Child
263 );
264 if (EFI_ERROR (Status)) {
265 goto ON_ERROR;
266 }
267
268 Status = gBS->OpenProtocol (
269 Private->Dhcp4Child,
270 &gEfiDhcp4ProtocolGuid,
271 (VOID **) &Private->Dhcp4,
272 This->DriverBindingHandle,
273 ControllerHandle,
274 EFI_OPEN_PROTOCOL_BY_DRIVER
275 );
276 if (EFI_ERROR (Status)) {
277 goto ON_ERROR;
278 }
279
280 //
281 // Get the Ip4Config2 protocol, it's required to configure the default gateway address.
282 //
283 Status = gBS->OpenProtocol (
284 ControllerHandle,
285 &gEfiIp4Config2ProtocolGuid,
286 (VOID **) &Private->Ip4Config2,
287 This->DriverBindingHandle,
288 ControllerHandle,
289 EFI_OPEN_PROTOCOL_GET_PROTOCOL
290 );
291 if (EFI_ERROR (Status)) {
292 goto ON_ERROR;
293 }
294
295 //
296 // Get the NII interface if it exists, it's not required.
297 //
298 Status = gBS->OpenProtocol (
299 ControllerHandle,
300 &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
301 (VOID **) &Private->Nii,
302 This->DriverBindingHandle,
303 ControllerHandle,
304 EFI_OPEN_PROTOCOL_GET_PROTOCOL
305 );
306 if (EFI_ERROR (Status)) {
307 Private->Nii = NULL;
308 }
309
310 //
311 // Open Device Path Protocol to prepare for appending IP and URI node.
312 //
313 Status = gBS->OpenProtocol (
314 ControllerHandle,
315 &gEfiDevicePathProtocolGuid,
316 (VOID **) &Private->ParentDevicePath,
317 This->DriverBindingHandle,
318 ControllerHandle,
319 EFI_OPEN_PROTOCOL_GET_PROTOCOL
320 );
321 if (EFI_ERROR (Status)) {
322 goto ON_ERROR;
323 }
324
325 //
326 // Append IPv4 device path node.
327 //
328 Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
329 if (Node == NULL) {
330 Status = EFI_OUT_OF_RESOURCES;
331 goto ON_ERROR;
332 }
333 Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH;
334 Node->Ipv4.Header.SubType = MSG_IPv4_DP;
335 SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
336 Node->Ipv4.StaticIpAddress = FALSE;
337 DevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
338 FreePool (Node);
339 if (DevicePath == NULL) {
340 Status = EFI_OUT_OF_RESOURCES;
341 goto ON_ERROR;
342 }
343
344 //
345 // Append URI device path node.
346 //
347 Node = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL));
348 if (Node == NULL) {
349 Status = EFI_OUT_OF_RESOURCES;
350 goto ON_ERROR;
351 }
352 Node->DevPath.Type = MESSAGING_DEVICE_PATH;
353 Node->DevPath.SubType = MSG_URI_DP;
354 SetDevicePathNodeLength (Node, sizeof (EFI_DEVICE_PATH_PROTOCOL));
355 Private->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
356 FreePool (Node);
357 FreePool (DevicePath);
358 if (Private->DevicePath == NULL) {
359 Status = EFI_OUT_OF_RESOURCES;
360 goto ON_ERROR;
361 }
362
363 //
364 // Create a child handle for the HTTP boot and install DevPath and Load file protocol on it.
365 //
366 CopyMem (&Private->LoadFile, &gHttpBootDxeLoadFile, sizeof (Private->LoadFile));
367 Status = gBS->InstallMultipleProtocolInterfaces (
368 &Private->ChildHandle,
369 &gEfiLoadFileProtocolGuid,
370 &Private->LoadFile,
371 &gEfiDevicePathProtocolGuid,
372 Private->DevicePath,
373 NULL
374 );
375 if (EFI_ERROR (Status)) {
376 goto ON_ERROR;
377 }
378
379 //
380 // Install a protocol with Caller Id Guid to the NIC, this is just to build the relationship between
381 // NIC handle and the private data.
382 //
383 Status = gBS->InstallProtocolInterface (
384 &ControllerHandle,
385 &gEfiCallerIdGuid,
386 EFI_NATIVE_INTERFACE,
387 &Private->Id
388 );
389 if (EFI_ERROR (Status)) {
390 goto ON_ERROR;
391 }
392
393 //
394 // Open the Caller Id child to setup a parent-child relationship between
395 // real NIC handle and the HTTP boot child handle.
396 //
397 Status = gBS->OpenProtocol (
398 ControllerHandle,
399 &gEfiCallerIdGuid,
400 (VOID **) &Id,
401 This->DriverBindingHandle,
402 Private->ChildHandle,
403 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
404 );
405 if (EFI_ERROR (Status)) {
406 goto ON_ERROR;
407 }
408
409 return EFI_SUCCESS;
410
411 ON_ERROR:
412
413 HttpBootDestroyIp4Children (This, Private);
414 FreePool (Private);
415
416 return Status;
417 }
418
419 /**
420 Stops a device controller or a bus controller.
421
422 The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
423 As a result, much of the error checking on the parameters to Stop() has been moved
424 into this common boot service. It is legal to call Stop() from other locations,
425 but the following calling restrictions must be followed, or the system behavior will not be deterministic.
426 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
427 same driver's Start() function.
428 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
429 EFI_HANDLE. In addition, all of these handles must have been created in this driver's
430 Start() function, and the Start() function must have called OpenProtocol() on
431 ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
432
433 @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
434 @param[in] ControllerHandle A handle to the device being stopped. The handle must
435 support a bus specific I/O protocol for the driver
436 to use to stop the device.
437 @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
438 @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
439 if NumberOfChildren is 0.
440
441 @retval EFI_SUCCESS The device was stopped.
442 @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
443
444 **/
445 EFI_STATUS
446 EFIAPI
447 HttpBootIp4DxeDriverBindingStop (
448 IN EFI_DRIVER_BINDING_PROTOCOL *This,
449 IN EFI_HANDLE ControllerHandle,
450 IN UINTN NumberOfChildren,
451 IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
452 )
453 {
454 EFI_STATUS Status;
455 EFI_LOAD_FILE_PROTOCOL *LoadFile;
456 HTTP_BOOT_PRIVATE_DATA *Private;
457 EFI_HANDLE NicHandle;
458 UINT32 *Id;
459
460 //
461 // Try to get the Load File Protocol from the controller handle.
462 //
463 Status = gBS->OpenProtocol (
464 ControllerHandle,
465 &gEfiLoadFileProtocolGuid,
466 (VOID **) &LoadFile,
467 This->DriverBindingHandle,
468 ControllerHandle,
469 EFI_OPEN_PROTOCOL_GET_PROTOCOL
470 );
471 if (EFI_ERROR (Status)) {
472 //
473 // If failed, try to find the NIC handle for this controller.
474 //
475 NicHandle = HttpBootGetNicByIp4Children (ControllerHandle);
476 if (NicHandle == NULL) {
477 return EFI_SUCCESS;
478 }
479
480 //
481 // Try to retrieve the private data by the Caller Id Guid.
482 //
483 Status = gBS->OpenProtocol (
484 NicHandle,
485 &gEfiCallerIdGuid,
486 (VOID **) &Id,
487 This->DriverBindingHandle,
488 ControllerHandle,
489 EFI_OPEN_PROTOCOL_GET_PROTOCOL
490 );
491 if (EFI_ERROR (Status)) {
492 return Status;
493 }
494 Private = HTTP_BOOT_PRIVATE_DATA_FROM_ID (Id);
495 } else {
496 Private = HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE (LoadFile);
497 NicHandle = Private->Controller;
498 }
499
500 //
501 // Disable the HTTP boot function.
502 //
503 Status = HttpBootStop (Private);
504 if (Status != EFI_SUCCESS && Status != EFI_NOT_STARTED) {
505 return Status;
506 }
507
508 //
509 // Destory all child instance and uninstall protocol interface.
510 //
511 HttpBootDestroyIp4Children (This, Private);
512
513 //
514 // Release the cached data.
515 //
516 HttpBootFreeCacheList (Private);
517
518 gBS->UninstallProtocolInterface (
519 NicHandle,
520 &gEfiCallerIdGuid,
521 &Private->Id
522 );
523 FreePool (Private);
524
525 return EFI_SUCCESS;
526 }
527
528 /**
529 This is the declaration of an EFI image entry point. This entry point is
530 the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
531 both device drivers and bus drivers.
532
533 @param[in] ImageHandle The firmware allocated handle for the UEFI image.
534 @param[in] SystemTable A pointer to the EFI System Table.
535
536 @retval EFI_SUCCESS The operation completed successfully.
537 @retval Others An unexpected error occurred.
538
539 **/
540 EFI_STATUS
541 EFIAPI
542 HttpBootDxeDriverEntryPoint (
543 IN EFI_HANDLE ImageHandle,
544 IN EFI_SYSTEM_TABLE *SystemTable
545 )
546 {
547 //
548 // Install UEFI Driver Model protocol(s).
549 //
550 return EfiLibInstallDriverBindingComponentName2 (
551 ImageHandle,
552 SystemTable,
553 &gHttpBootIp4DxeDriverBinding,
554 ImageHandle,
555 &gHttpBootDxeComponentName,
556 &gHttpBootDxeComponentName2
557 );
558 }
559