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