1 /*++ 2 3 Copyright (c) Microsoft Corporation 4 5 Module Name: 6 7 FxResourceCollection.cpp 8 9 Abstract: 10 11 This module implements a base object for derived collection classes and 12 the derived collection classes. 13 14 Author: 15 16 17 18 Environment: 19 20 User mode only 21 22 Revision History: 23 24 --*/ 25 26 #include "FxSupportPch.hpp" 27 #include <intsafe.h> 28 29 #if defined(EVENT_TRACING) 30 // Tracing support 31 extern "C" { 32 #include "FxResourceCollectionUm.tmh" 33 } 34 #endif 35 36 FxCmResList::~FxCmResList() 37 { 38 DeleteRegisterResourceTable(); 39 DeletePortResourceTable(); 40 } 41 42 NTSTATUS 43 FxCmResList::BuildRegisterResourceTable( 44 VOID 45 ) 46 { 47 ULONG count; 48 ULONG i, index; 49 PCM_PARTIAL_RESOURCE_DESCRIPTOR desc; 50 ULONG numRegisterDesc; 51 BOOLEAN locked = FALSE; 52 NTSTATUS status; 53 54 count = GetCount(); 55 numRegisterDesc = 0; 56 57 // 58 // count number of register descriptors 59 // 60 for (i = 0; i < count; i++) { 61 desc = GetDescriptor(i); 62 if (desc == NULL) { 63 status = STATUS_INVALID_DEVICE_STATE; 64 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 65 "Resource Descriptor not found %!STATUS!", status); 66 goto exit; 67 } 68 69 if (desc->Type == CmResourceTypeMemory || 70 desc->Type == CmResourceTypeMemoryLarge) { 71 numRegisterDesc++; 72 } 73 } 74 75 if (numRegisterDesc == 0) { 76 return STATUS_SUCCESS; 77 } 78 79 // 80 // allocate table 81 // 82 LockResourceTable(); 83 locked = TRUE; 84 85 status = FxRegisterResourceInfo::_CreateAndInit( 86 GetDriverGlobals(), 87 numRegisterDesc, 88 &m_RegisterResourceTable 89 ); 90 if (!NT_SUCCESS(status)) { 91 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 92 "Failed to allocate memory for resource table" 93 " %!STATUS!", status); 94 goto exit; 95 } 96 m_RegisterResourceTableSizeCe = numRegisterDesc; 97 98 // 99 // Populate table 100 // 101 index = 0; 102 for (i = 0; i < count; i++) { 103 desc = GetDescriptor(i); 104 if (desc == NULL) { 105 status = STATUS_INVALID_DEVICE_STATE; 106 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 107 "Resource Descriptor not found %!STATUS!", status); 108 goto exit; 109 } 110 111 if (desc->Type == CmResourceTypeMemory || 112 desc->Type == CmResourceTypeMemoryLarge) { 113 SIZE_T len; 114 PHYSICAL_ADDRESS pa; 115 116 // 117 // This will populate Length and StartPa 118 // 119 len = GetResourceLength(desc, &pa); 120 if (len) { 121 m_RegisterResourceTable[index].SetPhysicalAddress(pa, len); 122 } 123 124 index++; 125 } 126 } 127 128 exit: 129 130 if (!NT_SUCCESS(status)) { 131 if (m_RegisterResourceTable != NULL) { 132 delete [] m_RegisterResourceTable; 133 m_RegisterResourceTable = NULL; 134 m_RegisterResourceTableSizeCe = 0; 135 } 136 } 137 138 if (locked) { 139 UnlockResourceTable(); 140 } 141 142 return status; 143 } 144 145 NTSTATUS 146 FxCmResList::BuildPortResourceTable( 147 VOID 148 ) 149 { 150 ULONG count; 151 ULONG i, index; 152 PCM_PARTIAL_RESOURCE_DESCRIPTOR desc; 153 ULONG numPortDesc; 154 BOOLEAN locked = FALSE; 155 NTSTATUS status; 156 157 count = GetCount(); 158 numPortDesc = 0; 159 160 // 161 // count number of register descriptors 162 // 163 for (i = 0; i < count; i++) { 164 desc = GetDescriptor(i); 165 if (desc == NULL) { 166 status = STATUS_INVALID_DEVICE_STATE; 167 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 168 "Resource Descriptor not found %!STATUS!", status); 169 goto exit; 170 } 171 172 if (desc->Type == CmResourceTypePort) { 173 numPortDesc++; 174 } 175 } 176 177 if (numPortDesc == 0) { 178 return STATUS_SUCCESS; 179 } 180 181 // 182 // allocate table 183 // 184 LockResourceTable(); 185 locked = TRUE; 186 187 status = FxPortResourceInfo::_CreateAndInit( 188 GetDriverGlobals(), 189 numPortDesc, 190 &m_PortResourceTable 191 ); 192 if (!NT_SUCCESS(status)) { 193 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 194 "Failed to allocate memory for resource table" 195 " %!STATUS!", status); 196 goto exit; 197 } 198 m_PortResourceTableSizeCe = numPortDesc; 199 200 // 201 // Populate table 202 // 203 index = 0; 204 for (i = 0; i < count; i++) { 205 desc = GetDescriptor(i); 206 if (desc == NULL) { 207 status = STATUS_INVALID_DEVICE_STATE; 208 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 209 "Resource Descriptor not found %!STATUS!", status); 210 goto exit; 211 } 212 213 if (desc->Type == CmResourceTypePort) { 214 SIZE_T len; 215 PHYSICAL_ADDRESS pa; 216 217 // 218 // This will populate Length, StartPa and EndPa 219 // 220 len = GetResourceLength(desc, &pa); 221 if (len) { 222 m_PortResourceTable[index].SetPhysicalAddress(pa, len); 223 } 224 225 index++; 226 } 227 } 228 229 exit: 230 231 if (!NT_SUCCESS(status)) { 232 if (m_PortResourceTable != NULL) { 233 delete [] m_PortResourceTable; 234 m_PortResourceTable = NULL; 235 m_PortResourceTableSizeCe = 0; 236 } 237 } 238 239 if (locked) { 240 UnlockResourceTable(); 241 } 242 243 return status; 244 } 245 246 247 VOID 248 FxCmResList::UpdateRegisterResourceEntryLocked( 249 __in FxRegisterResourceInfo* Entry, 250 __in PVOID SystemMappedAddress, 251 __in SIZE_T NumberOfBytes, 252 __in PVOID UsermodeMappedAddress 253 ) 254 { 255 Entry->SetMappedAddress(SystemMappedAddress, NumberOfBytes, UsermodeMappedAddress); 256 } 257 258 VOID 259 FxCmResList::ClearRegisterResourceEntryLocked( 260 __in FxRegisterResourceInfo* Entry 261 ) 262 { 263 Entry->ClearMappedAddress(); 264 } 265 266 HRESULT 267 FxCmResList::ValidateRegisterPhysicalAddressRange ( 268 __in PHYSICAL_ADDRESS PhysicalAddress, 269 __in SIZE_T Size, 270 __out FxRegisterResourceInfo** TableEntry 271 ) 272 /*++ 273 274 Routine Description: 275 276 This routine checks whether the physical address range is part of the resources 277 assigned to the device by pnp manager. It also returns the table entry 278 corresponding to the physical address range from register resource table. 279 280 Arguments: 281 282 PhysicalAddress - Supplies physical address to validate 283 284 Size - Supplies size of address range in bytes. 285 286 TableEntry - Supplies a pointer to store the table entry that corresponds to 287 this physical address. 288 289 Return Value: 290 291 HRESULT 292 293 S_OK if physical address is one assigned by pnp manager to this device. 294 E_INAVLIDARG otherwise. 295 296 --*/ 297 { 298 ULONG i; 299 HRESULT hr; 300 ULONGLONG driverStartPa, driverEndPa, systemStartPa, systemEndPa; 301 ULONGLONG tmp; 302 FxRegisterResourceInfo* entry = NULL; 303 304 *TableEntry = NULL; 305 306 // 307 // Physical address is of LONGLONG type (signed) we need to cast it to 308 // ULONGLONG for comparision because in a LONGLONG comprison, the 309 // result is different when highest bit is set vs when it is not set. 310 // 311 driverStartPa = PhysicalAddress.QuadPart; 312 313 // 314 // driverEndPa = driverStartPa + Size - 1; 315 // 316 hr = ULongLongAdd(driverStartPa, Size, &tmp); 317 FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO), CHECK("Integer overflow occurred" 318 "when computing register address range", SUCCEEDED(hr)), 319 GetDriverGlobals()->Public.DriverName); 320 321 driverEndPa = tmp - 1; 322 323 // 324 // We allow one physical address range mapping only. The base address and 325 // length can be flexible within the assigned range. 326 // 327 for (i = 0; i < m_RegisterResourceTableSizeCe; i++) { 328 entry = &m_RegisterResourceTable[i]; 329 330 // 331 // No need to do int overflow safe additon here since start address and 332 // length are assigned by pnp manager. Note that we don't store endPa in 333 // resource table the way we do for SystemVa is because endPa is not 334 // needed in hot path so can be computed using length. 335 // 336 systemStartPa = entry->m_StartPa.QuadPart; 337 systemEndPa = systemStartPa + entry->m_Length - 1; 338 339 if (driverStartPa >= systemStartPa && 340 driverEndPa <= systemEndPa) { 341 342 FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO), CHECK("Attempt to do multiple " 343 "mapping of same resource, or multiple mapping in same resource" 344 " range", 345 (entry->m_StartSystemVa == NULL)), GetDriverGlobals()->Public.DriverName); 346 FX_VERIFY_WITH_NAME(INTERNAL, CHECK_NULL(entry->m_EndSystemVa), 347 GetDriverGlobals()->Public.DriverName); 348 FX_VERIFY_WITH_NAME(INTERNAL, CHECK_NULL(entry->m_StartUsermodeVa), 349 GetDriverGlobals()->Public.DriverName); 350 FX_VERIFY_WITH_NAME(INTERNAL, CHECK("Mapped length not zero", 351 (entry->m_MappedLength == 0)), GetDriverGlobals()->Public.DriverName); 352 353 *TableEntry = entry; 354 355 return S_OK; 356 } 357 } 358 359 return E_INVALIDARG; 360 } 361 362 HRESULT 363 FxCmResList::ValidateAndClearMapping( 364 __in PVOID Address, 365 __in SIZE_T Length 366 ) 367 /*++ 368 369 Routine Description: 370 371 This routine checks whether the mapped system base address and size is part 372 of the resources assigned to the device by pnp manager. If so it clears the 373 system and usermode address mapping from the table. 374 375 Arguments: 376 377 Address - Supplies system base address to validate 378 379 Size - Supplies size of address range in bytes. 380 381 Return Value: 382 383 HRESULT 384 385 S_OK if system address is one mapped to a register resource. 386 E_INAVLIDARG otherwise. 387 388 --*/ 389 { 390 HRESULT hr = E_INVALIDARG; 391 ULONG i; 392 FxRegisterResourceInfo* entry = NULL; 393 394 LockResourceTable(); 395 396 for (i = 0; i < m_RegisterResourceTableSizeCe; i++) { 397 entry = &m_RegisterResourceTable[i]; 398 399 if (NULL != entry->m_StartSystemVa && 400 Address == entry->m_StartSystemVa && 401 Length == entry->m_MappedLength) { 402 // 403 // there is a valid mapping. clear it. 404 // 405 FX_VERIFY_WITH_NAME(INTERNAL, CHECK_NOT_NULL(entry->m_EndSystemVa), 406 GetDriverGlobals()->Public.DriverName); 407 408 ClearRegisterResourceEntryLocked(entry); 409 410 hr = S_OK; 411 break; 412 } 413 } 414 415 UnlockResourceTable(); 416 417 return hr; 418 } 419 420 HRESULT 421 FxCmResList::ValidateRegisterSystemBaseAddress ( 422 __in PVOID Address, 423 __out PVOID* UsermodeBaseAddress 424 ) 425 /*++ 426 427 Routine Description: 428 429 This routine checks whether the mapped system base address and size is part 430 of the resources assigned to the device by pnp manager. If so, it returns 431 corresponding user-mode mapped base address. It is applicable 432 only when registers are mapped to user-mode. 433 434 Arguments: 435 436 Address - Supplies system base address to validate 437 438 Return Value: 439 440 HRESULT 441 442 S_OK if system address is one mapped to a register resource. 443 E_INAVLIDARG otherwise. 444 445 --*/ 446 { 447 ULONG i; 448 FxRegisterResourceInfo* entry = NULL; 449 450 LockResourceTable(); 451 for (i = 0; i < m_RegisterResourceTableSizeCe; i++) { 452 entry = &m_RegisterResourceTable[i]; 453 454 if (Address == entry->m_StartSystemVa) { 455 456 FX_VERIFY_WITH_NAME(INTERNAL, CHECK_NOT_NULL(entry->m_StartUsermodeVa), 457 GetDriverGlobals()->Public.DriverName); 458 459 *UsermodeBaseAddress = entry->m_StartUsermodeVa; 460 461 UnlockResourceTable(); 462 return S_OK; 463 } 464 } 465 466 UnlockResourceTable(); 467 return E_INVALIDARG; 468 } 469 470 HRESULT 471 FxCmResList::ValidateRegisterSystemAddressRange ( 472 __in PVOID SystemAddress, 473 __in SIZE_T Length, 474 __out_opt PVOID* UsermodeAddress 475 ) 476 /*++ 477 478 Routine Description: 479 480 This routine checks whether given system mapped address and length is within 481 one of the assigned resource ranges. Optionally, tt computes the usermode 482 address corresponding to the system address. 483 484 Arguments: 485 486 Address - Supplies register address to validate 487 488 Size - Supplies size of address range in bytes. 489 490 UsermodeAddress - returns usermode address corresponding to system address 491 492 Return Value: 493 494 HRESULT 495 496 S_OK if system address range is valid. 497 E_INAVLIDARG otherwise. 498 499 --*/ 500 { 501 HRESULT hr = E_INVALIDARG; 502 FxRegisterResourceInfo* entry = NULL; 503 SIZE_T offset = 0; 504 ULONG i; 505 PVOID start = NULL; 506 PVOID end = NULL; 507 ULONG_PTR tmp; 508 509 // 510 // compute system address range to look for 511 // 512 start = SystemAddress; 513 514 // 515 // Use interger overflow safe functions 516 // end = ((PUCHAR)SystemAddress) + Length - 1; 517 // 518 hr = ULongPtrAdd((ULONG_PTR) start, Length, &tmp); 519 FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO), CHECK("Integer overflow occurred" 520 "when computing register address range", SUCCEEDED(hr)), 521 GetDriverGlobals()->Public.DriverName); 522 523 end = (PVOID)(tmp - 1); 524 525 // 526 // check if range is in the register resource table 527 // 528 hr = E_INVALIDARG; 529 for (i = 0; i < m_RegisterResourceTableSizeCe; i++) { 530 entry = &m_RegisterResourceTable[i]; 531 532 if (start >= entry->m_StartSystemVa && 533 end <= entry->m_EndSystemVa) { 534 hr = S_OK; 535 break; 536 } 537 } 538 539 // 540 // compute the corresponding usermode address 541 // 542 if (SUCCEEDED(hr) && UsermodeAddress != NULL) { 543 offset = ((PUCHAR)SystemAddress) - ((PUCHAR)entry->m_StartSystemVa); 544 *UsermodeAddress = ((PUCHAR)entry->m_StartUsermodeVa) + offset; 545 } 546 547 return hr; 548 } 549 550 SIZE_T 551 FxCmResList::GetResourceLength( 552 __in PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor, 553 __out_opt PHYSICAL_ADDRESS* Start 554 ) 555 /*++ 556 557 Routine Description: 558 559 This routine decodes the length from a CmPartialResourceDescriptor 560 describing a memory resource. 561 562 Arguments: 563 564 Descriptor - Supplies resource descriptor from which to decode length 565 566 Start - Supplies optional buffer into which start address will be stored. 567 568 Return Value: 569 570 Decoded Length 571 572 --*/ 573 { 574 ULONGLONG length; 575 576 length = 0; 577 578 ASSERT((Descriptor->Type == CmResourceTypeMemory) || 579 (Descriptor->Type == CmResourceTypeMemoryLarge) || 580 (Descriptor->Type == CmResourceTypePort)); 581 582 // 583 // If it is not large memory resource than length is in u.Memory.Length. 584 // For large memory resource, the length is given by different fields in 585 // CM_PARTIAL_RESOURCE_DESCRIPTOR structure. 586 // 587 if ((Descriptor->Type == CmResourceTypeMemory) || 588 (Descriptor->Type == CmResourceTypePort)) { 589 length = Descriptor->u.Memory.Length; 590 591 } else if (Descriptor->Flags & CM_RESOURCE_MEMORY_LARGE_40) { 592 length = (((ULONGLONG)Descriptor->u.Memory40.Length40) << 8); 593 594 } else if (Descriptor->Flags & CM_RESOURCE_MEMORY_LARGE_48) { 595 length = (((ULONGLONG)Descriptor->u.Memory48.Length48) << 16); 596 597 } else if (Descriptor->Flags & CM_RESOURCE_MEMORY_LARGE_64) { 598 length = (((ULONGLONG)Descriptor->u.Memory64.Length64) << 32); 599 600 } else { 601 // 602 // It should not be possible to get here. 603 // 604 ASSERT(FALSE); 605 } 606 607 if (Start != NULL) { 608 *Start = Descriptor->u.Generic.Start; 609 } 610 611 // 612 // large memory descriptor is only supported on 64-bit so the casting 613 // below is ok. 614 // 615 return (SIZE_T) length; 616 } 617 618 HRESULT 619 FxCmResList::MapIoSpaceWorker( 620 __in PHYSICAL_ADDRESS PhysicalAddress, 621 __in SIZE_T NumberOfBytes, 622 __in MEMORY_CACHING_TYPE CacheType, 623 __deref_out VOID** PseudoBaseAddress 624 ) 625 { 626 IWudfDeviceStack *deviceStack; 627 PVOID systemAddress; 628 PVOID usermodeAddress; 629 HRESULT hr; 630 FxRegisterResourceInfo* resEntry; 631 632 // 633 // check if this physical resource is among the assigned resources. 634 // If it is, retrieve the table entry corresponding to to register res. 635 // 636 LockResourceTable(); 637 638 hr = ValidateRegisterPhysicalAddressRange(PhysicalAddress, 639 NumberOfBytes, 640 &resEntry); 641 642 FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO), 643 CHECK("Invalid physical address or number of bytes provided", 644 (SUCCEEDED(hr))), GetDriverGlobals()->Public.DriverName); 645 646 *PseudoBaseAddress = NULL; 647 648 // 649 // Call host 650 // 651 deviceStack = GetDevice()->GetDeviceStack(); 652 systemAddress = NULL; 653 usermodeAddress = NULL; 654 655 if(GetDevice()->AreRegistersMappedToUsermode()) { 656 hr = deviceStack->MapIoSpace(PhysicalAddress, 657 NumberOfBytes, 658 CacheType, 659 &systemAddress, 660 &usermodeAddress); 661 } 662 else { 663 hr = deviceStack->MapIoSpace(PhysicalAddress, 664 NumberOfBytes, 665 CacheType, 666 &systemAddress, 667 NULL); 668 } 669 670 if (SUCCEEDED(hr)) { 671 // 672 // update the mapped resource list entry and add it to list 673 // 674 UpdateRegisterResourceEntryLocked(resEntry, 675 systemAddress, 676 NumberOfBytes, 677 usermodeAddress); 678 679 // 680 // Convert system address to pseudo (opaque) base address 681 // 682 *PseudoBaseAddress = GetDevice()->GetPseudoAddressFromSystemAddress( 683 systemAddress 684 ); 685 } 686 687 UnlockResourceTable(); 688 689 return hr; 690 } 691 692 VOID 693 FxCmResList::ValidateResourceUnmap( 694 VOID 695 ) 696 { 697 ULONG i; 698 FxRegisterResourceInfo* entry = NULL; 699 700 // 701 // make sure driver has unmapped its resources. No need to 702 // acquire the resource validation table lock as this is called in 703 // ReleaseHardware pnp callback and cannot race with another framework 704 // pnp callback that updates this table (PrepareHardware) so no invalid 705 // access. If a driver thread unmaps after ReleaseHardware return then also 706 // it will be a valid access of table entry. 707 // 708 709 for (i = 0; i < m_RegisterResourceTableSizeCe; i++) { 710 entry = &m_RegisterResourceTable[i]; 711 712 FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO), CHECK("Driver did not unmap its " 713 "register resources", (entry->m_StartSystemVa == NULL)), GetDriverGlobals()->Public.DriverName); 714 } 715 } 716 717 HRESULT 718 FxCmResList::ValidatePortAddressRange( 719 __in PVOID Address, 720 __in SIZE_T Length 721 ) 722 { 723 ULONG i; 724 HRESULT hr; 725 ULONGLONG driverStartPa, driverEndPa, systemStartPa, systemEndPa; 726 ULONGLONG tmp; 727 FxPortResourceInfo* entry = NULL; 728 729 driverStartPa = (ULONGLONG)Address; 730 731 // 732 // driverEndPa = driverStartPa + Length - 1; 733 // 734 hr = ULongLongAdd(driverStartPa, Length, &tmp); 735 FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO), CHECK("Integer overflow occurred" 736 "when computing port address range", SUCCEEDED(hr)), 737 GetDriverGlobals()->Public.DriverName); 738 739 driverEndPa = tmp - 1; 740 741 for (i = 0; i < m_PortResourceTableSizeCe; i++) { 742 entry = &m_PortResourceTable[i]; 743 744 systemStartPa = entry->m_StartPa.QuadPart; 745 systemEndPa = entry->m_EndPa.QuadPart; 746 747 if (driverStartPa >= systemStartPa && 748 driverEndPa <= systemEndPa) { 749 return S_OK; 750 } 751 } 752 753 return E_INVALIDARG; 754 } 755 756 _Must_inspect_result_ 757 NTSTATUS 758 FxCmResList::CheckForConnectionResources( 759 VOID 760 ) 761 { 762 NTSTATUS status; 763 ULONG i; 764 ULONG count; 765 PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor; 766 767 status = STATUS_SUCCESS; 768 count = GetCount(); 769 770 for (i = 0; i < count; i++) { 771 pDescriptor = GetDescriptor(i); 772 if (pDescriptor == NULL) { 773 status = STATUS_INVALID_DEVICE_STATE; 774 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 775 "Resource Descriptor not found %!STATUS!", status); 776 goto exit; 777 } 778 779 if (pDescriptor->Type == CmResourceTypeConnection) { 780 m_HasConnectionResources = TRUE; 781 break; 782 } 783 } 784 785 exit: 786 return status; 787 } 788 789