xref: /freebsd/lib/libefivar/uefi-dputil.c (revision 1d386b48)
17270962aSWarner Losh /*-
27270962aSWarner Losh  * Copyright (c) 2017 Netflix, Inc.
37270962aSWarner Losh  *
47270962aSWarner Losh  * Redistribution and use in source and binary forms, with or without
57270962aSWarner Losh  * modification, are permitted provided that the following conditions
67270962aSWarner Losh  * are met:
77270962aSWarner Losh  * 1. Redistributions of source code must retain the above copyright
86decf2ccSEd Maste  *    notice, this list of conditions and the following disclaimer.
97270962aSWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
107270962aSWarner Losh  *    notice, this list of conditions and the following disclaimer in the
117270962aSWarner Losh  *    documentation and/or other materials provided with the distribution.
127270962aSWarner Losh  *
136decf2ccSEd Maste  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
146decf2ccSEd Maste  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
156decf2ccSEd Maste  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
166decf2ccSEd Maste  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
176decf2ccSEd Maste  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
186decf2ccSEd Maste  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
196decf2ccSEd Maste  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
206decf2ccSEd Maste  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
216decf2ccSEd Maste  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
226decf2ccSEd Maste  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
236decf2ccSEd Maste  * SUCH DAMAGE.
247270962aSWarner Losh  */
257270962aSWarner Losh 
267270962aSWarner Losh /*
277270962aSWarner Losh  * Routines to format EFI_DEVICE_PATHs from the UEFI standard. Much of
287270962aSWarner Losh  * this file is taken from EDK2 and rototilled.
297270962aSWarner Losh  */
307270962aSWarner Losh 
317270962aSWarner Losh #include <sys/cdefs.h>
327270962aSWarner Losh #include <efivar.h>
337270962aSWarner Losh #include <limits.h>
347270962aSWarner Losh #include <stdio.h>
357270962aSWarner Losh #include <string.h>
367270962aSWarner Losh #include <sys/endian.h>
377270962aSWarner Losh 
387270962aSWarner Losh #include "efi-osdep.h"
397270962aSWarner Losh 
407270962aSWarner Losh #include "uefi-dplib.h"
417270962aSWarner Losh 
424c69a17fSWarner Losh /* XXX maybe I should include the entire DevicePathUtiltiies.c and ifdef out what we don't use */
437270962aSWarner Losh 
447270962aSWarner Losh /*
457270962aSWarner Losh  * Taken from MdePkg/Library/UefiDevicePathLib/DevicePathUtilities.c
467270962aSWarner Losh  * hash a11928f3310518ab1c6fd34e8d0fdbb72de9602c 2017-Mar-01
477270962aSWarner Losh  */
487270962aSWarner Losh 
497270962aSWarner Losh /** @file
507270962aSWarner Losh   Device Path services. The thing to remember is device paths are built out of
517270962aSWarner Losh   nodes. The device path is terminated by an end node that is length
527270962aSWarner Losh   sizeof(EFI_DEVICE_PATH_PROTOCOL). That would be why there is sizeof(EFI_DEVICE_PATH_PROTOCOL)
537270962aSWarner Losh   all over this file.
547270962aSWarner Losh 
557270962aSWarner Losh   The only place where multi-instance device paths are supported is in
567270962aSWarner Losh   environment varibles. Multi-instance device paths should never be placed
577270962aSWarner Losh   on a Handle.
587270962aSWarner Losh 
597270962aSWarner Losh   Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
607270962aSWarner Losh   This program and the accompanying materials
617270962aSWarner Losh   are licensed and made available under the terms and conditions of the BSD License
627270962aSWarner Losh   which accompanies this distribution.  The full text of the license may be found at
637270962aSWarner Losh   http://opensource.org/licenses/bsd-license.php.
647270962aSWarner Losh 
657270962aSWarner Losh   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
667270962aSWarner Losh   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
677270962aSWarner Losh 
687270962aSWarner Losh **/
697270962aSWarner Losh 
707270962aSWarner Losh //
717270962aSWarner Losh // Template for an end-of-device path node.
727270962aSWarner Losh //
737270962aSWarner Losh static CONST EFI_DEVICE_PATH_PROTOCOL  mUefiDevicePathLibEndDevicePath = {
747270962aSWarner Losh   END_DEVICE_PATH_TYPE,
757270962aSWarner Losh   END_ENTIRE_DEVICE_PATH_SUBTYPE,
767270962aSWarner Losh   {
777270962aSWarner Losh     END_DEVICE_PATH_LENGTH,
787270962aSWarner Losh     0
797270962aSWarner Losh   }
807270962aSWarner Losh };
817270962aSWarner Losh 
827270962aSWarner Losh 
837270962aSWarner Losh /**
847270962aSWarner Losh   Returns the size of a device path in bytes.
857270962aSWarner Losh 
867270962aSWarner Losh   This function returns the size, in bytes, of the device path data structure
877270962aSWarner Losh   specified by DevicePath including the end of device path node.
887270962aSWarner Losh   If DevicePath is NULL or invalid, then 0 is returned.
897270962aSWarner Losh 
907270962aSWarner Losh   @param  DevicePath  A pointer to a device path data structure.
917270962aSWarner Losh 
927270962aSWarner Losh   @retval 0           If DevicePath is NULL or invalid.
937270962aSWarner Losh   @retval Others      The size of a device path in bytes.
947270962aSWarner Losh 
957270962aSWarner Losh **/
967270962aSWarner Losh UINTN
977270962aSWarner Losh EFIAPI
GetDevicePathSize(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath)987270962aSWarner Losh GetDevicePathSize (
997270962aSWarner Losh   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath
1007270962aSWarner Losh   )
1017270962aSWarner Losh {
1027270962aSWarner Losh   CONST EFI_DEVICE_PATH_PROTOCOL  *Start;
1037270962aSWarner Losh 
1047270962aSWarner Losh   if (DevicePath == NULL) {
1057270962aSWarner Losh     return 0;
1067270962aSWarner Losh   }
1077270962aSWarner Losh 
1087270962aSWarner Losh   if (!IsDevicePathValid (DevicePath, 0)) {
1097270962aSWarner Losh     return 0;
1107270962aSWarner Losh   }
1117270962aSWarner Losh 
1127270962aSWarner Losh   //
1137270962aSWarner Losh   // Search for the end of the device path structure
1147270962aSWarner Losh   //
1157270962aSWarner Losh   Start = DevicePath;
1167270962aSWarner Losh   while (!IsDevicePathEnd (DevicePath)) {
1177270962aSWarner Losh     DevicePath = NextDevicePathNode (DevicePath);
1187270962aSWarner Losh   }
1197270962aSWarner Losh 
1207270962aSWarner Losh   //
1217270962aSWarner Losh   // Compute the size and add back in the size of the end device path structure
1227270962aSWarner Losh   //
1237270962aSWarner Losh   return ((UINTN) DevicePath - (UINTN) Start) + DevicePathNodeLength (DevicePath);
1247270962aSWarner Losh }
1257270962aSWarner Losh 
1267270962aSWarner Losh /**
1277270962aSWarner Losh   Determine whether a given device path is valid.
1287270962aSWarner Losh   If DevicePath is NULL, then ASSERT().
1297270962aSWarner Losh 
1307270962aSWarner Losh   @param  DevicePath  A pointer to a device path data structure.
1317270962aSWarner Losh   @param  MaxSize     The maximum size of the device path data structure.
1327270962aSWarner Losh 
1337270962aSWarner Losh   @retval TRUE        DevicePath is valid.
134d52a982eSEitan Adler   @retval FALSE       The length of any node in the DevicePath is less
1357270962aSWarner Losh                       than sizeof (EFI_DEVICE_PATH_PROTOCOL).
1367270962aSWarner Losh   @retval FALSE       If MaxSize is not zero, the size of the DevicePath
1377270962aSWarner Losh                       exceeds MaxSize.
1387270962aSWarner Losh   @retval FALSE       If PcdMaximumDevicePathNodeCount is not zero, the node
1397270962aSWarner Losh                       count of the DevicePath exceeds PcdMaximumDevicePathNodeCount.
1407270962aSWarner Losh **/
1417270962aSWarner Losh BOOLEAN
1427270962aSWarner Losh EFIAPI
IsDevicePathValid(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath,IN UINTN MaxSize)1437270962aSWarner Losh IsDevicePathValid (
1447270962aSWarner Losh   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
1457270962aSWarner Losh   IN       UINTN                    MaxSize
1467270962aSWarner Losh   )
1477270962aSWarner Losh {
1487270962aSWarner Losh   UINTN Count;
1497270962aSWarner Losh   UINTN Size;
1507270962aSWarner Losh   UINTN NodeLength;
1517270962aSWarner Losh 
1527270962aSWarner Losh   ASSERT (DevicePath != NULL);
1537270962aSWarner Losh 
1547270962aSWarner Losh   if (MaxSize == 0) {
1557270962aSWarner Losh     MaxSize = MAX_UINTN;
1567270962aSWarner Losh   }
1577270962aSWarner Losh 
1587270962aSWarner Losh   //
1597270962aSWarner Losh   // Validate the input size big enough to touch the first node.
1607270962aSWarner Losh   //
1617270962aSWarner Losh   if (MaxSize < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
1627270962aSWarner Losh     return FALSE;
1637270962aSWarner Losh   }
1647270962aSWarner Losh 
1657270962aSWarner Losh   for (Count = 0, Size = 0; !IsDevicePathEnd (DevicePath); DevicePath = NextDevicePathNode (DevicePath)) {
1667270962aSWarner Losh     NodeLength = DevicePathNodeLength (DevicePath);
1677270962aSWarner Losh     if (NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
1687270962aSWarner Losh       return FALSE;
1697270962aSWarner Losh     }
1707270962aSWarner Losh 
1717270962aSWarner Losh     if (NodeLength > MAX_UINTN - Size) {
1727270962aSWarner Losh       return FALSE;
1737270962aSWarner Losh     }
1747270962aSWarner Losh     Size += NodeLength;
1757270962aSWarner Losh 
1767270962aSWarner Losh     //
1777270962aSWarner Losh     // Validate next node before touch it.
1787270962aSWarner Losh     //
1797270962aSWarner Losh     if (Size > MaxSize - END_DEVICE_PATH_LENGTH ) {
1807270962aSWarner Losh       return FALSE;
1817270962aSWarner Losh     }
1827270962aSWarner Losh 
1837270962aSWarner Losh     if (PcdGet32 (PcdMaximumDevicePathNodeCount) > 0) {
1847270962aSWarner Losh       Count++;
1857270962aSWarner Losh       if (Count >= PcdGet32 (PcdMaximumDevicePathNodeCount)) {
1867270962aSWarner Losh         return FALSE;
1877270962aSWarner Losh       }
1887270962aSWarner Losh     }
1897270962aSWarner Losh   }
1907270962aSWarner Losh 
1917270962aSWarner Losh   //
1927270962aSWarner Losh   // Only return TRUE when the End Device Path node is valid.
1937270962aSWarner Losh   //
1947270962aSWarner Losh   return (BOOLEAN) (DevicePathNodeLength (DevicePath) == END_DEVICE_PATH_LENGTH);
1957270962aSWarner Losh }
1967270962aSWarner Losh 
1977270962aSWarner Losh /**
1987270962aSWarner Losh   Returns the Type field of a device path node.
1997270962aSWarner Losh 
2007270962aSWarner Losh   Returns the Type field of the device path node specified by Node.
2017270962aSWarner Losh 
2027270962aSWarner Losh   If Node is NULL, then ASSERT().
2037270962aSWarner Losh 
2047270962aSWarner Losh   @param  Node      A pointer to a device path node data structure.
2057270962aSWarner Losh 
2067270962aSWarner Losh   @return The Type field of the device path node specified by Node.
2077270962aSWarner Losh 
2087270962aSWarner Losh **/
2097270962aSWarner Losh UINT8
2107270962aSWarner Losh EFIAPI
DevicePathType(IN CONST VOID * Node)2117270962aSWarner Losh DevicePathType (
2127270962aSWarner Losh   IN CONST VOID  *Node
2137270962aSWarner Losh   )
2147270962aSWarner Losh {
2157270962aSWarner Losh   ASSERT (Node != NULL);
2167270962aSWarner Losh   return ((const EFI_DEVICE_PATH_PROTOCOL *)(Node))->Type;
2177270962aSWarner Losh }
2187270962aSWarner Losh 
2197270962aSWarner Losh 
2207270962aSWarner Losh /**
2217270962aSWarner Losh   Returns the SubType field of a device path node.
2227270962aSWarner Losh 
2237270962aSWarner Losh   Returns the SubType field of the device path node specified by Node.
2247270962aSWarner Losh 
2257270962aSWarner Losh   If Node is NULL, then ASSERT().
2267270962aSWarner Losh 
2277270962aSWarner Losh   @param  Node      A pointer to a device path node data structure.
2287270962aSWarner Losh 
2297270962aSWarner Losh   @return The SubType field of the device path node specified by Node.
2307270962aSWarner Losh 
2317270962aSWarner Losh **/
2327270962aSWarner Losh UINT8
2337270962aSWarner Losh EFIAPI
DevicePathSubType(IN CONST VOID * Node)2347270962aSWarner Losh DevicePathSubType (
2357270962aSWarner Losh   IN CONST VOID  *Node
2367270962aSWarner Losh   )
2377270962aSWarner Losh {
2387270962aSWarner Losh   ASSERT (Node != NULL);
2397270962aSWarner Losh   return ((const EFI_DEVICE_PATH_PROTOCOL *)(Node))->SubType;
2407270962aSWarner Losh }
2417270962aSWarner Losh 
2427270962aSWarner Losh /**
2437270962aSWarner Losh   Returns the 16-bit Length field of a device path node.
2447270962aSWarner Losh 
2457270962aSWarner Losh   Returns the 16-bit Length field of the device path node specified by Node.
2467270962aSWarner Losh   Node is not required to be aligned on a 16-bit boundary, so it is recommended
2477270962aSWarner Losh   that a function such as ReadUnaligned16() be used to extract the contents of
2487270962aSWarner Losh   the Length field.
2497270962aSWarner Losh 
2507270962aSWarner Losh   If Node is NULL, then ASSERT().
2517270962aSWarner Losh 
2527270962aSWarner Losh   @param  Node      A pointer to a device path node data structure.
2537270962aSWarner Losh 
2547270962aSWarner Losh   @return The 16-bit Length field of the device path node specified by Node.
2557270962aSWarner Losh 
2567270962aSWarner Losh **/
2577270962aSWarner Losh UINTN
2587270962aSWarner Losh EFIAPI
DevicePathNodeLength(IN CONST VOID * Node)2597270962aSWarner Losh DevicePathNodeLength (
2607270962aSWarner Losh   IN CONST VOID  *Node
2617270962aSWarner Losh   )
2627270962aSWarner Losh {
2637270962aSWarner Losh   ASSERT (Node != NULL);
2647270962aSWarner Losh   return ((const EFI_DEVICE_PATH_PROTOCOL *)Node)->Length[0] |
2657270962aSWarner Losh       (((const EFI_DEVICE_PATH_PROTOCOL *)Node)->Length[1] << 8);
2667270962aSWarner Losh }
2677270962aSWarner Losh 
2687270962aSWarner Losh /**
2697270962aSWarner Losh   Returns a pointer to the next node in a device path.
2707270962aSWarner Losh 
2717270962aSWarner Losh   Returns a pointer to the device path node that follows the device path node
2727270962aSWarner Losh   specified by Node.
2737270962aSWarner Losh 
2747270962aSWarner Losh   If Node is NULL, then ASSERT().
2757270962aSWarner Losh 
2767270962aSWarner Losh   @param  Node      A pointer to a device path node data structure.
2777270962aSWarner Losh 
2787270962aSWarner Losh   @return a pointer to the device path node that follows the device path node
2797270962aSWarner Losh   specified by Node.
2807270962aSWarner Losh 
2817270962aSWarner Losh **/
2827270962aSWarner Losh EFI_DEVICE_PATH_PROTOCOL *
2837270962aSWarner Losh EFIAPI
NextDevicePathNode(IN CONST VOID * Node)2847270962aSWarner Losh NextDevicePathNode (
2857270962aSWarner Losh   IN CONST VOID  *Node
2867270962aSWarner Losh   )
2877270962aSWarner Losh {
2887270962aSWarner Losh   ASSERT (Node != NULL);
2897270962aSWarner Losh   return ((EFI_DEVICE_PATH_PROTOCOL *)(__DECONST(UINT8 *, Node) + DevicePathNodeLength(Node)));
2907270962aSWarner Losh }
2917270962aSWarner Losh 
2927270962aSWarner Losh /**
2937270962aSWarner Losh   Determines if a device path node is an end node of a device path.
2947270962aSWarner Losh   This includes nodes that are the end of a device path instance and nodes that
2957270962aSWarner Losh   are the end of an entire device path.
2967270962aSWarner Losh 
2977270962aSWarner Losh   Determines if the device path node specified by Node is an end node of a device path.
2987270962aSWarner Losh   This includes nodes that are the end of a device path instance and nodes that are the
2997270962aSWarner Losh   end of an entire device path.  If Node represents an end node of a device path,
3007270962aSWarner Losh   then TRUE is returned.  Otherwise, FALSE is returned.
3017270962aSWarner Losh 
3027270962aSWarner Losh   If Node is NULL, then ASSERT().
3037270962aSWarner Losh 
3047270962aSWarner Losh   @param  Node      A pointer to a device path node data structure.
3057270962aSWarner Losh 
3067270962aSWarner Losh   @retval TRUE      The device path node specified by Node is an end node of a
3077270962aSWarner Losh                     device path.
3087270962aSWarner Losh   @retval FALSE     The device path node specified by Node is not an end node of
3097270962aSWarner Losh                     a device path.
3107270962aSWarner Losh 
3117270962aSWarner Losh **/
3127270962aSWarner Losh BOOLEAN
3137270962aSWarner Losh EFIAPI
IsDevicePathEndType(IN CONST VOID * Node)3147270962aSWarner Losh IsDevicePathEndType (
3157270962aSWarner Losh   IN CONST VOID  *Node
3167270962aSWarner Losh   )
3177270962aSWarner Losh {
3187270962aSWarner Losh   ASSERT (Node != NULL);
3197270962aSWarner Losh   return (BOOLEAN) (DevicePathType (Node) == END_DEVICE_PATH_TYPE);
3207270962aSWarner Losh }
3217270962aSWarner Losh 
3227270962aSWarner Losh /**
3237270962aSWarner Losh   Determines if a device path node is an end node of an entire device path.
3247270962aSWarner Losh 
3257270962aSWarner Losh   Determines if a device path node specified by Node is an end node of an entire
3267270962aSWarner Losh   device path. If Node represents the end of an entire device path, then TRUE is
3277270962aSWarner Losh   returned.  Otherwise, FALSE is returned.
3287270962aSWarner Losh 
3297270962aSWarner Losh   If Node is NULL, then ASSERT().
3307270962aSWarner Losh 
3317270962aSWarner Losh   @param  Node      A pointer to a device path node data structure.
3327270962aSWarner Losh 
3337270962aSWarner Losh   @retval TRUE      The device path node specified by Node is the end of an entire
3347270962aSWarner Losh                     device path.
3357270962aSWarner Losh   @retval FALSE     The device path node specified by Node is not the end of an
3367270962aSWarner Losh                     entire device path.
3377270962aSWarner Losh 
3387270962aSWarner Losh **/
3397270962aSWarner Losh BOOLEAN
3407270962aSWarner Losh EFIAPI
IsDevicePathEnd(IN CONST VOID * Node)3417270962aSWarner Losh IsDevicePathEnd (
3427270962aSWarner Losh   IN CONST VOID  *Node
3437270962aSWarner Losh   )
3447270962aSWarner Losh {
3457270962aSWarner Losh   ASSERT (Node != NULL);
3467270962aSWarner Losh   return (BOOLEAN) (IsDevicePathEndType (Node) && DevicePathSubType(Node) == END_ENTIRE_DEVICE_PATH_SUBTYPE);
3477270962aSWarner Losh }
3487270962aSWarner Losh 
3497270962aSWarner Losh /**
3507270962aSWarner Losh   Fills in all the fields of a device path node that is the end of an entire device path.
3517270962aSWarner Losh 
3527270962aSWarner Losh   Fills in all the fields of a device path node specified by Node so Node represents
3537270962aSWarner Losh   the end of an entire device path.  The Type field of Node is set to
3547270962aSWarner Losh   END_DEVICE_PATH_TYPE, the SubType field of Node is set to
3557270962aSWarner Losh   END_ENTIRE_DEVICE_PATH_SUBTYPE, and the Length field of Node is set to
3567270962aSWarner Losh   END_DEVICE_PATH_LENGTH.  Node is not required to be aligned on a 16-bit boundary,
3577270962aSWarner Losh   so it is recommended that a function such as WriteUnaligned16() be used to set
3587270962aSWarner Losh   the contents of the Length field.
3597270962aSWarner Losh 
3607270962aSWarner Losh   If Node is NULL, then ASSERT().
3617270962aSWarner Losh 
3627270962aSWarner Losh   @param  Node      A pointer to a device path node data structure.
3637270962aSWarner Losh 
3647270962aSWarner Losh **/
3657270962aSWarner Losh VOID
3667270962aSWarner Losh EFIAPI
SetDevicePathEndNode(OUT VOID * Node)3677270962aSWarner Losh SetDevicePathEndNode (
3687270962aSWarner Losh   OUT VOID  *Node
3697270962aSWarner Losh   )
3707270962aSWarner Losh {
3717270962aSWarner Losh   ASSERT (Node != NULL);
3727270962aSWarner Losh   memcpy (Node, &mUefiDevicePathLibEndDevicePath, sizeof (mUefiDevicePathLibEndDevicePath));
3737270962aSWarner Losh }
3747270962aSWarner Losh 
3757270962aSWarner Losh /**
3767270962aSWarner Losh   Sets the length, in bytes, of a device path node.
3777270962aSWarner Losh 
3787270962aSWarner Losh   Sets the length of the device path node specified by Node to the value specified
3797270962aSWarner Losh   by NodeLength.  NodeLength is returned.  Node is not required to be aligned on
3807270962aSWarner Losh   a 16-bit boundary, so it is recommended that a function such as WriteUnaligned16()
3817270962aSWarner Losh   be used to set the contents of the Length field.
3827270962aSWarner Losh 
3837270962aSWarner Losh   If Node is NULL, then ASSERT().
3847270962aSWarner Losh   If NodeLength >= SIZE_64KB, then ASSERT().
3857270962aSWarner Losh   If NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL), then ASSERT().
3867270962aSWarner Losh 
3877270962aSWarner Losh   @param  Node      A pointer to a device path node data structure.
3887270962aSWarner Losh   @param  Length    The length, in bytes, of the device path node.
3897270962aSWarner Losh 
3907270962aSWarner Losh   @return Length
3917270962aSWarner Losh 
3927270962aSWarner Losh **/
3937270962aSWarner Losh UINT16
3947270962aSWarner Losh EFIAPI
SetDevicePathNodeLength(IN OUT VOID * Node,IN UINTN Length)3957270962aSWarner Losh SetDevicePathNodeLength (
3967270962aSWarner Losh   IN OUT VOID  *Node,
3977270962aSWarner Losh   IN UINTN     Length
3987270962aSWarner Losh   )
3997270962aSWarner Losh {
4007270962aSWarner Losh   ASSERT (Node != NULL);
4017270962aSWarner Losh   ASSERT ((Length >= sizeof (EFI_DEVICE_PATH_PROTOCOL)) && (Length < SIZE_64KB));
4027270962aSWarner Losh //  return WriteUnaligned16 ((UINT16 *)&((EFI_DEVICE_PATH_PROTOCOL *)(Node))->Length[0], (UINT16)(Length));
4037270962aSWarner Losh   le16enc(&((EFI_DEVICE_PATH_PROTOCOL *)(Node))->Length[0], (UINT16)(Length));
4047270962aSWarner Losh   return Length;
4057270962aSWarner Losh }
4067270962aSWarner Losh 
4077270962aSWarner Losh /**
4087270962aSWarner Losh   Creates a device node.
4097270962aSWarner Losh 
4107270962aSWarner Losh   This function creates a new device node in a newly allocated buffer of size
4117270962aSWarner Losh   NodeLength and initializes the device path node header with NodeType and NodeSubType.
4127270962aSWarner Losh   The new device path node is returned.
4137270962aSWarner Losh   If NodeLength is smaller than a device path header, then NULL is returned.
4147270962aSWarner Losh   If there is not enough memory to allocate space for the new device path, then
4157270962aSWarner Losh   NULL is returned.
4167270962aSWarner Losh   The memory is allocated from EFI boot services memory. It is the responsibility
4177270962aSWarner Losh   of the caller to free the memory allocated.
4187270962aSWarner Losh 
4197270962aSWarner Losh   @param  NodeType                   The device node type for the new device node.
4207270962aSWarner Losh   @param  NodeSubType                The device node sub-type for the new device node.
4217270962aSWarner Losh   @param  NodeLength                 The length of the new device node.
4227270962aSWarner Losh 
4237270962aSWarner Losh   @return The new device path.
4247270962aSWarner Losh 
4257270962aSWarner Losh **/
4267270962aSWarner Losh EFI_DEVICE_PATH_PROTOCOL *
4277270962aSWarner Losh EFIAPI
CreateDeviceNode(IN UINT8 NodeType,IN UINT8 NodeSubType,IN UINT16 NodeLength)4287270962aSWarner Losh CreateDeviceNode (
4297270962aSWarner Losh   IN UINT8                           NodeType,
4307270962aSWarner Losh   IN UINT8                           NodeSubType,
4317270962aSWarner Losh   IN UINT16                          NodeLength
4327270962aSWarner Losh   )
4337270962aSWarner Losh {
4347270962aSWarner Losh   EFI_DEVICE_PATH_PROTOCOL      *DevicePath;
4357270962aSWarner Losh 
4367270962aSWarner Losh   if (NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
4377270962aSWarner Losh     //
4387270962aSWarner Losh     // NodeLength is less than the size of the header.
4397270962aSWarner Losh     //
4407270962aSWarner Losh     return NULL;
4417270962aSWarner Losh   }
4427270962aSWarner Losh 
4437270962aSWarner Losh   DevicePath = AllocateZeroPool (NodeLength);
4447270962aSWarner Losh   if (DevicePath != NULL) {
4457270962aSWarner Losh      DevicePath->Type    = NodeType;
4467270962aSWarner Losh      DevicePath->SubType = NodeSubType;
4477270962aSWarner Losh      SetDevicePathNodeLength (DevicePath, NodeLength);
4487270962aSWarner Losh   }
4497270962aSWarner Losh 
4507270962aSWarner Losh   return DevicePath;
4517270962aSWarner Losh }
4527270962aSWarner Losh 
4537270962aSWarner Losh /**
4547270962aSWarner Losh   Creates a new copy of an existing device path.
4557270962aSWarner Losh 
4567270962aSWarner Losh   This function allocates space for a new copy of the device path specified by DevicePath.
4577270962aSWarner Losh   If DevicePath is NULL, then NULL is returned.  If the memory is successfully
4587270962aSWarner Losh   allocated, then the contents of DevicePath are copied to the newly allocated
4597270962aSWarner Losh   buffer, and a pointer to that buffer is returned.  Otherwise, NULL is returned.
4607270962aSWarner Losh   The memory for the new device path is allocated from EFI boot services memory.
4617270962aSWarner Losh   It is the responsibility of the caller to free the memory allocated.
4627270962aSWarner Losh 
4637270962aSWarner Losh   @param  DevicePath    A pointer to a device path data structure.
4647270962aSWarner Losh 
4657270962aSWarner Losh   @retval NULL          DevicePath is NULL or invalid.
4667270962aSWarner Losh   @retval Others        A pointer to the duplicated device path.
4677270962aSWarner Losh 
4687270962aSWarner Losh **/
4697270962aSWarner Losh EFI_DEVICE_PATH_PROTOCOL *
4707270962aSWarner Losh EFIAPI
DuplicateDevicePath(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath)4717270962aSWarner Losh DuplicateDevicePath (
4727270962aSWarner Losh   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath
4737270962aSWarner Losh   )
4747270962aSWarner Losh {
4757270962aSWarner Losh   UINTN                     Size;
4767270962aSWarner Losh 
4777270962aSWarner Losh   //
4787270962aSWarner Losh   // Compute the size
4797270962aSWarner Losh   //
4807270962aSWarner Losh   Size = GetDevicePathSize (DevicePath);
4817270962aSWarner Losh   if (Size == 0) {
4827270962aSWarner Losh     return NULL;
4837270962aSWarner Losh   }
4847270962aSWarner Losh 
4857270962aSWarner Losh   //
4867270962aSWarner Losh   // Allocate space for duplicate device path
4877270962aSWarner Losh   //
4887270962aSWarner Losh 
4897270962aSWarner Losh   return AllocateCopyPool (Size, DevicePath);
4907270962aSWarner Losh }
4917270962aSWarner Losh 
4927270962aSWarner Losh /**
4937270962aSWarner Losh   Creates a new device path by appending a second device path to a first device path.
4947270962aSWarner Losh 
4957270962aSWarner Losh   This function creates a new device path by appending a copy of SecondDevicePath
4967270962aSWarner Losh   to a copy of FirstDevicePath in a newly allocated buffer.  Only the end-of-device-path
4977270962aSWarner Losh   device node from SecondDevicePath is retained. The newly created device path is
4987270962aSWarner Losh   returned. If FirstDevicePath is NULL, then it is ignored, and a duplicate of
4997270962aSWarner Losh   SecondDevicePath is returned.  If SecondDevicePath is NULL, then it is ignored,
5007270962aSWarner Losh   and a duplicate of FirstDevicePath is returned. If both FirstDevicePath and
5017270962aSWarner Losh   SecondDevicePath are NULL, then a copy of an end-of-device-path is returned.
5027270962aSWarner Losh 
5037270962aSWarner Losh   If there is not enough memory for the newly allocated buffer, then NULL is returned.
5047270962aSWarner Losh   The memory for the new device path is allocated from EFI boot services memory.
5057270962aSWarner Losh   It is the responsibility of the caller to free the memory allocated.
5067270962aSWarner Losh 
5077270962aSWarner Losh   @param  FirstDevicePath            A pointer to a device path data structure.
5087270962aSWarner Losh   @param  SecondDevicePath           A pointer to a device path data structure.
5097270962aSWarner Losh 
5107270962aSWarner Losh   @retval NULL      If there is not enough memory for the newly allocated buffer.
5117270962aSWarner Losh   @retval NULL      If FirstDevicePath or SecondDevicePath is invalid.
5127270962aSWarner Losh   @retval Others    A pointer to the new device path if success.
5137270962aSWarner Losh                     Or a copy an end-of-device-path if both FirstDevicePath and SecondDevicePath are NULL.
5147270962aSWarner Losh 
5157270962aSWarner Losh **/
5167270962aSWarner Losh EFI_DEVICE_PATH_PROTOCOL *
5177270962aSWarner Losh EFIAPI
AppendDevicePath(IN CONST EFI_DEVICE_PATH_PROTOCOL * FirstDevicePath,OPTIONAL IN CONST EFI_DEVICE_PATH_PROTOCOL * SecondDevicePath OPTIONAL)5187270962aSWarner Losh AppendDevicePath (
5197270962aSWarner Losh   IN CONST EFI_DEVICE_PATH_PROTOCOL  *FirstDevicePath,  OPTIONAL
5207270962aSWarner Losh   IN CONST EFI_DEVICE_PATH_PROTOCOL  *SecondDevicePath  OPTIONAL
5217270962aSWarner Losh   )
5227270962aSWarner Losh {
5237270962aSWarner Losh   UINTN                     Size;
5247270962aSWarner Losh   UINTN                     Size1;
5257270962aSWarner Losh   UINTN                     Size2;
5267270962aSWarner Losh   EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;
5277270962aSWarner Losh   EFI_DEVICE_PATH_PROTOCOL  *DevicePath2;
5287270962aSWarner Losh 
5297270962aSWarner Losh   //
5307270962aSWarner Losh   // If there's only 1 path, just duplicate it.
5317270962aSWarner Losh   //
5327270962aSWarner Losh   if (FirstDevicePath == NULL) {
5337270962aSWarner Losh     return DuplicateDevicePath ((SecondDevicePath != NULL) ? SecondDevicePath : &mUefiDevicePathLibEndDevicePath);
5347270962aSWarner Losh   }
5357270962aSWarner Losh 
5367270962aSWarner Losh   if (SecondDevicePath == NULL) {
5377270962aSWarner Losh     return DuplicateDevicePath (FirstDevicePath);
5387270962aSWarner Losh   }
5397270962aSWarner Losh 
5407270962aSWarner Losh   if (!IsDevicePathValid (FirstDevicePath, 0) || !IsDevicePathValid (SecondDevicePath, 0)) {
5417270962aSWarner Losh     return NULL;
5427270962aSWarner Losh   }
5437270962aSWarner Losh 
5447270962aSWarner Losh   //
5457270962aSWarner Losh   // Allocate space for the combined device path. It only has one end node of
5467270962aSWarner Losh   // length EFI_DEVICE_PATH_PROTOCOL.
5477270962aSWarner Losh   //
5487270962aSWarner Losh   Size1         = GetDevicePathSize (FirstDevicePath);
5497270962aSWarner Losh   Size2         = GetDevicePathSize (SecondDevicePath);
5507270962aSWarner Losh   Size          = Size1 + Size2 - END_DEVICE_PATH_LENGTH;
5517270962aSWarner Losh 
5527270962aSWarner Losh   NewDevicePath = AllocatePool (Size);
5537270962aSWarner Losh 
5547270962aSWarner Losh   if (NewDevicePath != NULL) {
5557270962aSWarner Losh     NewDevicePath = CopyMem (NewDevicePath, FirstDevicePath, Size1);
5567270962aSWarner Losh     //
5577270962aSWarner Losh     // Over write FirstDevicePath EndNode and do the copy
5587270962aSWarner Losh     //
5597270962aSWarner Losh     DevicePath2 = (EFI_DEVICE_PATH_PROTOCOL *) ((CHAR8 *) NewDevicePath +
5607270962aSWarner Losh                   (Size1 - END_DEVICE_PATH_LENGTH));
5617270962aSWarner Losh     CopyMem (DevicePath2, SecondDevicePath, Size2);
5627270962aSWarner Losh   }
5637270962aSWarner Losh 
5647270962aSWarner Losh   return NewDevicePath;
5657270962aSWarner Losh }
5667270962aSWarner Losh 
5677270962aSWarner Losh /**
5687270962aSWarner Losh   Creates a new path by appending the device node to the device path.
5697270962aSWarner Losh 
5707270962aSWarner Losh   This function creates a new device path by appending a copy of the device node
5717270962aSWarner Losh   specified by DevicePathNode to a copy of the device path specified by DevicePath
5727270962aSWarner Losh   in an allocated buffer. The end-of-device-path device node is moved after the
5737270962aSWarner Losh   end of the appended device node.
5747270962aSWarner Losh   If DevicePathNode is NULL then a copy of DevicePath is returned.
5757270962aSWarner Losh   If DevicePath is NULL then a copy of DevicePathNode, followed by an end-of-device
5767270962aSWarner Losh   path device node is returned.
5777270962aSWarner Losh   If both DevicePathNode and DevicePath are NULL then a copy of an end-of-device-path
5787270962aSWarner Losh   device node is returned.
5797270962aSWarner Losh   If there is not enough memory to allocate space for the new device path, then
5807270962aSWarner Losh   NULL is returned.
5817270962aSWarner Losh   The memory is allocated from EFI boot services memory. It is the responsibility
5827270962aSWarner Losh   of the caller to free the memory allocated.
5837270962aSWarner Losh 
5847270962aSWarner Losh   @param  DevicePath                 A pointer to a device path data structure.
5857270962aSWarner Losh   @param  DevicePathNode             A pointer to a single device path node.
5867270962aSWarner Losh 
5877270962aSWarner Losh   @retval NULL      If there is not enough memory for the new device path.
5887270962aSWarner Losh   @retval Others    A pointer to the new device path if success.
5897270962aSWarner Losh                     A copy of DevicePathNode followed by an end-of-device-path node
5907270962aSWarner Losh                     if both FirstDevicePath and SecondDevicePath are NULL.
5917270962aSWarner Losh                     A copy of an end-of-device-path node if both FirstDevicePath
5927270962aSWarner Losh                     and SecondDevicePath are NULL.
5937270962aSWarner Losh 
5947270962aSWarner Losh **/
5957270962aSWarner Losh EFI_DEVICE_PATH_PROTOCOL *
5967270962aSWarner Losh EFIAPI
AppendDevicePathNode(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath,OPTIONAL IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePathNode OPTIONAL)5977270962aSWarner Losh AppendDevicePathNode (
5987270962aSWarner Losh   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath,     OPTIONAL
5997270962aSWarner Losh   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePathNode  OPTIONAL
6007270962aSWarner Losh   )
6017270962aSWarner Losh {
6027270962aSWarner Losh   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath;
6037270962aSWarner Losh   EFI_DEVICE_PATH_PROTOCOL  *NextNode;
6047270962aSWarner Losh   EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;
6057270962aSWarner Losh   UINTN                     NodeLength;
6067270962aSWarner Losh 
6077270962aSWarner Losh   if (DevicePathNode == NULL) {
6087270962aSWarner Losh     return DuplicateDevicePath ((DevicePath != NULL) ? DevicePath : &mUefiDevicePathLibEndDevicePath);
6097270962aSWarner Losh   }
6107270962aSWarner Losh   //
6117270962aSWarner Losh   // Build a Node that has a terminator on it
6127270962aSWarner Losh   //
6137270962aSWarner Losh   NodeLength = DevicePathNodeLength (DevicePathNode);
6147270962aSWarner Losh 
6157270962aSWarner Losh   TempDevicePath = AllocatePool (NodeLength + END_DEVICE_PATH_LENGTH);
6167270962aSWarner Losh   if (TempDevicePath == NULL) {
6177270962aSWarner Losh     return NULL;
6187270962aSWarner Losh   }
6197270962aSWarner Losh   TempDevicePath = CopyMem (TempDevicePath, DevicePathNode, NodeLength);
6207270962aSWarner Losh   //
6217270962aSWarner Losh   // Add and end device path node to convert Node to device path
6227270962aSWarner Losh   //
6237270962aSWarner Losh   NextNode = NextDevicePathNode (TempDevicePath);
6247270962aSWarner Losh   SetDevicePathEndNode (NextNode);
6257270962aSWarner Losh   //
6267270962aSWarner Losh   // Append device paths
6277270962aSWarner Losh   //
6287270962aSWarner Losh   NewDevicePath = AppendDevicePath (DevicePath, TempDevicePath);
6297270962aSWarner Losh 
6307270962aSWarner Losh   FreePool (TempDevicePath);
6317270962aSWarner Losh 
6327270962aSWarner Losh   return NewDevicePath;
6337270962aSWarner Losh }
634