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 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 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 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 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 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 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 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 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 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 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 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 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 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 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