xref: /freebsd/lib/libefivar/uefi-dputil.c (revision c66ec88fed842fbaad62c30d510644ceb7bd2d71)
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 //
75 static 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 **/
98 UINTN
99 EFIAPI
100 GetDevicePathSize (
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 **/
143 BOOLEAN
144 EFIAPI
145 IsDevicePathValid (
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 **/
211 UINT8
212 EFIAPI
213 DevicePathType (
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 **/
234 UINT8
235 EFIAPI
236 DevicePathSubType (
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 **/
259 UINTN
260 EFIAPI
261 DevicePathNodeLength (
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 **/
284 EFI_DEVICE_PATH_PROTOCOL *
285 EFIAPI
286 NextDevicePathNode (
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 **/
314 BOOLEAN
315 EFIAPI
316 IsDevicePathEndType (
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 **/
341 BOOLEAN
342 EFIAPI
343 IsDevicePathEnd (
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 **/
367 VOID
368 EFIAPI
369 SetDevicePathEndNode (
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 **/
395 UINT16
396 EFIAPI
397 SetDevicePathNodeLength (
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 **/
428 EFI_DEVICE_PATH_PROTOCOL *
429 EFIAPI
430 CreateDeviceNode (
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 **/
471 EFI_DEVICE_PATH_PROTOCOL *
472 EFIAPI
473 DuplicateDevicePath (
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 **/
518 EFI_DEVICE_PATH_PROTOCOL *
519 EFIAPI
520 AppendDevicePath (
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 **/
597 EFI_DEVICE_PATH_PROTOCOL *
598 EFIAPI
599 AppendDevicePathNode (
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