1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxCommonBuffer.cpp
8 
9 Abstract:
10 
11     WDF CommonBuffer Object
12 
13 Environment:
14 
15     Kernel mode only.
16 
17 Notes:
18 
19 
20 Revision History:
21 
22 --*/
23 
24 #include "fxdmapch.hpp"
25 
26 extern "C" {
27 // #include "FxCommonBuffer.tmh"
28 }
29 
30 FxCommonBuffer::FxCommonBuffer(
31     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
32     __in FxDmaEnabler * pDmaEnabler
33     ) :
34     FxNonPagedObject(FX_TYPE_COMMON_BUFFER, sizeof(FxCommonBuffer), FxDriverGlobals)
35 {
36     m_DmaEnabler           = pDmaEnabler;
37     m_BufferRawVA          = NULL;   // allocated buffer base (unaligned)
38     m_BufferAlignedVA      = NULL;   // aligned buffer base
39     m_BufferAlignedLA.QuadPart = NULL;   // aligned physical buffer base
40     m_BufferRawLA.QuadPart = NULL;   // allocated buffer phy base (unaligned)
41     m_Length               = 0;
42     m_RawLength            = 0;
43 
44     MarkDisposeOverride(ObjectDoNotLock);
45 
46     //
47     // By default use the alignment of the dma enabler.
48     //
49     m_Alignment = m_DmaEnabler->GetAlignment();
50 }
51 
52 BOOLEAN
53 FxCommonBuffer::Dispose()
54 {
55     FreeCommonBuffer();
56     return TRUE;
57 }
58 
59 _Must_inspect_result_
60 NTSTATUS
61 FxCommonBuffer::AllocateCommonBuffer(
62     __in size_t  Length
63     )
64 {
65     NTSTATUS status;
66     PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
67     ULONGLONG offset;
68     ULONG result;
69 
70     //
71     // Must be running at PASIVE_LEVEL
72     //
73     status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
74     if (!NT_SUCCESS(status)) {
75         return status;
76     }
77 
78     m_Length = Length;
79 
80     //
81     // If required, add alignment to the length.
82     // If alignment is <= page-1, we actually don't need to do it b/c
83     // AllocateCommonBuffer allocates at least a page of memory, regardless
84     // of the requested Length. If driver version is < v1.11, we still add the
85     // alignment to the length for compatibility.
86     //
87     if (m_Alignment > PAGE_SIZE-1 ||
88         pFxDriverGlobals->IsVersionGreaterThanOrEqualTo(1,11) == FALSE) {
89 
90         status = RtlSizeTAdd(Length, m_Alignment, &m_RawLength);
91         if (!NT_SUCCESS(status)) {
92             DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
93                 "WDFDMAENABLER %p AllocateCommonBuffer:  overflow when adding Length "
94                 "%I64d + Alignment %I64d", GetObjectHandle(), Length, m_Alignment);
95             FxVerifierDbgBreakPoint(pFxDriverGlobals);
96             return status;
97         }
98     }
99     else {
100         m_RawLength = Length;
101     }
102 
103     m_DmaEnabler->AllocateCommonBuffer(m_RawLength,
104                                        &m_BufferRawVA,
105                                        &m_BufferRawLA);
106     if (m_BufferRawVA) {
107 
108         m_BufferAlignedVA = FX_ALIGN_VIRTUAL_ADDRESS(m_BufferRawVA, m_Alignment);
109         m_BufferAlignedLA.QuadPart = FX_ALIGN_LOGICAL_ADDRESS(m_BufferRawLA, m_Alignment);
110 
111         if (m_Alignment > PAGE_SIZE-1) {
112             //
113             // If the alignment mask is over a page-size then the aligned virtual
114             // and aligned logical could be pointing to different locations
115             // in memory. So ajdust the VA to match the LA address by adding
116             // the offset of alignedLA and RawLA to VA. By doing this we
117             // only guarantee alignment of LA when the page alignment exceeds PAGE_SIZE.
118             //
119             status = RtlULongLongSub(m_BufferAlignedLA.QuadPart,
120                                      m_BufferRawLA.QuadPart,
121                                      &offset);
122             if (!NT_SUCCESS(status)) {
123                 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
124                     "WDFDMAENABLER %p AllocateCommonBuffer:  overflow when subtracting "
125                     "RawLA %I64x from AlignedLA %I64x",
126                     GetObjectHandle(), m_BufferRawLA.QuadPart, m_BufferAlignedLA.QuadPart);
127                 FxVerifierDbgBreakPoint(pFxDriverGlobals);
128                 return status;
129             }
130 
131             status = RtlULongLongToULong(offset, &result);
132             if (!NT_SUCCESS(status)) {
133                 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
134                     "WDFDMAENABLER %p AllocateCommonBuffer:  overflow when  "
135                     "converting from ULongLong %I64d to ULong",
136                     GetObjectHandle(), offset);
137                 FxVerifierDbgBreakPoint(pFxDriverGlobals);
138                 return status;
139             }
140 
141             m_BufferAlignedVA = WDF_PTR_ADD_OFFSET(m_BufferRawVA, result);
142         }
143         else {
144             ASSERT(m_BufferAlignedVA == m_BufferRawVA);
145             ASSERT(m_BufferAlignedLA.QuadPart == m_BufferRawLA.QuadPart);
146         }
147     } else {
148         m_Length                   = 0;
149         m_RawLength                = 0;
150         m_BufferAlignedVA          = NULL;
151         m_BufferAlignedLA.QuadPart = NULL;
152         return STATUS_INSUFFICIENT_RESOURCES;
153     }
154 
155     return status;
156 }
157 
158 VOID
159 FxCommonBuffer::FreeCommonBuffer(
160     VOID
161     )
162 {
163     //
164     // Free this CommonBuffer per DmaEnabler
165     //
166     if (m_BufferRawVA != NULL) {
167         m_DmaEnabler->FreeCommonBuffer((ULONG) m_RawLength,
168                                         m_BufferRawVA,
169                                         m_BufferRawLA);
170     }
171 }
172 
173 
174