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