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 sould 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 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 } 637