xref: /freebsd/lib/libefivar/uefi-dputil.c (revision 069ac184)
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 //
73 static 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 **/
96 UINTN
97 EFIAPI
98 GetDevicePathSize (
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 **/
141 BOOLEAN
142 EFIAPI
143 IsDevicePathValid (
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 **/
209 UINT8
210 EFIAPI
211 DevicePathType (
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 **/
232 UINT8
233 EFIAPI
234 DevicePathSubType (
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 **/
257 UINTN
258 EFIAPI
259 DevicePathNodeLength (
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 **/
282 EFI_DEVICE_PATH_PROTOCOL *
283 EFIAPI
284 NextDevicePathNode (
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 **/
312 BOOLEAN
313 EFIAPI
314 IsDevicePathEndType (
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 **/
339 BOOLEAN
340 EFIAPI
341 IsDevicePathEnd (
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 **/
365 VOID
366 EFIAPI
367 SetDevicePathEndNode (
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 **/
393 UINT16
394 EFIAPI
395 SetDevicePathNodeLength (
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 **/
426 EFI_DEVICE_PATH_PROTOCOL *
427 EFIAPI
428 CreateDeviceNode (
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 **/
469 EFI_DEVICE_PATH_PROTOCOL *
470 EFIAPI
471 DuplicateDevicePath (
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 **/
516 EFI_DEVICE_PATH_PROTOCOL *
517 EFIAPI
518 AppendDevicePath (
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 **/
595 EFI_DEVICE_PATH_PROTOCOL *
596 EFIAPI
597 AppendDevicePathNode (
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