NetworkPkg: Stop and release DHCP4 child after boot info is ready
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootImpl.c
1 /** @file
2 The implementation of EFI_LOAD_FILE_PROTOCOL 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 Enable the use of UEFI HTTP boot function.
19
20 @param[in] Private The pointer to the driver's private data.
21
22 @retval EFI_SUCCESS HTTP boot was successfully enabled.
23 @retval EFI_INVALID_PARAMETER Private is NULL.
24 @retval EFI_ALREADY_STARTED The driver is already in started state.
25
26 **/
27 EFI_STATUS
28 HttpBootStart (
29 IN HTTP_BOOT_PRIVATE_DATA *Private
30 )
31 {
32 UINTN Index;
33
34 if (Private == NULL) {
35 return EFI_INVALID_PARAMETER;
36 }
37
38 if (Private->Started) {
39 return EFI_ALREADY_STARTED;
40 }
41
42 if (!Private->UsingIpv6) {
43 //
44 // Init the content of cached DHCP offer list.
45 //
46 ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
47 for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
48 Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = HTTP_BOOT_DHCP4_PACKET_MAX_SIZE;
49 }
50 } else {
51 ASSERT (FALSE);
52 }
53
54 Private->Started = TRUE;
55
56 return EFI_SUCCESS;
57 }
58
59 /**
60 Attempt to complete a DHCPv4 D.O.R.A sequence to retrieve the boot resource information.
61
62 @param[in] Private The pointer to the driver's private data.
63
64 @retval EFI_SUCCESS Boot info was successfully retrieved.
65 @retval EFI_INVALID_PARAMETER Private is NULL.
66 @retval EFI_NOT_STARTED The driver is in stopped state.
67 @retval EFI_DEVICE_ERROR An unexpected network error occurred.
68 @retval Others Other errors as indicated.
69
70 **/
71 EFI_STATUS
72 HttpBootDhcp (
73 IN HTTP_BOOT_PRIVATE_DATA *Private
74 )
75 {
76 EFI_STATUS Status;
77
78 if (Private == NULL) {
79 return EFI_INVALID_PARAMETER;
80 }
81
82 if (!Private->Started) {
83 return EFI_NOT_STARTED;
84 }
85
86 Status = EFI_DEVICE_ERROR;
87
88 if (!Private->UsingIpv6) {
89 Status = HttpBootDhcp4Dora (Private);
90 } else {
91 ASSERT (FALSE);
92 }
93
94 return Status;
95 }
96
97 /**
98 Attempt to download the boot file through HTTP message exchange.
99
100 @param[in] Private The pointer to the driver's private data.
101 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
102 code of EFI_SUCCESS, the amount of data transferred to
103 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
104 the size of Buffer required to retrieve the requested file.
105 @param[in] Buffer The memory buffer to transfer the file to. If Buffer is NULL,
106 then the size of the requested file is returned in
107 BufferSize.
108
109 @retval EFI_SUCCESS Boot file was loaded successfully.
110 @retval EFI_INVALID_PARAMETER Private is NULL.
111 @retval EFI_NOT_STARTED The driver is in stopped state.
112 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the boot file. BufferSize has
113 been updated with the size needed to complete the request.
114 @retval EFI_DEVICE_ERROR An unexpected network error occurred.
115 @retval Others Other errors as indicated.
116
117 **/
118 EFI_STATUS
119 HttpBootLoadFile (
120 IN HTTP_BOOT_PRIVATE_DATA *Private,
121 IN OUT UINTN *BufferSize,
122 IN VOID *Buffer OPTIONAL
123 )
124 {
125 EFI_STATUS Status;
126
127 if (Private == NULL) {
128 return EFI_INVALID_PARAMETER;
129 }
130
131 if (!Private->Started) {
132 return EFI_NOT_STARTED;
133 }
134
135 Status = EFI_DEVICE_ERROR;
136
137 if (Private->BootFileUri == NULL) {
138 //
139 // Parse the cached offer to get the boot file URL first.
140 //
141 Status = HttpBootDiscoverBootInfo (Private);
142 if (EFI_ERROR (Status)) {
143 return Status;
144 }
145 }
146
147 if (!Private->HttpCreated) {
148 //
149 // Create HTTP child.
150 //
151 Status = HttpBootCreateHttpIo (Private);
152 if (EFI_ERROR (Status)) {
153 return Status;
154 }
155 }
156
157 if (Private->BootFileSize == 0) {
158 //
159 // Discover the information about the bootfile if we haven't.
160 //
161
162 //
163 // Try to use HTTP HEAD method.
164 //
165 Status = HttpBootGetBootFile (
166 Private,
167 TRUE,
168 &Private->BootFileSize,
169 NULL
170 );
171 if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
172 //
173 // Failed to get file size by HEAD method, may be trunked encoding, try HTTP GET method.
174 //
175 ASSERT (Private->BootFileSize == 0);
176 Status = HttpBootGetBootFile (
177 Private,
178 FALSE,
179 &Private->BootFileSize,
180 NULL
181 );
182 if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
183 return Status;
184 }
185 }
186 }
187
188 if (*BufferSize < Private->BootFileSize) {
189 *BufferSize = Private->BootFileSize;
190 return EFI_BUFFER_TOO_SMALL;
191 }
192
193 //
194 // Load the boot file into Buffer
195 //
196 return HttpBootGetBootFile (
197 Private,
198 FALSE,
199 BufferSize,
200 Buffer
201 );
202 }
203
204 /**
205 Disable the use of UEFI HTTP boot function.
206
207 @param[in] Private The pointer to the driver's private data.
208
209 @retval EFI_SUCCESS HTTP boot was successfully disabled.
210 @retval EFI_NOT_STARTED The driver is already in stopped state.
211 @retval EFI_INVALID_PARAMETER Private is NULL.
212 @retval Others Unexpected error when stop the function.
213
214 **/
215 EFI_STATUS
216 HttpBootStop (
217 IN HTTP_BOOT_PRIVATE_DATA *Private
218 )
219 {
220 UINTN Index;
221
222 if (Private == NULL) {
223 return EFI_INVALID_PARAMETER;
224 }
225
226 if (!Private->Started) {
227 return EFI_NOT_STARTED;
228 }
229
230 if (Private->HttpCreated) {
231 HttpIoDestroyIo (&Private->HttpIo);
232 Private->HttpCreated = FALSE;
233 }
234
235 Private->Started = FALSE;
236 ZeroMem (&Private->StationIp, sizeof (EFI_IP_ADDRESS));
237 ZeroMem (&Private->SubnetMask, sizeof (EFI_IP_ADDRESS));
238 ZeroMem (&Private->GatewayIp, sizeof (EFI_IP_ADDRESS));
239 Private->Port = 0;
240 Private->BootFileUri = NULL;
241 Private->BootFileUriParser = NULL;
242 Private->BootFileSize = 0;
243 Private->SelectIndex = 0;
244 Private->SelectProxyType = HttpOfferTypeMax;
245
246 if (!Private->UsingIpv6) {
247 //
248 // Stop and release the DHCP4 child.
249 //
250 Private->Dhcp4->Stop (Private->Dhcp4);
251 Private->Dhcp4->Configure (Private->Dhcp4, NULL);
252
253 for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
254 if (Private->OfferBuffer[Index].Dhcp4.UriParser) {
255 HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp4.UriParser);
256 }
257 }
258 } else {
259 ASSERT (FALSE);
260 }
261
262 ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
263 Private->OfferNum = 0;
264 ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
265 ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
266
267 return EFI_SUCCESS;
268 }
269
270 /**
271 Causes the driver to load a specified file.
272
273 @param This Protocol instance pointer.
274 @param FilePath The device specific path of the file to load.
275 @param BootPolicy If TRUE, indicates that the request originates from the
276 boot manager is attempting to load FilePath as a boot
277 selection. If FALSE, then FilePath must match as exact file
278 to be loaded.
279 @param BufferSize On input the size of Buffer in bytes. On output with a return
280 code of EFI_SUCCESS, the amount of data transferred to
281 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
282 the size of Buffer required to retrieve the requested file.
283 @param Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
284 then the size of the requested file is returned in
285 BufferSize.
286
287 @retval EFI_SUCCESS The file was loaded.
288 @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy
289 @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
290 BufferSize is NULL.
291 @retval EFI_NO_MEDIA No medium was present to load the file.
292 @retval EFI_DEVICE_ERROR The file was not loaded due to a device error.
293 @retval EFI_NO_RESPONSE The remote system did not respond.
294 @retval EFI_NOT_FOUND The file was not found.
295 @retval EFI_ABORTED The file load process was manually cancelled.
296 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
297 BufferSize has been updated with the size needed to complete
298 the request.
299
300 **/
301 EFI_STATUS
302 EFIAPI
303 HttpBootDxeLoadFile (
304 IN EFI_LOAD_FILE_PROTOCOL *This,
305 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
306 IN BOOLEAN BootPolicy,
307 IN OUT UINTN *BufferSize,
308 IN VOID *Buffer OPTIONAL
309 )
310 {
311 HTTP_BOOT_PRIVATE_DATA *Private;
312 BOOLEAN MediaPresent;
313 EFI_STATUS Status;
314
315 if (This == NULL || BufferSize == NULL) {
316 return EFI_INVALID_PARAMETER;
317 }
318
319 //
320 // Only support BootPolicy
321 //
322 if (!BootPolicy) {
323 return EFI_UNSUPPORTED;
324 }
325
326 Private = HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE (This);
327
328 //
329 // Check media status before HTTP boot start
330 //
331 MediaPresent = TRUE;
332 NetLibDetectMedia (Private->Controller, &MediaPresent);
333 if (!MediaPresent) {
334 return EFI_NO_MEDIA;
335 }
336
337 //
338 // Initialize HTTP boot and load the boot file.
339 //
340 Status = HttpBootStart (Private);
341 if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) {
342 Status = HttpBootLoadFile (Private, BufferSize, Buffer);
343 }
344
345 if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) {
346 HttpBootStop (Private);
347 } else {
348 //
349 // Stop and release the DHCP4 child.
350 //
351 Private->Dhcp4->Stop (Private->Dhcp4);
352 Private->Dhcp4->Configure (Private->Dhcp4, NULL);
353 }
354
355 return Status;
356 }
357
358 ///
359 /// Load File Protocol instance
360 ///
361 GLOBAL_REMOVE_IF_UNREFERENCED
362 EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile = {
363 HttpBootDxeLoadFile
364 };