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