]> git.proxmox.com Git - mirror_edk2.git/blame - MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.c
1, Move device path utility macros from protocol's header file to DevicePathLib libra...
[mirror_edk2.git] / MdePkg / Library / UefiDevicePathLib / UefiDevicePathLib.c
CommitLineData
e386b444 1/** @file\r
2 Device Path services. The thing to remember is device paths are built out of\r
3 nodes. The device path is terminated by an end node that is length\r
4 sizeof(EFI_DEVICE_PATH_PROTOCOL). That would be why there is sizeof(EFI_DEVICE_PATH_PROTOCOL)\r
5 all over this file.\r
6\r
7 The only place where multi-instance device paths are supported is in\r
8 environment varibles. Multi-instance device paths should never be placed\r
9 on a Handle.\r
10\r
11 Copyright (c) 2006, Intel Corporation \r
12 All rights reserved. This program and the accompanying materials \r
13 are licensed and made available under the terms and conditions of the BSD License \r
14 which accompanies this distribution. The full text of the license may be found at \r
15 http://opensource.org/licenses/bsd-license.php \r
16\r
17 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
18 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
19\r
e386b444 20**/\r
21\r
c892d846 22\r
c7d265a9 23#include <Uefi.h>\r
c892d846 24\r
c7d265a9 25#include <Protocol/DevicePath.h>\r
c892d846 26\r
c7d265a9 27#include <Library/DevicePathLib.h>\r
28#include <Library/BaseMemoryLib.h>\r
29#include <Library/DebugLib.h>\r
30#include <Library/MemoryAllocationLib.h>\r
31#include <Library/UefiBootServicesTableLib.h>\r
32#include <Library/BaseLib.h>\r
e386b444 33\r
98a14db6 34//\r
35// Template for an end-of-device path node.\r
36//\r
34abfd7c 37GLOBAL_REMOVE_IF_UNREFERENCED CONST EFI_DEVICE_PATH_PROTOCOL mUefiDevicePathLibEndDevicePath = {\r
98a14db6 38 END_DEVICE_PATH_TYPE,\r
39 END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
697f4d59 40 {\r
41 END_DEVICE_PATH_LENGTH,\r
42 0\r
43 }\r
98a14db6 44};\r
45\r
e386b444 46/**\r
47 Returns the size of a device path in bytes.\r
48\r
49 This function returns the size, in bytes, of the device path data structure specified by\r
50 DevicePath including the end of device path node. If DevicePath is NULL, then 0 is returned.\r
51\r
52 @param DevicePath A pointer to a device path data structure.\r
53\r
54 @return The size of a device path in bytes.\r
55\r
56**/\r
57UINTN\r
58EFIAPI\r
59GetDevicePathSize (\r
60 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
61 )\r
62{\r
63 CONST EFI_DEVICE_PATH_PROTOCOL *Start;\r
64\r
65 if (DevicePath == NULL) {\r
66 return 0;\r
67 }\r
68\r
69 //\r
70 // Search for the end of the device path structure\r
71 //\r
72 Start = DevicePath;\r
e5dab016 73 while (!IsDevicePathEnd (DevicePath)) {\r
74 DevicePath = NextDevicePathNode (DevicePath);\r
e386b444 75 }\r
76\r
77 //\r
78 // Compute the size and add back in the size of the end device path structure\r
79 //\r
e5dab016 80 return ((UINTN) DevicePath - (UINTN) Start) + DevicePathNodeLength (DevicePath);\r
e386b444 81}\r
82\r
83/**\r
84 Creates a new device path by appending a second device path to a first device path.\r
85\r
86 This function allocates space for a new copy of the device path specified by DevicePath. If\r
87 DevicePath is NULL, then NULL is returned. If the memory is successfully allocated, then the\r
88 contents of DevicePath are copied to the newly allocated buffer, and a pointer to that buffer\r
89 is returned. Otherwise, NULL is returned. \r
90 \r
91 @param DevicePath A pointer to a device path data structure.\r
92\r
93 @return A pointer to the duplicated device path.\r
94\r
95**/\r
96EFI_DEVICE_PATH_PROTOCOL *\r
97EFIAPI\r
98DuplicateDevicePath (\r
99 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
100 )\r
101{\r
e386b444 102 UINTN Size;\r
103\r
104 //\r
105 // Compute the size\r
106 //\r
107 Size = GetDevicePathSize (DevicePath);\r
108 if (Size == 0) {\r
109 return NULL;\r
110 }\r
111\r
112 //\r
113 // Allocate space for duplicate device path\r
114 //\r
e386b444 115\r
f008fc32 116 return AllocateCopyPool (Size, DevicePath);\r
e386b444 117}\r
118\r
119/**\r
120 Creates a new device path by appending a second device path to a first device path.\r
121\r
122 This function creates a new device path by appending a copy of SecondDevicePath to a copy of\r
123 FirstDevicePath in a newly allocated buffer. Only the end-of-device-path device node from\r
124 SecondDevicePath is retained. The newly created device path is returned. \r
125 If FirstDevicePath is NULL, then it is ignored, and a duplicate of SecondDevicePath is returned. \r
126 If SecondDevicePath is NULL, then it is ignored, and a duplicate of FirstDevicePath is returned. \r
98a14db6 127 If both FirstDevicePath and SecondDevicePath are NULL, then a copy of an end-of-device-path is\r
128 returned. \r
e386b444 129 If there is not enough memory for the newly allocated buffer, then NULL is returned.\r
130 The memory for the new device path is allocated from EFI boot services memory. It is the\r
131 responsibility of the caller to free the memory allocated.\r
132\r
133 @param FirstDevicePath A pointer to a device path data structure.\r
134 @param SecondDevicePath A pointer to a device path data structure.\r
135\r
136 @return A pointer to the new device path.\r
137\r
138**/\r
139EFI_DEVICE_PATH_PROTOCOL *\r
140EFIAPI\r
141AppendDevicePath (\r
142 IN CONST EFI_DEVICE_PATH_PROTOCOL *FirstDevicePath, OPTIONAL\r
143 IN CONST EFI_DEVICE_PATH_PROTOCOL *SecondDevicePath OPTIONAL\r
144 )\r
145{\r
146 UINTN Size;\r
147 UINTN Size1;\r
148 UINTN Size2;\r
149 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;\r
150 EFI_DEVICE_PATH_PROTOCOL *DevicePath2;\r
151\r
152 //\r
153 // If there's only 1 path, just duplicate it.\r
154 //\r
155 if (FirstDevicePath == NULL) {\r
34abfd7c 156 return DuplicateDevicePath ((SecondDevicePath != NULL) ? SecondDevicePath : &mUefiDevicePathLibEndDevicePath);\r
e386b444 157 }\r
158\r
159 if (SecondDevicePath == NULL) {\r
160 return DuplicateDevicePath (FirstDevicePath);\r
161 }\r
162\r
163 //\r
164 // Allocate space for the combined device path. It only has one end node of\r
165 // length EFI_DEVICE_PATH_PROTOCOL.\r
166 //\r
167 Size1 = GetDevicePathSize (FirstDevicePath);\r
168 Size2 = GetDevicePathSize (SecondDevicePath);\r
e5dab016 169 Size = Size1 + Size2 - END_DEVICE_PATH_LENGTH;\r
e386b444 170\r
171 NewDevicePath = AllocatePool (Size);\r
172\r
173 if (NewDevicePath != NULL) {\r
174 NewDevicePath = CopyMem (NewDevicePath, FirstDevicePath, Size1);\r
175 //\r
176 // Over write FirstDevicePath EndNode and do the copy\r
177 //\r
178 DevicePath2 = (EFI_DEVICE_PATH_PROTOCOL *) ((CHAR8 *) NewDevicePath +\r
e5dab016 179 (Size1 - END_DEVICE_PATH_LENGTH));\r
e386b444 180 CopyMem (DevicePath2, SecondDevicePath, Size2);\r
181 }\r
182\r
183 return NewDevicePath;\r
184}\r
185\r
186/**\r
187 Creates a new path by appending the device node to the device path.\r
188\r
189 This function creates a new device path by appending a copy of the device node specified by\r
190 DevicePathNode to a copy of the device path specified by DevicePath in an allocated buffer.\r
191 The end-of-device-path device node is moved after the end of the appended device node.\r
98a14db6 192 If DevicePathNode is NULL then a copy of DevicePath is returned.\r
6336a895 193 If DevicePath is NULL then a copy of DevicePathNode, followed by an end-of-device path device\r
194 node is returned.\r
98a14db6 195 If both DevicePathNode and DevicePath are NULL then a copy of an end-of-device-path device node\r
196 is returned.\r
e386b444 197 If there is not enough memory to allocate space for the new device path, then NULL is returned. \r
198 The memory is allocated from EFI boot services memory. It is the responsibility of the caller to\r
199 free the memory allocated.\r
200\r
201 @param DevicePath A pointer to a device path data structure.\r
202 @param DevicePathNode A pointer to a single device path node.\r
203\r
204 @return A pointer to the new device path.\r
205\r
206**/\r
207EFI_DEVICE_PATH_PROTOCOL *\r
208EFIAPI\r
209AppendDevicePathNode (\r
210 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, OPTIONAL\r
211 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePathNode OPTIONAL\r
212 )\r
213{\r
214 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
215 EFI_DEVICE_PATH_PROTOCOL *NextNode;\r
216 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;\r
217 UINTN NodeLength;\r
218\r
98a14db6 219 if (DevicePathNode == NULL) {\r
34abfd7c 220 return DuplicateDevicePath ((DevicePath != NULL) ? DevicePath : &mUefiDevicePathLibEndDevicePath);\r
e386b444 221 }\r
222 //\r
223 // Build a Node that has a terminator on it\r
224 //\r
225 NodeLength = DevicePathNodeLength (DevicePathNode);\r
226\r
e5dab016 227 TempDevicePath = AllocatePool (NodeLength + END_DEVICE_PATH_LENGTH);\r
e386b444 228 if (TempDevicePath == NULL) {\r
229 return NULL;\r
230 }\r
231 TempDevicePath = CopyMem (TempDevicePath, DevicePathNode, NodeLength);\r
232 //\r
233 // Add and end device path node to convert Node to device path\r
234 //\r
235 NextNode = NextDevicePathNode (TempDevicePath);\r
236 SetDevicePathEndNode (NextNode);\r
237 //\r
238 // Append device paths\r
239 //\r
240 NewDevicePath = AppendDevicePath (DevicePath, TempDevicePath);\r
241\r
242 FreePool (TempDevicePath);\r
243\r
244 return NewDevicePath;\r
245}\r
246\r
247/**\r
248 Creates a new device path by appending the specified device path instance to the specified device\r
249 path.\r
250 \r
251 This function creates a new device path by appending a copy of the device path instance specified\r
252 by DevicePathInstance to a copy of the device path secified by DevicePath in a allocated buffer.\r
253 The end-of-device-path device node is moved after the end of the appended device path instance\r
254 and a new end-of-device-path-instance node is inserted between. \r
255 If DevicePath is NULL, then a copy if DevicePathInstance is returned.\r
256 If DevicePathInstance is NULL, then NULL is returned.\r
257 If there is not enough memory to allocate space for the new device path, then NULL is returned. \r
258 The memory is allocated from EFI boot services memory. It is the responsibility of the caller to\r
259 free the memory allocated.\r
260 \r
261 @param DevicePath A pointer to a device path data structure.\r
262 @param DevicePathInstance A pointer to a device path instance.\r
263\r
264 @return A pointer to the new device path.\r
265\r
266**/\r
267EFI_DEVICE_PATH_PROTOCOL *\r
268EFIAPI\r
269AppendDevicePathInstance (\r
270 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, OPTIONAL\r
271 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePathInstance OPTIONAL\r
272 )\r
273{\r
274 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;\r
275 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
276 UINTN SrcSize;\r
277 UINTN InstanceSize;\r
278\r
279 if (DevicePath == NULL) {\r
280 return DuplicateDevicePath (DevicePathInstance);\r
281 }\r
282\r
283 if (DevicePathInstance == NULL) {\r
284 return NULL;\r
285 }\r
286\r
287 SrcSize = GetDevicePathSize (DevicePath);\r
288 InstanceSize = GetDevicePathSize (DevicePathInstance);\r
289\r
290 NewDevicePath = AllocatePool (SrcSize + InstanceSize);\r
291 if (NewDevicePath != NULL) {\r
292 \r
293 TempDevicePath = CopyMem (NewDevicePath, DevicePath, SrcSize);;\r
294 \r
295 while (!IsDevicePathEnd (TempDevicePath)) {\r
296 TempDevicePath = NextDevicePathNode (TempDevicePath);\r
297 }\r
298 \r
299 TempDevicePath->SubType = END_INSTANCE_DEVICE_PATH_SUBTYPE;\r
300 TempDevicePath = NextDevicePathNode (TempDevicePath);\r
301 CopyMem (TempDevicePath, DevicePathInstance, InstanceSize);\r
302 }\r
303\r
304 return NewDevicePath;\r
305}\r
306\r
307/**\r
308 Creates a copy of the current device path instance and returns a pointer to the next device path\r
309 instance.\r
310\r
311 This function creates a copy of the current device path instance. It also updates DevicePath to\r
312 point to the next device path instance in the device path (or NULL if no more) and updates Size\r
313 to hold the size of the device path instance copy.\r
314 If DevicePath is NULL, then NULL is returned.\r
315 If there is not enough memory to allocate space for the new device path, then NULL is returned. \r
316 The memory is allocated from EFI boot services memory. It is the responsibility of the caller to\r
317 free the memory allocated.\r
318 If Size is NULL, then ASSERT().\r
319 \r
320 @param DevicePath On input, this holds the pointer to the current device path\r
321 instance. On output, this holds the pointer to the next device\r
322 path instance or NULL if there are no more device path\r
323 instances in the device path pointer to a device path data\r
324 structure.\r
325 @param Size On output, this holds the size of the device path instance, in\r
326 bytes or zero, if DevicePath is NULL.\r
327\r
328 @return A pointer to the current device path instance.\r
329\r
330**/\r
331EFI_DEVICE_PATH_PROTOCOL *\r
332EFIAPI\r
333GetNextDevicePathInstance (\r
334 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,\r
335 OUT UINTN *Size\r
336 )\r
337{\r
338 EFI_DEVICE_PATH_PROTOCOL *DevPath;\r
339 EFI_DEVICE_PATH_PROTOCOL *ReturnValue;\r
340 UINT8 Temp;\r
341\r
342 ASSERT (Size != NULL);\r
343\r
344 if (DevicePath == NULL || *DevicePath == NULL) {\r
345 *Size = 0;\r
346 return NULL;\r
347 }\r
348\r
349 //\r
350 // Find the end of the device path instance\r
351 //\r
352 DevPath = *DevicePath;\r
353 while (!IsDevicePathEndType (DevPath)) {\r
354 DevPath = NextDevicePathNode (DevPath);\r
355 }\r
356\r
357 //\r
358 // Compute the size of the device path instance\r
359 //\r
360 *Size = ((UINTN) DevPath - (UINTN) (*DevicePath)) + sizeof (EFI_DEVICE_PATH_PROTOCOL);\r
361 \r
362 //\r
363 // Make a copy and return the device path instance\r
364 //\r
365 Temp = DevPath->SubType;\r
366 DevPath->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;\r
367 ReturnValue = DuplicateDevicePath (*DevicePath);\r
368 DevPath->SubType = Temp;\r
369\r
370 //\r
371 // If DevPath is the end of an entire device path, then another instance\r
372 // does not follow, so *DevicePath is set to NULL.\r
373 //\r
374 if (DevicePathSubType (DevPath) == END_ENTIRE_DEVICE_PATH_SUBTYPE) {\r
375 *DevicePath = NULL;\r
376 } else {\r
377 *DevicePath = NextDevicePathNode (DevPath);\r
378 }\r
379\r
380 return ReturnValue;\r
381}\r
382\r
383/**\r
384 Creates a copy of the current device path instance and returns a pointer to the next device path\r
385 instance.\r
386\r
387 This function creates a new device node in a newly allocated buffer of size NodeLength and\r
388 initializes the device path node header with NodeType and NodeSubType. The new device path node\r
389 is returned.\r
390 If NodeLength is smaller than a device path header, then NULL is returned. \r
391 If there is not enough memory to allocate space for the new device path, then NULL is returned. \r
392 The memory is allocated from EFI boot services memory. It is the responsibility of the caller to\r
393 free the memory allocated.\r
394\r
395 @param NodeType The device node type for the new device node.\r
396 @param NodeSubType The device node sub-type for the new device node.\r
397 @param NodeLength The length of the new device node.\r
398\r
f008fc32 399 @return A pointer to the new create device path.\r
e386b444 400\r
401**/\r
402EFI_DEVICE_PATH_PROTOCOL *\r
403EFIAPI\r
404CreateDeviceNode (\r
405 IN UINT8 NodeType,\r
406 IN UINT8 NodeSubType,\r
407 IN UINT16 NodeLength\r
408 )\r
409{\r
410 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
411\r
412 if (NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {\r
413 //\r
414 // NodeLength is less than the size of the header.\r
415 //\r
416 return NULL;\r
417 }\r
418 \r
6577541d 419 DevicePath = AllocateZeroPool (NodeLength);\r
e386b444 420 if (DevicePath != NULL) {\r
421 DevicePath->Type = NodeType;\r
422 DevicePath->SubType = NodeSubType;\r
423 SetDevicePathNodeLength (DevicePath, NodeLength);\r
424 }\r
425\r
426 return DevicePath;\r
427}\r
428\r
429/**\r
430 Determines if a device path is single or multi-instance.\r
431\r
432 This function returns TRUE if the device path specified by DevicePath is multi-instance.\r
433 Otherwise, FALSE is returned. If DevicePath is NULL, then FALSE is returned.\r
434\r
435 @param DevicePath A pointer to a device path data structure.\r
436\r
437 @retval TRUE DevicePath is multi-instance.\r
438 @retval FALSE DevicePath is not multi-instance or DevicePath is NULL.\r
439\r
440**/\r
441BOOLEAN\r
442EFIAPI\r
443IsDevicePathMultiInstance (\r
444 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
445 )\r
446{\r
447 CONST EFI_DEVICE_PATH_PROTOCOL *Node;\r
448\r
449 if (DevicePath == NULL) {\r
450 return FALSE;\r
451 }\r
452\r
453 Node = DevicePath;\r
e5dab016 454 while (!IsDevicePathEnd (Node)) {\r
455 if (IsDevicePathEndInstance (Node)) {\r
e386b444 456 return TRUE;\r
457 }\r
458\r
e5dab016 459 Node = NextDevicePathNode (Node);\r
e386b444 460 }\r
461\r
462 return FALSE;\r
463}\r
464\r
465\r
466/**\r
467 Retrieves the device path protocol from a handle.\r
468\r
469 This function returns the device path protocol from the handle specified by Handle. If Handle is\r
470 NULL or Handle does not contain a device path protocol, then NULL is returned.\r
471 \r
472 @param Handle The handle from which to retrieve the device path protocol.\r
473\r
474 @return The device path protocol from the handle specified by Handle.\r
475\r
476**/\r
477EFI_DEVICE_PATH_PROTOCOL *\r
478EFIAPI\r
479DevicePathFromHandle (\r
480 IN EFI_HANDLE Handle\r
481 )\r
482{\r
483 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
484 EFI_STATUS Status;\r
485\r
486 Status = gBS->HandleProtocol (\r
487 Handle,\r
488 &gEfiDevicePathProtocolGuid,\r
489 (VOID *) &DevicePath\r
490 );\r
491 if (EFI_ERROR (Status)) {\r
492 DevicePath = NULL;\r
493 }\r
494 return DevicePath;\r
495}\r
496\r
497/**\r
498 Allocates a device path for a file and appends it to an existing device path.\r
499\r
500 If Device is a valid device handle that contains a device path protocol, then a device path for\r
501 the file specified by FileName is allocated and appended to the device path associated with the\r
502 handle Device. The allocated device path is returned. If Device is NULL or Device is a handle\r
503 that does not support the device path protocol, then a device path containing a single device\r
504 path node for the file specified by FileName is allocated and returned.\r
505 If FileName is NULL, then ASSERT().\r
506\r
507 @param Device A pointer to a device handle. This parameter is optional and\r
508 may be NULL.\r
509 @param FileName A pointer to a Null-terminated Unicode string.\r
510\r
f008fc32 511 @return A pointer to the new created file device path.\r
e386b444 512\r
513**/\r
514EFI_DEVICE_PATH_PROTOCOL *\r
515EFIAPI\r
516FileDevicePath (\r
517 IN EFI_HANDLE Device, OPTIONAL\r
518 IN CONST CHAR16 *FileName\r
519 )\r
520{\r
e5dab016 521 UINT16 Size;\r
e386b444 522 FILEPATH_DEVICE_PATH *FilePath;\r
523 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
524 EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;\r
525\r
526 DevicePath = NULL;\r
527\r
e5dab016 528 Size = (UINT16) StrSize (FileName);\r
529 \r
530 FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + END_DEVICE_PATH_LENGTH);\r
e386b444 531 if (FileDevicePath != NULL) {\r
532 FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath;\r
533 FilePath->Header.Type = MEDIA_DEVICE_PATH;\r
534 FilePath->Header.SubType = MEDIA_FILEPATH_DP;\r
535 CopyMem (&FilePath->PathName, FileName, Size);\r
536 SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH);\r
537 SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header));\r
538\r
539 if (Device != NULL) {\r
540 DevicePath = DevicePathFromHandle (Device);\r
541 }\r
542\r
543 DevicePath = AppendDevicePath (DevicePath, FileDevicePath);\r
544 FreePool (FileDevicePath);\r
545 }\r
546\r
547 return DevicePath;\r
548}\r
549\r