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