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