xref: /dflybsd-src/lib/libefivar/uefi-dputil.c (revision 4661c1690a24c9a90726afa4b714671b5ea02dc5)
1*4661c169SSascha Wildner /*-
2*4661c169SSascha Wildner  * Copyright (c) 2017 Netflix, Inc.
3*4661c169SSascha Wildner  *
4*4661c169SSascha Wildner  * Redistribution and use in source and binary forms, with or without
5*4661c169SSascha Wildner  * modification, are permitted provided that the following conditions
6*4661c169SSascha Wildner  * are met:
7*4661c169SSascha Wildner  * 1. Redistributions of source code must retain the above copyright
8*4661c169SSascha Wildner  *    notice, this list of conditions and the following disclaimer.
9*4661c169SSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
10*4661c169SSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
11*4661c169SSascha Wildner  *    documentation and/or other materials provided with the distribution.
12*4661c169SSascha Wildner  *
13*4661c169SSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14*4661c169SSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15*4661c169SSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16*4661c169SSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17*4661c169SSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18*4661c169SSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19*4661c169SSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20*4661c169SSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21*4661c169SSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22*4661c169SSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23*4661c169SSascha Wildner  * SUCH DAMAGE.
24*4661c169SSascha Wildner  *
25*4661c169SSascha Wildner  * $FreeBSD: head/lib/libefivar/uefi-dputil.c 343755 2019-02-04 21:28:25Z imp $
26*4661c169SSascha Wildner  */
27*4661c169SSascha Wildner 
28*4661c169SSascha Wildner /*
29*4661c169SSascha Wildner  * Routines to format EFI_DEVICE_PATHs from the UEFI standard. Much of
30*4661c169SSascha Wildner  * this file is taken from EDK2 and rototilled.
31*4661c169SSascha Wildner  */
32*4661c169SSascha Wildner 
33*4661c169SSascha Wildner #include <efivar.h>
34*4661c169SSascha Wildner #include <limits.h>
35*4661c169SSascha Wildner #include <stdio.h>
36*4661c169SSascha Wildner #include <string.h>
37*4661c169SSascha Wildner #include <sys/endian.h>
38*4661c169SSascha Wildner 
39*4661c169SSascha Wildner #include "efi-osdep.h"
40*4661c169SSascha Wildner 
41*4661c169SSascha Wildner #include "uefi-dplib.h"
42*4661c169SSascha Wildner 
43*4661c169SSascha Wildner /* XXX maybe I should include the entire DevicePathUtilities.c and ifdef out what we don't use */
44*4661c169SSascha Wildner 
45*4661c169SSascha Wildner /*
46*4661c169SSascha Wildner  * Taken from MdePkg/Library/UefiDevicePathLib/DevicePathUtilities.c
47*4661c169SSascha Wildner  * hash a11928f3310518ab1c6fd34e8d0fdbb72de9602c 2017-Mar-01
48*4661c169SSascha Wildner  */
49*4661c169SSascha Wildner 
50*4661c169SSascha Wildner /** @file
51*4661c169SSascha Wildner   Device Path services. The thing to remember is device paths are built out of
52*4661c169SSascha Wildner   nodes. The device path is terminated by an end node that is length
53*4661c169SSascha Wildner   sizeof(EFI_DEVICE_PATH_PROTOCOL). That would be why there is sizeof(EFI_DEVICE_PATH_PROTOCOL)
54*4661c169SSascha Wildner   all over this file.
55*4661c169SSascha Wildner 
56*4661c169SSascha Wildner   The only place where multi-instance device paths are supported is in
57*4661c169SSascha Wildner   environment varibles. Multi-instance device paths should never be placed
58*4661c169SSascha Wildner   on a Handle.
59*4661c169SSascha Wildner 
60*4661c169SSascha Wildner   Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
61*4661c169SSascha Wildner   This program and the accompanying materials
62*4661c169SSascha Wildner   are licensed and made available under the terms and conditions of the BSD License
63*4661c169SSascha Wildner   which accompanies this distribution.  The full text of the license may be found at
64*4661c169SSascha Wildner   http://opensource.org/licenses/bsd-license.php.
65*4661c169SSascha Wildner 
66*4661c169SSascha Wildner   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
67*4661c169SSascha Wildner   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
68*4661c169SSascha Wildner 
69*4661c169SSascha Wildner **/
70*4661c169SSascha Wildner 
71*4661c169SSascha Wildner //
72*4661c169SSascha Wildner // Template for an end-of-device path node.
73*4661c169SSascha Wildner //
74*4661c169SSascha Wildner static CONST EFI_DEVICE_PATH_PROTOCOL  mUefiDevicePathLibEndDevicePath = {
75*4661c169SSascha Wildner   END_DEVICE_PATH_TYPE,
76*4661c169SSascha Wildner   END_ENTIRE_DEVICE_PATH_SUBTYPE,
77*4661c169SSascha Wildner   {
78*4661c169SSascha Wildner     END_DEVICE_PATH_LENGTH,
79*4661c169SSascha Wildner     0
80*4661c169SSascha Wildner   }
81*4661c169SSascha Wildner };
82*4661c169SSascha Wildner 
83*4661c169SSascha Wildner 
84*4661c169SSascha Wildner /**
85*4661c169SSascha Wildner   Returns the size of a device path in bytes.
86*4661c169SSascha Wildner 
87*4661c169SSascha Wildner   This function returns the size, in bytes, of the device path data structure
88*4661c169SSascha Wildner   specified by DevicePath including the end of device path node.
89*4661c169SSascha Wildner   If DevicePath is NULL or invalid, then 0 is returned.
90*4661c169SSascha Wildner 
91*4661c169SSascha Wildner   @param  DevicePath  A pointer to a device path data structure.
92*4661c169SSascha Wildner 
93*4661c169SSascha Wildner   @retval 0           If DevicePath is NULL or invalid.
94*4661c169SSascha Wildner   @retval Others      The size of a device path in bytes.
95*4661c169SSascha Wildner 
96*4661c169SSascha Wildner **/
97*4661c169SSascha Wildner UINTN
98*4661c169SSascha Wildner EFIAPI
GetDevicePathSize(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath)99*4661c169SSascha Wildner GetDevicePathSize (
100*4661c169SSascha Wildner   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath
101*4661c169SSascha Wildner   )
102*4661c169SSascha Wildner {
103*4661c169SSascha Wildner   CONST EFI_DEVICE_PATH_PROTOCOL  *Start;
104*4661c169SSascha Wildner 
105*4661c169SSascha Wildner   if (DevicePath == NULL) {
106*4661c169SSascha Wildner     return 0;
107*4661c169SSascha Wildner   }
108*4661c169SSascha Wildner 
109*4661c169SSascha Wildner   if (!IsDevicePathValid (DevicePath, 0)) {
110*4661c169SSascha Wildner     return 0;
111*4661c169SSascha Wildner   }
112*4661c169SSascha Wildner 
113*4661c169SSascha Wildner   //
114*4661c169SSascha Wildner   // Search for the end of the device path structure
115*4661c169SSascha Wildner   //
116*4661c169SSascha Wildner   Start = DevicePath;
117*4661c169SSascha Wildner   while (!IsDevicePathEnd (DevicePath)) {
118*4661c169SSascha Wildner     DevicePath = NextDevicePathNode (DevicePath);
119*4661c169SSascha Wildner   }
120*4661c169SSascha Wildner 
121*4661c169SSascha Wildner   //
122*4661c169SSascha Wildner   // Compute the size and add back in the size of the end device path structure
123*4661c169SSascha Wildner   //
124*4661c169SSascha Wildner   return ((UINTN) DevicePath - (UINTN) Start) + DevicePathNodeLength (DevicePath);
125*4661c169SSascha Wildner }
126*4661c169SSascha Wildner 
127*4661c169SSascha Wildner /**
128*4661c169SSascha Wildner   Determine whether a given device path is valid.
129*4661c169SSascha Wildner   If DevicePath is NULL, then ASSERT().
130*4661c169SSascha Wildner 
131*4661c169SSascha Wildner   @param  DevicePath  A pointer to a device path data structure.
132*4661c169SSascha Wildner   @param  MaxSize     The maximum size of the device path data structure.
133*4661c169SSascha Wildner 
134*4661c169SSascha Wildner   @retval TRUE        DevicePath is valid.
135*4661c169SSascha Wildner   @retval FALSE       The length of any node in the DevicePath is less
136*4661c169SSascha Wildner                       than sizeof (EFI_DEVICE_PATH_PROTOCOL).
137*4661c169SSascha Wildner   @retval FALSE       If MaxSize is not zero, the size of the DevicePath
138*4661c169SSascha Wildner                       exceeds MaxSize.
139*4661c169SSascha Wildner   @retval FALSE       If PcdMaximumDevicePathNodeCount is not zero, the node
140*4661c169SSascha Wildner                       count of the DevicePath exceeds PcdMaximumDevicePathNodeCount.
141*4661c169SSascha Wildner **/
142*4661c169SSascha Wildner BOOLEAN
143*4661c169SSascha Wildner EFIAPI
IsDevicePathValid(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath,IN UINTN MaxSize)144*4661c169SSascha Wildner IsDevicePathValid (
145*4661c169SSascha Wildner   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
146*4661c169SSascha Wildner   IN       UINTN                    MaxSize
147*4661c169SSascha Wildner   )
148*4661c169SSascha Wildner {
149*4661c169SSascha Wildner   UINTN Count;
150*4661c169SSascha Wildner   UINTN Size;
151*4661c169SSascha Wildner   UINTN NodeLength;
152*4661c169SSascha Wildner 
153*4661c169SSascha Wildner   ASSERT (DevicePath != NULL);
154*4661c169SSascha Wildner 
155*4661c169SSascha Wildner   if (MaxSize == 0) {
156*4661c169SSascha Wildner     MaxSize = MAX_UINTN;
157*4661c169SSascha Wildner   }
158*4661c169SSascha Wildner 
159*4661c169SSascha Wildner   //
160*4661c169SSascha Wildner   // Validate the input size big enough to touch the first node.
161*4661c169SSascha Wildner   //
162*4661c169SSascha Wildner   if (MaxSize < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
163*4661c169SSascha Wildner     return FALSE;
164*4661c169SSascha Wildner   }
165*4661c169SSascha Wildner 
166*4661c169SSascha Wildner   for (Count = 0, Size = 0; !IsDevicePathEnd (DevicePath); DevicePath = NextDevicePathNode (DevicePath)) {
167*4661c169SSascha Wildner     NodeLength = DevicePathNodeLength (DevicePath);
168*4661c169SSascha Wildner     if (NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
169*4661c169SSascha Wildner       return FALSE;
170*4661c169SSascha Wildner     }
171*4661c169SSascha Wildner 
172*4661c169SSascha Wildner     if (NodeLength > MAX_UINTN - Size) {
173*4661c169SSascha Wildner       return FALSE;
174*4661c169SSascha Wildner     }
175*4661c169SSascha Wildner     Size += NodeLength;
176*4661c169SSascha Wildner 
177*4661c169SSascha Wildner     //
178*4661c169SSascha Wildner     // Validate next node before touch it.
179*4661c169SSascha Wildner     //
180*4661c169SSascha Wildner     if (Size > MaxSize - END_DEVICE_PATH_LENGTH ) {
181*4661c169SSascha Wildner       return FALSE;
182*4661c169SSascha Wildner     }
183*4661c169SSascha Wildner 
184*4661c169SSascha Wildner     if (PcdGet32 (PcdMaximumDevicePathNodeCount) > 0) {
185*4661c169SSascha Wildner       Count++;
186*4661c169SSascha Wildner       if (Count >= PcdGet32 (PcdMaximumDevicePathNodeCount)) {
187*4661c169SSascha Wildner         return FALSE;
188*4661c169SSascha Wildner       }
189*4661c169SSascha Wildner     }
190*4661c169SSascha Wildner   }
191*4661c169SSascha Wildner 
192*4661c169SSascha Wildner   //
193*4661c169SSascha Wildner   // Only return TRUE when the End Device Path node is valid.
194*4661c169SSascha Wildner   //
195*4661c169SSascha Wildner   return (BOOLEAN) (DevicePathNodeLength (DevicePath) == END_DEVICE_PATH_LENGTH);
196*4661c169SSascha Wildner }
197*4661c169SSascha Wildner 
198*4661c169SSascha Wildner /**
199*4661c169SSascha Wildner   Returns the Type field of a device path node.
200*4661c169SSascha Wildner 
201*4661c169SSascha Wildner   Returns the Type field of the device path node specified by Node.
202*4661c169SSascha Wildner 
203*4661c169SSascha Wildner   If Node is NULL, then ASSERT().
204*4661c169SSascha Wildner 
205*4661c169SSascha Wildner   @param  Node      A pointer to a device path node data structure.
206*4661c169SSascha Wildner 
207*4661c169SSascha Wildner   @return The Type field of the device path node specified by Node.
208*4661c169SSascha Wildner 
209*4661c169SSascha Wildner **/
210*4661c169SSascha Wildner UINT8
211*4661c169SSascha Wildner EFIAPI
DevicePathType(IN CONST VOID * Node)212*4661c169SSascha Wildner DevicePathType (
213*4661c169SSascha Wildner   IN CONST VOID  *Node
214*4661c169SSascha Wildner   )
215*4661c169SSascha Wildner {
216*4661c169SSascha Wildner   ASSERT (Node != NULL);
217*4661c169SSascha Wildner   return ((const EFI_DEVICE_PATH_PROTOCOL *)(Node))->Type;
218*4661c169SSascha Wildner }
219*4661c169SSascha Wildner 
220*4661c169SSascha Wildner 
221*4661c169SSascha Wildner /**
222*4661c169SSascha Wildner   Returns the SubType field of a device path node.
223*4661c169SSascha Wildner 
224*4661c169SSascha Wildner   Returns the SubType field of the device path node specified by Node.
225*4661c169SSascha Wildner 
226*4661c169SSascha Wildner   If Node is NULL, then ASSERT().
227*4661c169SSascha Wildner 
228*4661c169SSascha Wildner   @param  Node      A pointer to a device path node data structure.
229*4661c169SSascha Wildner 
230*4661c169SSascha Wildner   @return The SubType field of the device path node specified by Node.
231*4661c169SSascha Wildner 
232*4661c169SSascha Wildner **/
233*4661c169SSascha Wildner UINT8
234*4661c169SSascha Wildner EFIAPI
DevicePathSubType(IN CONST VOID * Node)235*4661c169SSascha Wildner DevicePathSubType (
236*4661c169SSascha Wildner   IN CONST VOID  *Node
237*4661c169SSascha Wildner   )
238*4661c169SSascha Wildner {
239*4661c169SSascha Wildner   ASSERT (Node != NULL);
240*4661c169SSascha Wildner   return ((const EFI_DEVICE_PATH_PROTOCOL *)(Node))->SubType;
241*4661c169SSascha Wildner }
242*4661c169SSascha Wildner 
243*4661c169SSascha Wildner /**
244*4661c169SSascha Wildner   Returns the 16-bit Length field of a device path node.
245*4661c169SSascha Wildner 
246*4661c169SSascha Wildner   Returns the 16-bit Length field of the device path node specified by Node.
247*4661c169SSascha Wildner   Node is not required to be aligned on a 16-bit boundary, so it is recommended
248*4661c169SSascha Wildner   that a function such as ReadUnaligned16() be used to extract the contents of
249*4661c169SSascha Wildner   the Length field.
250*4661c169SSascha Wildner 
251*4661c169SSascha Wildner   If Node is NULL, then ASSERT().
252*4661c169SSascha Wildner 
253*4661c169SSascha Wildner   @param  Node      A pointer to a device path node data structure.
254*4661c169SSascha Wildner 
255*4661c169SSascha Wildner   @return The 16-bit Length field of the device path node specified by Node.
256*4661c169SSascha Wildner 
257*4661c169SSascha Wildner **/
258*4661c169SSascha Wildner UINTN
259*4661c169SSascha Wildner EFIAPI
DevicePathNodeLength(IN CONST VOID * Node)260*4661c169SSascha Wildner DevicePathNodeLength (
261*4661c169SSascha Wildner   IN CONST VOID  *Node
262*4661c169SSascha Wildner   )
263*4661c169SSascha Wildner {
264*4661c169SSascha Wildner   ASSERT (Node != NULL);
265*4661c169SSascha Wildner   return ((const EFI_DEVICE_PATH_PROTOCOL *)Node)->Length[0] |
266*4661c169SSascha Wildner       (((const EFI_DEVICE_PATH_PROTOCOL *)Node)->Length[1] << 8);
267*4661c169SSascha Wildner }
268*4661c169SSascha Wildner 
269*4661c169SSascha Wildner /**
270*4661c169SSascha Wildner   Returns a pointer to the next node in a device path.
271*4661c169SSascha Wildner 
272*4661c169SSascha Wildner   Returns a pointer to the device path node that follows the device path node
273*4661c169SSascha Wildner   specified by Node.
274*4661c169SSascha Wildner 
275*4661c169SSascha Wildner   If Node is NULL, then ASSERT().
276*4661c169SSascha Wildner 
277*4661c169SSascha Wildner   @param  Node      A pointer to a device path node data structure.
278*4661c169SSascha Wildner 
279*4661c169SSascha Wildner   @return a pointer to the device path node that follows the device path node
280*4661c169SSascha Wildner   specified by Node.
281*4661c169SSascha Wildner 
282*4661c169SSascha Wildner **/
283*4661c169SSascha Wildner EFI_DEVICE_PATH_PROTOCOL *
284*4661c169SSascha Wildner EFIAPI
NextDevicePathNode(IN CONST VOID * Node)285*4661c169SSascha Wildner NextDevicePathNode (
286*4661c169SSascha Wildner   IN CONST VOID  *Node
287*4661c169SSascha Wildner   )
288*4661c169SSascha Wildner {
289*4661c169SSascha Wildner   ASSERT (Node != NULL);
290*4661c169SSascha Wildner   return ((EFI_DEVICE_PATH_PROTOCOL *)(__DECONST(UINT8 *, Node) + DevicePathNodeLength(Node)));
291*4661c169SSascha Wildner }
292*4661c169SSascha Wildner 
293*4661c169SSascha Wildner /**
294*4661c169SSascha Wildner   Determines if a device path node is an end node of a device path.
295*4661c169SSascha Wildner   This includes nodes that are the end of a device path instance and nodes that
296*4661c169SSascha Wildner   are the end of an entire device path.
297*4661c169SSascha Wildner 
298*4661c169SSascha Wildner   Determines if the device path node specified by Node is an end node of a device path.
299*4661c169SSascha Wildner   This includes nodes that are the end of a device path instance and nodes that are the
300*4661c169SSascha Wildner   end of an entire device path.  If Node represents an end node of a device path,
301*4661c169SSascha Wildner   then TRUE is returned.  Otherwise, FALSE is returned.
302*4661c169SSascha Wildner 
303*4661c169SSascha Wildner   If Node is NULL, then ASSERT().
304*4661c169SSascha Wildner 
305*4661c169SSascha Wildner   @param  Node      A pointer to a device path node data structure.
306*4661c169SSascha Wildner 
307*4661c169SSascha Wildner   @retval TRUE      The device path node specified by Node is an end node of a
308*4661c169SSascha Wildner                     device path.
309*4661c169SSascha Wildner   @retval FALSE     The device path node specified by Node is not an end node of
310*4661c169SSascha Wildner                     a device path.
311*4661c169SSascha Wildner 
312*4661c169SSascha Wildner **/
313*4661c169SSascha Wildner BOOLEAN
314*4661c169SSascha Wildner EFIAPI
IsDevicePathEndType(IN CONST VOID * Node)315*4661c169SSascha Wildner IsDevicePathEndType (
316*4661c169SSascha Wildner   IN CONST VOID  *Node
317*4661c169SSascha Wildner   )
318*4661c169SSascha Wildner {
319*4661c169SSascha Wildner   ASSERT (Node != NULL);
320*4661c169SSascha Wildner   return (BOOLEAN) (DevicePathType (Node) == END_DEVICE_PATH_TYPE);
321*4661c169SSascha Wildner }
322*4661c169SSascha Wildner 
323*4661c169SSascha Wildner /**
324*4661c169SSascha Wildner   Determines if a device path node is an end node of an entire device path.
325*4661c169SSascha Wildner 
326*4661c169SSascha Wildner   Determines if a device path node specified by Node is an end node of an entire
327*4661c169SSascha Wildner   device path. If Node represents the end of an entire device path, then TRUE is
328*4661c169SSascha Wildner   returned.  Otherwise, FALSE is returned.
329*4661c169SSascha Wildner 
330*4661c169SSascha Wildner   If Node is NULL, then ASSERT().
331*4661c169SSascha Wildner 
332*4661c169SSascha Wildner   @param  Node      A pointer to a device path node data structure.
333*4661c169SSascha Wildner 
334*4661c169SSascha Wildner   @retval TRUE      The device path node specified by Node is the end of an entire
335*4661c169SSascha Wildner                     device path.
336*4661c169SSascha Wildner   @retval FALSE     The device path node specified by Node is not the end of an
337*4661c169SSascha Wildner                     entire device path.
338*4661c169SSascha Wildner 
339*4661c169SSascha Wildner **/
340*4661c169SSascha Wildner BOOLEAN
341*4661c169SSascha Wildner EFIAPI
IsDevicePathEnd(IN CONST VOID * Node)342*4661c169SSascha Wildner IsDevicePathEnd (
343*4661c169SSascha Wildner   IN CONST VOID  *Node
344*4661c169SSascha Wildner   )
345*4661c169SSascha Wildner {
346*4661c169SSascha Wildner   ASSERT (Node != NULL);
347*4661c169SSascha Wildner   return (BOOLEAN) (IsDevicePathEndType (Node) && DevicePathSubType(Node) == END_ENTIRE_DEVICE_PATH_SUBTYPE);
348*4661c169SSascha Wildner }
349*4661c169SSascha Wildner 
350*4661c169SSascha Wildner /**
351*4661c169SSascha Wildner   Fills in all the fields of a device path node that is the end of an entire device path.
352*4661c169SSascha Wildner 
353*4661c169SSascha Wildner   Fills in all the fields of a device path node specified by Node so Node represents
354*4661c169SSascha Wildner   the end of an entire device path.  The Type field of Node is set to
355*4661c169SSascha Wildner   END_DEVICE_PATH_TYPE, the SubType field of Node is set to
356*4661c169SSascha Wildner   END_ENTIRE_DEVICE_PATH_SUBTYPE, and the Length field of Node is set to
357*4661c169SSascha Wildner   END_DEVICE_PATH_LENGTH.  Node is not required to be aligned on a 16-bit boundary,
358*4661c169SSascha Wildner   so it is recommended that a function such as WriteUnaligned16() be used to set
359*4661c169SSascha Wildner   the contents of the Length field.
360*4661c169SSascha Wildner 
361*4661c169SSascha Wildner   If Node is NULL, then ASSERT().
362*4661c169SSascha Wildner 
363*4661c169SSascha Wildner   @param  Node      A pointer to a device path node data structure.
364*4661c169SSascha Wildner 
365*4661c169SSascha Wildner **/
366*4661c169SSascha Wildner VOID
367*4661c169SSascha Wildner EFIAPI
SetDevicePathEndNode(OUT VOID * Node)368*4661c169SSascha Wildner SetDevicePathEndNode (
369*4661c169SSascha Wildner   OUT VOID  *Node
370*4661c169SSascha Wildner   )
371*4661c169SSascha Wildner {
372*4661c169SSascha Wildner   ASSERT (Node != NULL);
373*4661c169SSascha Wildner   memcpy (Node, &mUefiDevicePathLibEndDevicePath, sizeof (mUefiDevicePathLibEndDevicePath));
374*4661c169SSascha Wildner }
375*4661c169SSascha Wildner 
376*4661c169SSascha Wildner /**
377*4661c169SSascha Wildner   Sets the length, in bytes, of a device path node.
378*4661c169SSascha Wildner 
379*4661c169SSascha Wildner   Sets the length of the device path node specified by Node to the value specified
380*4661c169SSascha Wildner   by NodeLength.  NodeLength is returned.  Node is not required to be aligned on
381*4661c169SSascha Wildner   a 16-bit boundary, so it is recommended that a function such as WriteUnaligned16()
382*4661c169SSascha Wildner   be used to set the contents of the Length field.
383*4661c169SSascha Wildner 
384*4661c169SSascha Wildner   If Node is NULL, then ASSERT().
385*4661c169SSascha Wildner   If NodeLength >= SIZE_64KB, then ASSERT().
386*4661c169SSascha Wildner   If NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL), then ASSERT().
387*4661c169SSascha Wildner 
388*4661c169SSascha Wildner   @param  Node      A pointer to a device path node data structure.
389*4661c169SSascha Wildner   @param  Length    The length, in bytes, of the device path node.
390*4661c169SSascha Wildner 
391*4661c169SSascha Wildner   @return Length
392*4661c169SSascha Wildner 
393*4661c169SSascha Wildner **/
394*4661c169SSascha Wildner UINT16
395*4661c169SSascha Wildner EFIAPI
SetDevicePathNodeLength(IN OUT VOID * Node,IN UINTN Length)396*4661c169SSascha Wildner SetDevicePathNodeLength (
397*4661c169SSascha Wildner   IN OUT VOID  *Node,
398*4661c169SSascha Wildner   IN UINTN     Length
399*4661c169SSascha Wildner   )
400*4661c169SSascha Wildner {
401*4661c169SSascha Wildner   ASSERT (Node != NULL);
402*4661c169SSascha Wildner   ASSERT ((Length >= sizeof (EFI_DEVICE_PATH_PROTOCOL)) && (Length < SIZE_64KB));
403*4661c169SSascha Wildner //  return WriteUnaligned16 ((UINT16 *)&((EFI_DEVICE_PATH_PROTOCOL *)(Node))->Length[0], (UINT16)(Length));
404*4661c169SSascha Wildner   le16enc(&((EFI_DEVICE_PATH_PROTOCOL *)(Node))->Length[0], (UINT16)(Length));
405*4661c169SSascha Wildner   return Length;
406*4661c169SSascha Wildner }
407*4661c169SSascha Wildner 
408*4661c169SSascha Wildner /**
409*4661c169SSascha Wildner   Creates a device node.
410*4661c169SSascha Wildner 
411*4661c169SSascha Wildner   This function creates a new device node in a newly allocated buffer of size
412*4661c169SSascha Wildner   NodeLength and initializes the device path node header with NodeType and NodeSubType.
413*4661c169SSascha Wildner   The new device path node is returned.
414*4661c169SSascha Wildner   If NodeLength is smaller than a device path header, then NULL is returned.
415*4661c169SSascha Wildner   If there is not enough memory to allocate space for the new device path, then
416*4661c169SSascha Wildner   NULL is returned.
417*4661c169SSascha Wildner   The memory is allocated from EFI boot services memory. It is the responsibility
418*4661c169SSascha Wildner   of the caller to free the memory allocated.
419*4661c169SSascha Wildner 
420*4661c169SSascha Wildner   @param  NodeType                   The device node type for the new device node.
421*4661c169SSascha Wildner   @param  NodeSubType                The device node sub-type for the new device node.
422*4661c169SSascha Wildner   @param  NodeLength                 The length of the new device node.
423*4661c169SSascha Wildner 
424*4661c169SSascha Wildner   @return The new device path.
425*4661c169SSascha Wildner 
426*4661c169SSascha Wildner **/
427*4661c169SSascha Wildner EFI_DEVICE_PATH_PROTOCOL *
428*4661c169SSascha Wildner EFIAPI
CreateDeviceNode(IN UINT8 NodeType,IN UINT8 NodeSubType,IN UINT16 NodeLength)429*4661c169SSascha Wildner CreateDeviceNode (
430*4661c169SSascha Wildner   IN UINT8                           NodeType,
431*4661c169SSascha Wildner   IN UINT8                           NodeSubType,
432*4661c169SSascha Wildner   IN UINT16                          NodeLength
433*4661c169SSascha Wildner   )
434*4661c169SSascha Wildner {
435*4661c169SSascha Wildner   EFI_DEVICE_PATH_PROTOCOL      *DevicePath;
436*4661c169SSascha Wildner 
437*4661c169SSascha Wildner   if (NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
438*4661c169SSascha Wildner     //
439*4661c169SSascha Wildner     // NodeLength is less than the size of the header.
440*4661c169SSascha Wildner     //
441*4661c169SSascha Wildner     return NULL;
442*4661c169SSascha Wildner   }
443*4661c169SSascha Wildner 
444*4661c169SSascha Wildner   DevicePath = AllocateZeroPool (NodeLength);
445*4661c169SSascha Wildner   if (DevicePath != NULL) {
446*4661c169SSascha Wildner      DevicePath->Type    = NodeType;
447*4661c169SSascha Wildner      DevicePath->SubType = NodeSubType;
448*4661c169SSascha Wildner      SetDevicePathNodeLength (DevicePath, NodeLength);
449*4661c169SSascha Wildner   }
450*4661c169SSascha Wildner 
451*4661c169SSascha Wildner   return DevicePath;
452*4661c169SSascha Wildner }
453*4661c169SSascha Wildner 
454*4661c169SSascha Wildner /**
455*4661c169SSascha Wildner   Creates a new copy of an existing device path.
456*4661c169SSascha Wildner 
457*4661c169SSascha Wildner   This function allocates space for a new copy of the device path specified by DevicePath.
458*4661c169SSascha Wildner   If DevicePath is NULL, then NULL is returned.  If the memory is successfully
459*4661c169SSascha Wildner   allocated, then the contents of DevicePath are copied to the newly allocated
460*4661c169SSascha Wildner   buffer, and a pointer to that buffer is returned.  Otherwise, NULL is returned.
461*4661c169SSascha Wildner   The memory for the new device path is allocated from EFI boot services memory.
462*4661c169SSascha Wildner   It is the responsibility of the caller to free the memory allocated.
463*4661c169SSascha Wildner 
464*4661c169SSascha Wildner   @param  DevicePath    A pointer to a device path data structure.
465*4661c169SSascha Wildner 
466*4661c169SSascha Wildner   @retval NULL          DevicePath is NULL or invalid.
467*4661c169SSascha Wildner   @retval Others        A pointer to the duplicated device path.
468*4661c169SSascha Wildner 
469*4661c169SSascha Wildner **/
470*4661c169SSascha Wildner EFI_DEVICE_PATH_PROTOCOL *
471*4661c169SSascha Wildner EFIAPI
DuplicateDevicePath(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath)472*4661c169SSascha Wildner DuplicateDevicePath (
473*4661c169SSascha Wildner   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath
474*4661c169SSascha Wildner   )
475*4661c169SSascha Wildner {
476*4661c169SSascha Wildner   UINTN                     Size;
477*4661c169SSascha Wildner 
478*4661c169SSascha Wildner   //
479*4661c169SSascha Wildner   // Compute the size
480*4661c169SSascha Wildner   //
481*4661c169SSascha Wildner   Size = GetDevicePathSize (DevicePath);
482*4661c169SSascha Wildner   if (Size == 0) {
483*4661c169SSascha Wildner     return NULL;
484*4661c169SSascha Wildner   }
485*4661c169SSascha Wildner 
486*4661c169SSascha Wildner   //
487*4661c169SSascha Wildner   // Allocate space for duplicate device path
488*4661c169SSascha Wildner   //
489*4661c169SSascha Wildner 
490*4661c169SSascha Wildner   return AllocateCopyPool (Size, DevicePath);
491*4661c169SSascha Wildner }
492*4661c169SSascha Wildner 
493*4661c169SSascha Wildner /**
494*4661c169SSascha Wildner   Creates a new device path by appending a second device path to a first device path.
495*4661c169SSascha Wildner 
496*4661c169SSascha Wildner   This function creates a new device path by appending a copy of SecondDevicePath
497*4661c169SSascha Wildner   to a copy of FirstDevicePath in a newly allocated buffer.  Only the end-of-device-path
498*4661c169SSascha Wildner   device node from SecondDevicePath is retained. The newly created device path is
499*4661c169SSascha Wildner   returned. If FirstDevicePath is NULL, then it is ignored, and a duplicate of
500*4661c169SSascha Wildner   SecondDevicePath is returned.  If SecondDevicePath is NULL, then it is ignored,
501*4661c169SSascha Wildner   and a duplicate of FirstDevicePath is returned. If both FirstDevicePath and
502*4661c169SSascha Wildner   SecondDevicePath are NULL, then a copy of an end-of-device-path is returned.
503*4661c169SSascha Wildner 
504*4661c169SSascha Wildner   If there is not enough memory for the newly allocated buffer, then NULL is returned.
505*4661c169SSascha Wildner   The memory for the new device path is allocated from EFI boot services memory.
506*4661c169SSascha Wildner   It is the responsibility of the caller to free the memory allocated.
507*4661c169SSascha Wildner 
508*4661c169SSascha Wildner   @param  FirstDevicePath            A pointer to a device path data structure.
509*4661c169SSascha Wildner   @param  SecondDevicePath           A pointer to a device path data structure.
510*4661c169SSascha Wildner 
511*4661c169SSascha Wildner   @retval NULL      If there is not enough memory for the newly allocated buffer.
512*4661c169SSascha Wildner   @retval NULL      If FirstDevicePath or SecondDevicePath is invalid.
513*4661c169SSascha Wildner   @retval Others    A pointer to the new device path if success.
514*4661c169SSascha Wildner                     Or a copy an end-of-device-path if both FirstDevicePath and SecondDevicePath are NULL.
515*4661c169SSascha Wildner 
516*4661c169SSascha Wildner **/
517*4661c169SSascha Wildner EFI_DEVICE_PATH_PROTOCOL *
518*4661c169SSascha Wildner EFIAPI
AppendDevicePath(IN CONST EFI_DEVICE_PATH_PROTOCOL * FirstDevicePath,OPTIONAL IN CONST EFI_DEVICE_PATH_PROTOCOL * SecondDevicePath OPTIONAL)519*4661c169SSascha Wildner AppendDevicePath (
520*4661c169SSascha Wildner   IN CONST EFI_DEVICE_PATH_PROTOCOL  *FirstDevicePath,  OPTIONAL
521*4661c169SSascha Wildner   IN CONST EFI_DEVICE_PATH_PROTOCOL  *SecondDevicePath  OPTIONAL
522*4661c169SSascha Wildner   )
523*4661c169SSascha Wildner {
524*4661c169SSascha Wildner   UINTN                     Size;
525*4661c169SSascha Wildner   UINTN                     Size1;
526*4661c169SSascha Wildner   UINTN                     Size2;
527*4661c169SSascha Wildner   EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;
528*4661c169SSascha Wildner   EFI_DEVICE_PATH_PROTOCOL  *DevicePath2;
529*4661c169SSascha Wildner 
530*4661c169SSascha Wildner   //
531*4661c169SSascha Wildner   // If there's only 1 path, just duplicate it.
532*4661c169SSascha Wildner   //
533*4661c169SSascha Wildner   if (FirstDevicePath == NULL) {
534*4661c169SSascha Wildner     return DuplicateDevicePath ((SecondDevicePath != NULL) ? SecondDevicePath : &mUefiDevicePathLibEndDevicePath);
535*4661c169SSascha Wildner   }
536*4661c169SSascha Wildner 
537*4661c169SSascha Wildner   if (SecondDevicePath == NULL) {
538*4661c169SSascha Wildner     return DuplicateDevicePath (FirstDevicePath);
539*4661c169SSascha Wildner   }
540*4661c169SSascha Wildner 
541*4661c169SSascha Wildner   if (!IsDevicePathValid (FirstDevicePath, 0) || !IsDevicePathValid (SecondDevicePath, 0)) {
542*4661c169SSascha Wildner     return NULL;
543*4661c169SSascha Wildner   }
544*4661c169SSascha Wildner 
545*4661c169SSascha Wildner   //
546*4661c169SSascha Wildner   // Allocate space for the combined device path. It only has one end node of
547*4661c169SSascha Wildner   // length EFI_DEVICE_PATH_PROTOCOL.
548*4661c169SSascha Wildner   //
549*4661c169SSascha Wildner   Size1         = GetDevicePathSize (FirstDevicePath);
550*4661c169SSascha Wildner   Size2         = GetDevicePathSize (SecondDevicePath);
551*4661c169SSascha Wildner   Size          = Size1 + Size2 - END_DEVICE_PATH_LENGTH;
552*4661c169SSascha Wildner 
553*4661c169SSascha Wildner   NewDevicePath = AllocatePool (Size);
554*4661c169SSascha Wildner 
555*4661c169SSascha Wildner   if (NewDevicePath != NULL) {
556*4661c169SSascha Wildner     NewDevicePath = CopyMem (NewDevicePath, FirstDevicePath, Size1);
557*4661c169SSascha Wildner     //
558*4661c169SSascha Wildner     // Over write FirstDevicePath EndNode and do the copy
559*4661c169SSascha Wildner     //
560*4661c169SSascha Wildner     DevicePath2 = (EFI_DEVICE_PATH_PROTOCOL *) ((CHAR8 *) NewDevicePath +
561*4661c169SSascha Wildner                   (Size1 - END_DEVICE_PATH_LENGTH));
562*4661c169SSascha Wildner     CopyMem (DevicePath2, SecondDevicePath, Size2);
563*4661c169SSascha Wildner   }
564*4661c169SSascha Wildner 
565*4661c169SSascha Wildner   return NewDevicePath;
566*4661c169SSascha Wildner }
567*4661c169SSascha Wildner 
568*4661c169SSascha Wildner /**
569*4661c169SSascha Wildner   Creates a new path by appending the device node to the device path.
570*4661c169SSascha Wildner 
571*4661c169SSascha Wildner   This function creates a new device path by appending a copy of the device node
572*4661c169SSascha Wildner   specified by DevicePathNode to a copy of the device path specified by DevicePath
573*4661c169SSascha Wildner   in an allocated buffer. The end-of-device-path device node is moved after the
574*4661c169SSascha Wildner   end of the appended device node.
575*4661c169SSascha Wildner   If DevicePathNode is NULL then a copy of DevicePath is returned.
576*4661c169SSascha Wildner   If DevicePath is NULL then a copy of DevicePathNode, followed by an end-of-device
577*4661c169SSascha Wildner   path device node is returned.
578*4661c169SSascha Wildner   If both DevicePathNode and DevicePath are NULL then a copy of an end-of-device-path
579*4661c169SSascha Wildner   device node is returned.
580*4661c169SSascha Wildner   If there is not enough memory to allocate space for the new device path, then
581*4661c169SSascha Wildner   NULL is returned.
582*4661c169SSascha Wildner   The memory is allocated from EFI boot services memory. It is the responsibility
583*4661c169SSascha Wildner   of the caller to free the memory allocated.
584*4661c169SSascha Wildner 
585*4661c169SSascha Wildner   @param  DevicePath                 A pointer to a device path data structure.
586*4661c169SSascha Wildner   @param  DevicePathNode             A pointer to a single device path node.
587*4661c169SSascha Wildner 
588*4661c169SSascha Wildner   @retval NULL      If there is not enough memory for the new device path.
589*4661c169SSascha Wildner   @retval Others    A pointer to the new device path if success.
590*4661c169SSascha Wildner                     A copy of DevicePathNode followed by an end-of-device-path node
591*4661c169SSascha Wildner                     if both FirstDevicePath and SecondDevicePath are NULL.
592*4661c169SSascha Wildner                     A copy of an end-of-device-path node if both FirstDevicePath
593*4661c169SSascha Wildner                     and SecondDevicePath are NULL.
594*4661c169SSascha Wildner 
595*4661c169SSascha Wildner **/
596*4661c169SSascha Wildner EFI_DEVICE_PATH_PROTOCOL *
597*4661c169SSascha Wildner EFIAPI
AppendDevicePathNode(IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath,OPTIONAL IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePathNode OPTIONAL)598*4661c169SSascha Wildner AppendDevicePathNode (
599*4661c169SSascha Wildner   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath,     OPTIONAL
600*4661c169SSascha Wildner   IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePathNode  OPTIONAL
601*4661c169SSascha Wildner   )
602*4661c169SSascha Wildner {
603*4661c169SSascha Wildner   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath;
604*4661c169SSascha Wildner   EFI_DEVICE_PATH_PROTOCOL  *NextNode;
605*4661c169SSascha Wildner   EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;
606*4661c169SSascha Wildner   UINTN                     NodeLength;
607*4661c169SSascha Wildner 
608*4661c169SSascha Wildner   if (DevicePathNode == NULL) {
609*4661c169SSascha Wildner     return DuplicateDevicePath ((DevicePath != NULL) ? DevicePath : &mUefiDevicePathLibEndDevicePath);
610*4661c169SSascha Wildner   }
611*4661c169SSascha Wildner   //
612*4661c169SSascha Wildner   // Build a Node that has a terminator on it
613*4661c169SSascha Wildner   //
614*4661c169SSascha Wildner   NodeLength = DevicePathNodeLength (DevicePathNode);
615*4661c169SSascha Wildner 
616*4661c169SSascha Wildner   TempDevicePath = AllocatePool (NodeLength + END_DEVICE_PATH_LENGTH);
617*4661c169SSascha Wildner   if (TempDevicePath == NULL) {
618*4661c169SSascha Wildner     return NULL;
619*4661c169SSascha Wildner   }
620*4661c169SSascha Wildner   TempDevicePath = CopyMem (TempDevicePath, DevicePathNode, NodeLength);
621*4661c169SSascha Wildner   //
622*4661c169SSascha Wildner   // Add and end device path node to convert Node to device path
623*4661c169SSascha Wildner   //
624*4661c169SSascha Wildner   NextNode = NextDevicePathNode (TempDevicePath);
625*4661c169SSascha Wildner   SetDevicePathEndNode (NextNode);
626*4661c169SSascha Wildner   //
627*4661c169SSascha Wildner   // Append device paths
628*4661c169SSascha Wildner   //
629*4661c169SSascha Wildner   NewDevicePath = AppendDevicePath (DevicePath, TempDevicePath);
630*4661c169SSascha Wildner 
631*4661c169SSascha Wildner   FreePool (TempDevicePath);
632*4661c169SSascha Wildner 
633*4661c169SSascha Wildner   return NewDevicePath;
634*4661c169SSascha Wildner }
635