]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libefivar/uefi-dputil.c
MFV r328323,328324:
[FreeBSD/FreeBSD.git] / lib / libefivar / uefi-dputil.c
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 should 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 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 }