NetworkPkg: Add UEFI HTTP boot driver.
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootImpl.c
CommitLineData
c4545d76
FS
1/** @file
2 The implementation of EFI_LOAD_FILE_PROTOCOL for UEFI HTTP boot.
3
4Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials are licensed and made available under
6the terms and conditions of the BSD License that accompanies this distribution.
7The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php.
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT 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**/
27EFI_STATUS
28HttpBootStart (
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**/
71EFI_STATUS
72HttpBootDhcp (
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**/
118EFI_STATUS
119HttpBootLoadFile (
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**/
215EFI_STATUS
216HttpBootStop (
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**/
295EFI_STATUS
296EFIAPI
297HttpBootDxeLoadFile (
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///
349GLOBAL_REMOVE_IF_UNREFERENCED
350EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile = {
351 HttpBootDxeLoadFile
352};