711cc3c8c5f94236d06b2b59f62fc22c6c7d8de5
[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 for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
248 if (Private->OfferBuffer[Index].Dhcp4.UriParser) {
249 HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp4.UriParser);
250 }
251 }
252 } else {
253 ASSERT (FALSE);
254 }
255
256 ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
257 Private->OfferNum = 0;
258 ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
259 ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
260
261 return EFI_SUCCESS;
262 }
263
264 /**
265 Causes the driver to load a specified file.
266
267 @param This Protocol instance pointer.
268 @param FilePath The device specific path of the file to load.
269 @param BootPolicy If TRUE, indicates that the request originates from the
270 boot manager is attempting to load FilePath as a boot
271 selection. If FALSE, then FilePath must match as exact file
272 to be loaded.
273 @param BufferSize On input the size of Buffer in bytes. On output with a return
274 code of EFI_SUCCESS, the amount of data transferred to
275 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
276 the size of Buffer required to retrieve the requested file.
277 @param Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
278 then the size of the requested file is returned in
279 BufferSize.
280
281 @retval EFI_SUCCESS The file was loaded.
282 @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy
283 @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
284 BufferSize is NULL.
285 @retval EFI_NO_MEDIA No medium was present to load the file.
286 @retval EFI_DEVICE_ERROR The file was not loaded due to a device error.
287 @retval EFI_NO_RESPONSE The remote system did not respond.
288 @retval EFI_NOT_FOUND The file was not found.
289 @retval EFI_ABORTED The file load process was manually cancelled.
290 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
291 BufferSize has been updated with the size needed to complete
292 the request.
293
294 **/
295 EFI_STATUS
296 EFIAPI
297 HttpBootDxeLoadFile (
298 IN EFI_LOAD_FILE_PROTOCOL *This,
299 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
300 IN BOOLEAN BootPolicy,
301 IN OUT UINTN *BufferSize,
302 IN VOID *Buffer OPTIONAL
303 )
304 {
305 HTTP_BOOT_PRIVATE_DATA *Private;
306 BOOLEAN MediaPresent;
307 EFI_STATUS Status;
308
309 if (This == NULL || BufferSize == NULL) {
310 return EFI_INVALID_PARAMETER;
311 }
312
313 //
314 // Only support BootPolicy
315 //
316 if (!BootPolicy) {
317 return EFI_UNSUPPORTED;
318 }
319
320 Private = HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE (This);
321
322 //
323 // Check media status before HTTP boot start
324 //
325 MediaPresent = TRUE;
326 NetLibDetectMedia (Private->Controller, &MediaPresent);
327 if (!MediaPresent) {
328 return EFI_NO_MEDIA;
329 }
330
331 //
332 // Initialize HTTP boot and load the boot file.
333 //
334 Status = HttpBootStart (Private);
335 if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) {
336 Status = HttpBootLoadFile (Private, BufferSize, Buffer);
337 }
338
339 if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) {
340 HttpBootStop (Private);
341 }
342
343 return Status;
344 }
345
346 ///
347 /// Load File Protocol instance
348 ///
349 GLOBAL_REMOVE_IF_UNREFERENCED
350 EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile = {
351 HttpBootDxeLoadFile
352 };