xref: /freebsd/lib/libefivar/uefi-dputil.c (revision 8494a3de2c50e9d066b9e0b043306a49b932e025)
1 /*-
2  * Copyright (c) 2017 Netflix, Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 /*
27  * Routines to format EFI_DEVICE_PATHs from the UEFI standard. Much of
28  * this file is taken from EDK2 and rototilled.
29  */
30 
31 #include <efivar.h>
32 #include <limits.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <sys/endian.h>
36 #include "uefi-dplib.h"
37 
38 /*
39  * Taken from MdePkg/Library/UefiDevicePathLib/DevicePathUtilities.c
40  * hash 2f88bd3a1296c522317f1c21377876de63de5be7 2021-Dec-07
41  */
42 
43 /** @file
44   Device Path services. The thing to remember is device paths are built out of
45   nodes. The device path is terminated by an end node that is length
46   sizeof(EFI_DEVICE_PATH_PROTOCOL). That would be why there is sizeof(EFI_DEVICE_PATH_PROTOCOL)
47   all over this file.
48 
49   The only place where multi-instance device paths are supported is in
50   environment varibles. Multi-instance device paths should never be placed
51   on a Handle.
52 
53   Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
54   SPDX-License-Identifier: BSD-2-Clause-Patent
55 
56 **/
57 
58 // #include "UefiDevicePathLib.h"
59 
60 //
61 // Template for an end-of-device path node.
62 //
63 static CONST EFI_DEVICE_PATH_PROTOCOL  mUefiDevicePathLibEndDevicePath = {
64   END_DEVICE_PATH_TYPE,
65   END_ENTIRE_DEVICE_PATH_SUBTYPE,
66   {
67     END_DEVICE_PATH_LENGTH,
68     0
69   }
70 };
71 
72 /**
73   Determine whether a given device path is valid.
74 
75   @param  DevicePath  A pointer to a device path data structure.
76   @param  MaxSize     The maximum size of the device path data structure.
77 
78   @retval TRUE        DevicePath is valid.
79   @retval FALSE       DevicePath is NULL.
80   @retval FALSE       Maxsize is less than sizeof(EFI_DEVICE_PATH_PROTOCOL).
81   @retval FALSE       The length of any node Node in the DevicePath is less
82                       than sizeof (EFI_DEVICE_PATH_PROTOCOL).
83   @retval FALSE       If MaxSize is not zero, the size of the DevicePath
84                       exceeds MaxSize.
85   @retval FALSE       If PcdMaximumDevicePathNodeCount is not zero, the node
86                       count of the DevicePath exceeds PcdMaximumDevicePathNodeCount.
87 **/
88 BOOLEAN
89 EFIAPI
IsDevicePathValid(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath,IN UINTN MaxSize)90 IsDevicePathValid (
91   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath,
92   IN       UINTN                     MaxSize
93   )
94 {
95   UINTN  Count;
96   UINTN  Size;
97   UINTN  NodeLength;
98 
99   //
100   // Validate the input whether exists and its size big enough to touch the first node
101   //
102   if ((DevicePath == NULL) || ((MaxSize > 0) && (MaxSize < END_DEVICE_PATH_LENGTH))) {
103     return FALSE;
104   }
105 
106   if (MaxSize == 0) {
107     MaxSize = MAX_UINTN;
108   }
109 
110   for (Count = 0, Size = 0; !IsDevicePathEnd (DevicePath); DevicePath = NextDevicePathNode (DevicePath)) {
111     NodeLength = DevicePathNodeLength (DevicePath);
112     if (NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
113       return FALSE;
114     }
115 
116     if (NodeLength > MAX_UINTN - Size) {
117       return FALSE;
118     }
119 
120     Size += NodeLength;
121 
122     //
123     // Validate next node before touch it.
124     //
125     if (Size > MaxSize - END_DEVICE_PATH_LENGTH ) {
126       return FALSE;
127     }
128 
129     if (PcdGet32 (PcdMaximumDevicePathNodeCount) > 0) {
130       Count++;
131       if (Count >= PcdGet32 (PcdMaximumDevicePathNodeCount)) {
132         return FALSE;
133       }
134     }
135 
136     //
137     // FilePath must be a NULL-terminated string.
138     //
139     if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
140         (DevicePathSubType (DevicePath) == MEDIA_FILEPATH_DP) &&
141         (*(const CHAR16 *)((const UINT8 *) DevicePath + NodeLength - 2) != 0))
142     {
143       return FALSE;
144     }
145   }
146 
147   //
148   // Only return TRUE when the End Device Path node is valid.
149   //
150   return (BOOLEAN)(DevicePathNodeLength (DevicePath) == END_DEVICE_PATH_LENGTH);
151 }
152 
153 /**
154   Returns the Type field of a device path node.
155 
156   Returns the Type field of the device path node specified by Node.
157 
158   If Node is NULL, then ASSERT().
159 
160   @param  Node      A pointer to a device path node data structure.
161 
162   @return The Type field of the device path node specified by Node.
163 
164 **/
165 UINT8
166 EFIAPI
DevicePathType(IN CONST VOID * Node)167 DevicePathType (
168   IN CONST VOID  *Node
169   )
170 {
171   ASSERT (Node != NULL);
172   return ((const EFI_DEVICE_PATH_PROTOCOL *)(Node))->Type;
173 }
174 
175 /**
176   Returns the SubType field of a device path node.
177 
178   Returns the SubType field of the device path node specified by Node.
179 
180   If Node is NULL, then ASSERT().
181 
182   @param  Node      A pointer to a device path node data structure.
183 
184   @return The SubType field of the device path node specified by Node.
185 
186 **/
187 UINT8
188 EFIAPI
DevicePathSubType(IN CONST VOID * Node)189 DevicePathSubType (
190   IN CONST VOID  *Node
191   )
192 {
193   ASSERT (Node != NULL);
194   return ((const EFI_DEVICE_PATH_PROTOCOL *)(Node))->SubType;
195 }
196 
197 /**
198   Returns the 16-bit Length field of a device path node.
199 
200   Returns the 16-bit Length field of the device path node specified by Node.
201   Node is not required to be aligned on a 16-bit boundary, so it is recommended
202   that a function such as ReadUnaligned16() be used to extract the contents of
203   the Length field.
204 
205   If Node is NULL, then ASSERT().
206 
207   @param  Node      A pointer to a device path node data structure.
208 
209   @return The 16-bit Length field of the device path node specified by Node.
210 
211 **/
212 UINTN
213 EFIAPI
DevicePathNodeLength(IN CONST VOID * Node)214 DevicePathNodeLength (
215   IN CONST VOID  *Node
216   )
217 {
218   ASSERT (Node != NULL);
219 //  return ReadUnaligned16 ((UINT16 *)&((EFI_DEVICE_PATH_PROTOCOL *)(Node))->Length[0]);
220   return ((const EFI_DEVICE_PATH_PROTOCOL *)Node)->Length[0] |
221       (((const EFI_DEVICE_PATH_PROTOCOL *)Node)->Length[1] << 8);
222 }
223 
224 /**
225   Returns a pointer to the next node in a device path.
226 
227   Returns a pointer to the device path node that follows the device path node
228   specified by Node.
229 
230   If Node is NULL, then ASSERT().
231 
232   @param  Node      A pointer to a device path node data structure.
233 
234   @return a pointer to the device path node that follows the device path node
235   specified by Node.
236 
237 **/
238 EFI_DEVICE_PATH_PROTOCOL *
239 EFIAPI
NextDevicePathNode(IN CONST VOID * Node)240 NextDevicePathNode (
241   IN CONST VOID  *Node
242   )
243 {
244   ASSERT (Node != NULL);
245   return (EFI_DEVICE_PATH_PROTOCOL *)(__DECONST(UINT8 *, Node) + DevicePathNodeLength (Node));
246 }
247 
248 /**
249   Determines if a device path node is an end node of a device path.
250   This includes nodes that are the end of a device path instance and nodes that
251   are the end of an entire device path.
252 
253   Determines if the device path node specified by Node is an end node of a device path.
254   This includes nodes that are the end of a device path instance and nodes that are the
255   end of an entire device path.  If Node represents an end node of a device path,
256   then TRUE is returned.  Otherwise, FALSE is returned.
257 
258   If Node is NULL, then ASSERT().
259 
260   @param  Node      A pointer to a device path node data structure.
261 
262   @retval TRUE      The device path node specified by Node is an end node of a
263                     device path.
264   @retval FALSE     The device path node specified by Node is not an end node of
265                     a device path.
266 
267 **/
268 BOOLEAN
269 EFIAPI
IsDevicePathEndType(IN CONST VOID * Node)270 IsDevicePathEndType (
271   IN CONST VOID  *Node
272   )
273 {
274   ASSERT (Node != NULL);
275   return (BOOLEAN)(DevicePathType (Node) == END_DEVICE_PATH_TYPE);
276 }
277 
278 /**
279   Determines if a device path node is an end node of an entire device path.
280 
281   Determines if a device path node specified by Node is an end node of an entire
282   device path. If Node represents the end of an entire device path, then TRUE is
283   returned.  Otherwise, FALSE is returned.
284 
285   If Node is NULL, then ASSERT().
286 
287   @param  Node      A pointer to a device path node data structure.
288 
289   @retval TRUE      The device path node specified by Node is the end of an entire
290                     device path.
291   @retval FALSE     The device path node specified by Node is not the end of an
292                     entire device path.
293 
294 **/
295 BOOLEAN
296 EFIAPI
IsDevicePathEnd(IN CONST VOID * Node)297 IsDevicePathEnd (
298   IN CONST VOID  *Node
299   )
300 {
301   ASSERT (Node != NULL);
302   return (BOOLEAN)(IsDevicePathEndType (Node) && DevicePathSubType (Node) == END_ENTIRE_DEVICE_PATH_SUBTYPE);
303 }
304 
305 #ifndef __FreeBSD__
306 /**
307   Determines if a device path node is an end node of a device path instance.
308 
309   Determines if a device path node specified by Node is an end node of a device
310   path instance. If Node represents the end of a device path instance, then TRUE
311   is returned.  Otherwise, FALSE is returned.
312 
313   If Node is NULL, then ASSERT().
314 
315   @param  Node      A pointer to a device path node data structure.
316 
317   @retval TRUE      The device path node specified by Node is the end of a device
318                     path instance.
319   @retval FALSE     The device path node specified by Node is not the end of a
320                     device path instance.
321 
322 **/
323 BOOLEAN
324 EFIAPI
IsDevicePathEndInstance(IN CONST VOID * Node)325 IsDevicePathEndInstance (
326   IN CONST VOID  *Node
327   )
328 {
329   ASSERT (Node != NULL);
330   return (BOOLEAN)(IsDevicePathEndType (Node) && DevicePathSubType (Node) == END_INSTANCE_DEVICE_PATH_SUBTYPE);
331 }
332 #endif
333 
334 /**
335   Sets the length, in bytes, of a device path node.
336 
337   Sets the length of the device path node specified by Node to the value specified
338   by NodeLength.  NodeLength is returned.  Node is not required to be aligned on
339   a 16-bit boundary, so it is recommended that a function such as WriteUnaligned16()
340   be used to set the contents of the Length field.
341 
342   If Node is NULL, then ASSERT().
343   If NodeLength >= SIZE_64KB, then ASSERT().
344   If NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL), then ASSERT().
345 
346   @param  Node      A pointer to a device path node data structure.
347   @param  Length    The length, in bytes, of the device path node.
348 
349   @return Length
350 
351 **/
352 UINT16
353 EFIAPI
SetDevicePathNodeLength(IN OUT VOID * Node,IN UINTN Length)354 SetDevicePathNodeLength (
355   IN OUT VOID  *Node,
356   IN UINTN     Length
357   )
358 {
359   ASSERT (Node != NULL);
360   ASSERT ((Length >= sizeof (EFI_DEVICE_PATH_PROTOCOL)) && (Length < SIZE_64KB));
361 //  return WriteUnaligned16 ((UINT16 *)&((EFI_DEVICE_PATH_PROTOCOL *)(Node))->Length[0], (UINT16)(Length));
362   le16enc(&((EFI_DEVICE_PATH_PROTOCOL *)(Node))->Length[0], (UINT16)(Length));
363   return Length;
364 }
365 
366 /**
367   Fills in all the fields of a device path node that is the end of an entire device path.
368 
369   Fills in all the fields of a device path node specified by Node so Node represents
370   the end of an entire device path.  The Type field of Node is set to
371   END_DEVICE_PATH_TYPE, the SubType field of Node is set to
372   END_ENTIRE_DEVICE_PATH_SUBTYPE, and the Length field of Node is set to
373   END_DEVICE_PATH_LENGTH.  Node is not required to be aligned on a 16-bit boundary,
374   so it is recommended that a function such as WriteUnaligned16() be used to set
375   the contents of the Length field.
376 
377   If Node is NULL, then ASSERT().
378 
379   @param  Node      A pointer to a device path node data structure.
380 
381 **/
382 VOID
383 EFIAPI
SetDevicePathEndNode(OUT VOID * Node)384 SetDevicePathEndNode (
385   OUT VOID  *Node
386   )
387 {
388   ASSERT (Node != NULL);
389   memcpy (Node, &mUefiDevicePathLibEndDevicePath, sizeof (mUefiDevicePathLibEndDevicePath));
390 }
391 
392 /**
393   Returns the size of a device path in bytes.
394 
395   This function returns the size, in bytes, of the device path data structure
396   specified by DevicePath including the end of device path node.
397   If DevicePath is NULL or invalid, then 0 is returned.
398 
399   @param  DevicePath  A pointer to a device path data structure.
400 
401   @retval 0           If DevicePath is NULL or invalid.
402   @retval Others      The size of a device path in bytes.
403 
404 **/
405 UINTN
406 EFIAPI
GetDevicePathSize(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath)407 GetDevicePathSize (
408   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath
409   )
410 {
411   CONST EFI_DEVICE_PATH_PROTOCOL  *Start;
412 
413   if (DevicePath == NULL) {
414     return 0;
415   }
416 
417   if (!IsDevicePathValid (DevicePath, 0)) {
418     return 0;
419   }
420 
421   //
422   // Search for the end of the device path structure
423   //
424   Start = DevicePath;
425   while (!IsDevicePathEnd (DevicePath)) {
426     DevicePath = NextDevicePathNode (DevicePath);
427   }
428 
429   //
430   // Compute the size and add back in the size of the end device path structure
431   //
432   return ((UINTN)DevicePath - (UINTN)Start) + DevicePathNodeLength (DevicePath);
433 }
434 
435 /**
436   Creates a new copy of an existing device path.
437 
438   This function allocates space for a new copy of the device path specified by DevicePath.
439   If DevicePath is NULL, then NULL is returned.  If the memory is successfully
440   allocated, then the contents of DevicePath are copied to the newly allocated
441   buffer, and a pointer to that buffer is returned.  Otherwise, NULL is returned.
442   The memory for the new device path is allocated from EFI boot services memory.
443   It is the responsibility of the caller to free the memory allocated.
444 
445   @param  DevicePath    A pointer to a device path data structure.
446 
447   @retval NULL          DevicePath is NULL or invalid.
448   @retval Others        A pointer to the duplicated device path.
449 
450 **/
451 EFI_DEVICE_PATH_PROTOCOL *
452 EFIAPI
DuplicateDevicePath(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath)453 DuplicateDevicePath (
454   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath
455   )
456 {
457   UINTN  Size;
458 
459   //
460   // Compute the size
461   //
462   Size = GetDevicePathSize (DevicePath);
463   if (Size == 0) {
464     return NULL;
465   }
466 
467   //
468   // Allocate space for duplicate device path
469   //
470 
471   return AllocateCopyPool (Size, DevicePath);
472 }
473 
474 /**
475   Creates a new device path by appending a second device path to a first device path.
476 
477   This function creates a new device path by appending a copy of SecondDevicePath
478   to a copy of FirstDevicePath in a newly allocated buffer.  Only the end-of-device-path
479   device node from SecondDevicePath is retained. The newly created device path is
480   returned. If FirstDevicePath is NULL, then it is ignored, and a duplicate of
481   SecondDevicePath is returned.  If SecondDevicePath is NULL, then it is ignored,
482   and a duplicate of FirstDevicePath is returned. If both FirstDevicePath and
483   SecondDevicePath are NULL, then a copy of an end-of-device-path is returned.
484 
485   If there is not enough memory for the newly allocated buffer, then NULL is returned.
486   The memory for the new device path is allocated from EFI boot services memory.
487   It is the responsibility of the caller to free the memory allocated.
488 
489   @param  FirstDevicePath            A pointer to a device path data structure.
490   @param  SecondDevicePath           A pointer to a device path data structure.
491 
492   @retval NULL      If there is not enough memory for the newly allocated buffer.
493   @retval NULL      If FirstDevicePath or SecondDevicePath is invalid.
494   @retval Others    A pointer to the new device path if success.
495                     Or a copy an end-of-device-path if both FirstDevicePath and SecondDevicePath are NULL.
496 
497 **/
498 EFI_DEVICE_PATH_PROTOCOL *
499 EFIAPI
AppendDevicePath(IN CONST EFI_DEVICE_PATH_PROTOCOL * FirstDevicePath OPTIONAL,IN CONST EFI_DEVICE_PATH_PROTOCOL * SecondDevicePath OPTIONAL)500 AppendDevicePath (
501   IN CONST EFI_DEVICE_PATH_PROTOCOL  *FirstDevicePath   OPTIONAL,
502   IN CONST EFI_DEVICE_PATH_PROTOCOL  *SecondDevicePath  OPTIONAL
503   )
504 {
505   UINTN                     Size;
506   UINTN                     Size1;
507   UINTN                     Size2;
508   EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;
509   EFI_DEVICE_PATH_PROTOCOL  *DevicePath2;
510 
511   //
512   // If there's only 1 path, just duplicate it.
513   //
514   if (FirstDevicePath == NULL) {
515     return DuplicateDevicePath ((SecondDevicePath != NULL) ? SecondDevicePath : &mUefiDevicePathLibEndDevicePath);
516   }
517 
518   if (SecondDevicePath == NULL) {
519     return DuplicateDevicePath (FirstDevicePath);
520   }
521 
522   if (!IsDevicePathValid (FirstDevicePath, 0) || !IsDevicePathValid (SecondDevicePath, 0)) {
523     return NULL;
524   }
525 
526   //
527   // Allocate space for the combined device path. It only has one end node of
528   // length EFI_DEVICE_PATH_PROTOCOL.
529   //
530   Size1 = GetDevicePathSize (FirstDevicePath);
531   Size2 = GetDevicePathSize (SecondDevicePath);
532   Size  = Size1 + Size2 - END_DEVICE_PATH_LENGTH;
533 
534   NewDevicePath = AllocatePool (Size);
535 
536   if (NewDevicePath != NULL) {
537     NewDevicePath = CopyMem (NewDevicePath, FirstDevicePath, Size1);
538     //
539     // Over write FirstDevicePath EndNode and do the copy
540     //
541     DevicePath2 = (EFI_DEVICE_PATH_PROTOCOL *)((CHAR8 *)NewDevicePath +
542                                                (Size1 - END_DEVICE_PATH_LENGTH));
543     CopyMem (DevicePath2, SecondDevicePath, Size2);
544   }
545 
546   return NewDevicePath;
547 }
548 
549 /**
550   Creates a new path by appending the device node to the device path.
551 
552   This function creates a new device path by appending a copy of the device node
553   specified by DevicePathNode to a copy of the device path specified by DevicePath
554   in an allocated buffer. The end-of-device-path device node is moved after the
555   end of the appended device node.
556   If DevicePathNode is NULL then a copy of DevicePath is returned.
557   If DevicePath is NULL then a copy of DevicePathNode, followed by an end-of-device
558   path device node is returned.
559   If both DevicePathNode and DevicePath are NULL then a copy of an end-of-device-path
560   device node is returned.
561   If there is not enough memory to allocate space for the new device path, then
562   NULL is returned.
563   The memory is allocated from EFI boot services memory. It is the responsibility
564   of the caller to free the memory allocated.
565 
566   @param  DevicePath                 A pointer to a device path data structure.
567   @param  DevicePathNode             A pointer to a single device path node.
568 
569   @retval NULL      If there is not enough memory for the new device path.
570   @retval Others    A pointer to the new device path if success.
571                     A copy of DevicePathNode followed by an end-of-device-path node
572                     if both FirstDevicePath and SecondDevicePath are NULL.
573                     A copy of an end-of-device-path node if both FirstDevicePath
574                     and SecondDevicePath are NULL.
575 
576 **/
577 EFI_DEVICE_PATH_PROTOCOL *
578 EFIAPI
AppendDevicePathNode(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath OPTIONAL,IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePathNode OPTIONAL)579 AppendDevicePathNode (
580   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath      OPTIONAL,
581   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePathNode  OPTIONAL
582   )
583 {
584   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath;
585   EFI_DEVICE_PATH_PROTOCOL  *NextNode;
586   EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;
587   UINTN                     NodeLength;
588 
589   if (DevicePathNode == NULL) {
590     return DuplicateDevicePath ((DevicePath != NULL) ? DevicePath : &mUefiDevicePathLibEndDevicePath);
591   }
592 
593   //
594   // Build a Node that has a terminator on it
595   //
596   NodeLength = DevicePathNodeLength (DevicePathNode);
597 
598   TempDevicePath = AllocatePool (NodeLength + END_DEVICE_PATH_LENGTH);
599   if (TempDevicePath == NULL) {
600     return NULL;
601   }
602 
603   TempDevicePath = CopyMem (TempDevicePath, DevicePathNode, NodeLength);
604   //
605   // Add and end device path node to convert Node to device path
606   //
607   NextNode = NextDevicePathNode (TempDevicePath);
608   SetDevicePathEndNode (NextNode);
609   //
610   // Append device paths
611   //
612   NewDevicePath = AppendDevicePath (DevicePath, TempDevicePath);
613 
614   FreePool (TempDevicePath);
615 
616   return NewDevicePath;
617 }
618 
619 #ifndef __FreeBSD__
620 /**
621   Creates a new device path by appending the specified device path instance to the specified device
622   path.
623 
624   This function creates a new device path by appending a copy of the device path
625   instance specified by DevicePathInstance to a copy of the device path specified
626   by DevicePath in a allocated buffer.
627   The end-of-device-path device node is moved after the end of the appended device
628   path instance and a new end-of-device-path-instance node is inserted between.
629   If DevicePath is NULL, then a copy if DevicePathInstance is returned.
630   If DevicePathInstance is NULL, then NULL is returned.
631   If DevicePath or DevicePathInstance is invalid, then NULL is returned.
632   If there is not enough memory to allocate space for the new device path, then
633   NULL is returned.
634   The memory is allocated from EFI boot services memory. It is the responsibility
635   of the caller to free the memory allocated.
636 
637   @param  DevicePath                 A pointer to a device path data structure.
638   @param  DevicePathInstance         A pointer to a device path instance.
639 
640   @return A pointer to the new device path.
641 
642 **/
643 EFI_DEVICE_PATH_PROTOCOL *
644 EFIAPI
UefiDevicePathLibAppendDevicePathInstance(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath OPTIONAL,IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePathInstance OPTIONAL)645 UefiDevicePathLibAppendDevicePathInstance (
646   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath         OPTIONAL,
647   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePathInstance OPTIONAL
648   )
649 {
650   EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;
651   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath;
652   UINTN                     SrcSize;
653   UINTN                     InstanceSize;
654 
655   if (DevicePath == NULL) {
656     return DuplicateDevicePath (DevicePathInstance);
657   }
658 
659   if (DevicePathInstance == NULL) {
660     return NULL;
661   }
662 
663   if (!IsDevicePathValid (DevicePath, 0) || !IsDevicePathValid (DevicePathInstance, 0)) {
664     return NULL;
665   }
666 
667   SrcSize      = GetDevicePathSize (DevicePath);
668   InstanceSize = GetDevicePathSize (DevicePathInstance);
669 
670   NewDevicePath = AllocatePool (SrcSize + InstanceSize);
671   if (NewDevicePath != NULL) {
672     TempDevicePath = CopyMem (NewDevicePath, DevicePath, SrcSize);
673 
674     while (!IsDevicePathEnd (TempDevicePath)) {
675       TempDevicePath = NextDevicePathNode (TempDevicePath);
676     }
677 
678     TempDevicePath->SubType = END_INSTANCE_DEVICE_PATH_SUBTYPE;
679     TempDevicePath          = NextDevicePathNode (TempDevicePath);
680     CopyMem (TempDevicePath, DevicePathInstance, InstanceSize);
681   }
682 
683   return NewDevicePath;
684 }
685 
686 /**
687   Creates a copy of the current device path instance and returns a pointer to the next device path
688   instance.
689 
690   This function creates a copy of the current device path instance. It also updates
691   DevicePath to point to the next device path instance in the device path (or NULL
692   if no more) and updates Size to hold the size of the device path instance copy.
693   If DevicePath is NULL, then NULL is returned.
694   If DevicePath points to a invalid device path, then NULL is returned.
695   If there is not enough memory to allocate space for the new device path, then
696   NULL is returned.
697   The memory is allocated from EFI boot services memory. It is the responsibility
698   of the caller to free the memory allocated.
699   If Size is NULL, then ASSERT().
700 
701   @param  DevicePath                 On input, this holds the pointer to the current
702                                      device path instance. On output, this holds
703                                      the pointer to the next device path instance
704                                      or NULL if there are no more device path
705                                      instances in the device path pointer to a
706                                      device path data structure.
707   @param  Size                       On output, this holds the size of the device
708                                      path instance, in bytes or zero, if DevicePath
709                                      is NULL.
710 
711   @return A pointer to the current device path instance.
712 
713 **/
714 EFI_DEVICE_PATH_PROTOCOL *
715 EFIAPI
UefiDevicePathLibGetNextDevicePathInstance(IN OUT EFI_DEVICE_PATH_PROTOCOL ** DevicePath,OUT UINTN * Size)716 UefiDevicePathLibGetNextDevicePathInstance (
717   IN OUT EFI_DEVICE_PATH_PROTOCOL  **DevicePath,
718   OUT UINTN                        *Size
719   )
720 {
721   EFI_DEVICE_PATH_PROTOCOL  *DevPath;
722   EFI_DEVICE_PATH_PROTOCOL  *ReturnValue;
723   UINT8                     Temp;
724 
725   ASSERT (Size != NULL);
726 
727   if ((DevicePath == NULL) || (*DevicePath == NULL)) {
728     *Size = 0;
729     return NULL;
730   }
731 
732   if (!IsDevicePathValid (*DevicePath, 0)) {
733     return NULL;
734   }
735 
736   //
737   // Find the end of the device path instance
738   //
739   DevPath = *DevicePath;
740   while (!IsDevicePathEndType (DevPath)) {
741     DevPath = NextDevicePathNode (DevPath);
742   }
743 
744   //
745   // Compute the size of the device path instance
746   //
747   *Size = ((UINTN)DevPath - (UINTN)(*DevicePath)) + sizeof (EFI_DEVICE_PATH_PROTOCOL);
748 
749   //
750   // Make a copy and return the device path instance
751   //
752   Temp             = DevPath->SubType;
753   DevPath->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
754   ReturnValue      = DuplicateDevicePath (*DevicePath);
755   DevPath->SubType = Temp;
756 
757   //
758   // If DevPath is the end of an entire device path, then another instance
759   // does not follow, so *DevicePath is set to NULL.
760   //
761   if (DevicePathSubType (DevPath) == END_ENTIRE_DEVICE_PATH_SUBTYPE) {
762     *DevicePath = NULL;
763   } else {
764     *DevicePath = NextDevicePathNode (DevPath);
765   }
766 
767   return ReturnValue;
768 }
769 #endif
770 
771 /**
772   Creates a device node.
773 
774   This function creates a new device node in a newly allocated buffer of size
775   NodeLength and initializes the device path node header with NodeType and NodeSubType.
776   The new device path node is returned.
777   If NodeLength is smaller than a device path header, then NULL is returned.
778   If there is not enough memory to allocate space for the new device path, then
779   NULL is returned.
780   The memory is allocated from EFI boot services memory. It is the responsibility
781   of the caller to free the memory allocated.
782 
783   @param  NodeType                   The device node type for the new device node.
784   @param  NodeSubType                The device node sub-type for the new device node.
785   @param  NodeLength                 The length of the new device node.
786 
787   @return The new device path.
788 
789 **/
790 EFI_DEVICE_PATH_PROTOCOL *
791 EFIAPI
CreateDeviceNode(IN UINT8 NodeType,IN UINT8 NodeSubType,IN UINT16 NodeLength)792 CreateDeviceNode (
793   IN UINT8   NodeType,
794   IN UINT8   NodeSubType,
795   IN UINT16  NodeLength
796   )
797 {
798   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
799 
800   if (NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
801     //
802     // NodeLength is less than the size of the header.
803     //
804     return NULL;
805   }
806 
807   DevicePath = AllocateZeroPool (NodeLength);
808   if (DevicePath != NULL) {
809     DevicePath->Type    = NodeType;
810     DevicePath->SubType = NodeSubType;
811     SetDevicePathNodeLength (DevicePath, NodeLength);
812   }
813 
814   return DevicePath;
815 }
816 
817 #ifndef __FreeBSD__
818 /**
819   Determines if a device path is single or multi-instance.
820 
821   This function returns TRUE if the device path specified by DevicePath is
822   multi-instance.
823   Otherwise, FALSE is returned.
824   If DevicePath is NULL or invalid, then FALSE is returned.
825 
826   @param  DevicePath                 A pointer to a device path data structure.
827 
828   @retval  TRUE                      DevicePath is multi-instance.
829   @retval  FALSE                     DevicePath is not multi-instance, or DevicePath
830                                      is NULL or invalid.
831 
832 **/
833 BOOLEAN
834 EFIAPI
UefiDevicePathLibIsDevicePathMultiInstance(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath)835 UefiDevicePathLibIsDevicePathMultiInstance (
836   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath
837   )
838 {
839   CONST EFI_DEVICE_PATH_PROTOCOL  *Node;
840 
841   if (DevicePath == NULL) {
842     return FALSE;
843   }
844 
845   if (!IsDevicePathValid (DevicePath, 0)) {
846     return FALSE;
847   }
848 
849   Node = DevicePath;
850   while (!IsDevicePathEnd (Node)) {
851     if (IsDevicePathEndInstance (Node)) {
852       return TRUE;
853     }
854 
855     Node = NextDevicePathNode (Node);
856   }
857 
858   return FALSE;
859 }
860 
861 /**
862   Allocates a device path for a file and appends it to an existing device path.
863 
864   If Device is a valid device handle that contains a device path protocol, then a device path for
865   the file specified by FileName  is allocated and appended to the device path associated with the
866   handle Device.  The allocated device path is returned.  If Device is NULL or Device is a handle
867   that does not support the device path protocol, then a device path containing a single device
868   path node for the file specified by FileName is allocated and returned.
869   The memory for the new device path is allocated from EFI boot services memory. It is the responsibility
870   of the caller to free the memory allocated.
871 
872   If FileName is NULL, then ASSERT().
873   If FileName is not aligned on a 16-bit boundary, then ASSERT().
874 
875   @param  Device                     A pointer to a device handle.  This parameter
876                                      is optional and may be NULL.
877   @param  FileName                   A pointer to a Null-terminated Unicode string.
878 
879   @return The allocated device path.
880 
881 **/
882 EFI_DEVICE_PATH_PROTOCOL *
883 EFIAPI
FileDevicePath(IN EFI_HANDLE Device OPTIONAL,IN CONST CHAR16 * FileName)884 FileDevicePath (
885   IN EFI_HANDLE    Device      OPTIONAL,
886   IN CONST CHAR16  *FileName
887   )
888 {
889   UINTN                     Size;
890   FILEPATH_DEVICE_PATH      *FilePath;
891   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
892   EFI_DEVICE_PATH_PROTOCOL  *FileDevicePath;
893 
894   DevicePath = NULL;
895 
896   Size           = StrSize (FileName);
897   FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + END_DEVICE_PATH_LENGTH);
898   if (FileDevicePath != NULL) {
899     FilePath                 = (FILEPATH_DEVICE_PATH *)FileDevicePath;
900     FilePath->Header.Type    = MEDIA_DEVICE_PATH;
901     FilePath->Header.SubType = MEDIA_FILEPATH_DP;
902     CopyMem (&FilePath->PathName, FileName, Size);
903     SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH);
904     SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header));
905 
906     if (Device != NULL) {
907       DevicePath = DevicePathFromHandle (Device);
908     }
909 
910     DevicePath = AppendDevicePath (DevicePath, FileDevicePath);
911     FreePool (FileDevicePath);
912   }
913 
914   return DevicePath;
915 }
916 #endif
917