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