kvm-guest-drivers-windows

Форк
0
326 строк · 9.8 Кб
1
/*
2
 * Copyright (C) 2019 Red Hat, Inc.
3
 *
4
 * Written By: Gal Hammer <ghammer@redhat.com>
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met :
9
 * 1. Redistributions of source code must retain the above copyright
10
 *    notice, this list of conditions and the following disclaimer.
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
 * 3. Neither the names of the copyright holders nor the names of their contributors
15
 *    may be used to endorse or promote products derived from this software
16
 *    without specific prior written permission.
17
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29

30
#include "viofs.h"
31
#include "isrdpc.tmh"
32

33
static NTSTATUS MessageToQueueIdxs(BOOLEAN Signaled, ULONG Number,
34
                                   ULONG NumQueues,
35
                                   PULONG vq_idx_begin, PULONG vq_idx_end)
36
{
37
    if (Signaled)
38
    {
39
        if (Number == VQ_TYPE_HIPRIO)
40
        {
41
            *vq_idx_begin = 0;
42
            *vq_idx_end = 1;
43
        }
44
        else if (Number == VQ_TYPE_REQUEST)
45
        {
46
            *vq_idx_begin = 1;
47
            *vq_idx_end = NumQueues;
48
        }
49
        else
50
        {
51
            TraceEvents(TRACE_LEVEL_ERROR, DBG_INTERRUPT,
52
                "Can't find VQ for MessageNumber: %lu", Number);
53

54
            return STATUS_UNSUCCESSFUL;
55
        }
56
    }
57
    else
58
    {
59
        *vq_idx_begin = 0;
60
        *vq_idx_end = NumQueues;
61
    }
62

63
    return STATUS_SUCCESS;
64
}
65

66
NTSTATUS VirtFsEvtInterruptEnable(IN WDFINTERRUPT Interrupt,
67
                                  IN WDFDEVICE AssociatedDevice)
68
{
69
    PDEVICE_CONTEXT context;
70
    WDF_INTERRUPT_INFO info;
71
    ULONG vq_idx_begin = 0, vq_idx_end = 0;
72
    NTSTATUS status;
73

74
    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT,
75
        "--> %!FUNC! Interrupt: %p Device: %p",
76
        Interrupt, AssociatedDevice);
77

78
    context = GetDeviceContext(WdfInterruptGetDevice(Interrupt));
79

80
    WDF_INTERRUPT_INFO_INIT(&info);
81
    WdfInterruptGetInfo(Interrupt, &info);
82

83
    status = MessageToQueueIdxs(info.MessageSignaled, info.MessageNumber,
84
        context->NumQueues, &vq_idx_begin, &vq_idx_end);
85

86
    for (ULONG i = vq_idx_begin; i < vq_idx_end; i++)
87
    {
88
        struct virtqueue *vq = context->VirtQueues[i];
89

90
        virtqueue_enable_cb(vq);
91
        virtqueue_kick(vq);
92
    }
93

94
    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "<-- %!FUNC!");
95

96
    return status;
97
}
98

99
NTSTATUS VirtFsEvtInterruptDisable(IN WDFINTERRUPT Interrupt,
100
                                   IN WDFDEVICE AssociatedDevice)
101
{
102
    PDEVICE_CONTEXT context;
103
    WDF_INTERRUPT_INFO info;
104
    ULONG vq_idx_begin = 0, vq_idx_end = 0;
105
    NTSTATUS status;
106

107
    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT,
108
        "--> %!FUNC! Interrupt: %p Device: %p",
109
        Interrupt, AssociatedDevice);
110

111
    context = GetDeviceContext(WdfInterruptGetDevice(Interrupt));
112

113
    WDF_INTERRUPT_INFO_INIT(&info);
114
    WdfInterruptGetInfo(Interrupt, &info);
115

116
    status = MessageToQueueIdxs(info.MessageSignaled, info.MessageNumber,
117
        context->NumQueues, &vq_idx_begin, &vq_idx_end);
118

119
    for (ULONG i = vq_idx_begin; i < vq_idx_end; i++)
120
    {
121
        struct virtqueue *vq = context->VirtQueues[i];
122

123
        virtqueue_disable_cb(vq);
124
    }
125

126
    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "<-- %!FUNC!");
127

128
    return status;
129
}
130

131
BOOLEAN VirtFsEvtInterruptIsr(IN WDFINTERRUPT Interrupt, IN ULONG MessageId)
132
{
133
    PDEVICE_CONTEXT context;
134
    WDF_INTERRUPT_INFO info;
135
    BOOLEAN serviced;
136

137
    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT,
138
        "--> %!FUNC! Interrupt: %p MessageId: %u", Interrupt, MessageId);
139

140
    context = GetDeviceContext(WdfInterruptGetDevice(Interrupt));
141

142
    WDF_INTERRUPT_INFO_INIT(&info);
143
    WdfInterruptGetInfo(Interrupt, &info);
144

145
    if ((info.MessageSignaled && (MessageId < VQ_TYPE_MAX)) ||
146
        VirtIOWdfGetISRStatus(&context->VDevice))
147
    {
148
        WdfInterruptQueueDpcForIsr(Interrupt);
149
        serviced = TRUE;
150
    }
151
    else
152
    {
153
        serviced = FALSE;
154
    }
155

156
    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "<-- %!FUNC!");
157

158
    return serviced;
159
}
160

161
BOOLEAN VirtFsDequeueRequest(PDEVICE_CONTEXT Context, PVIRTIO_FS_REQUEST Req)
162
{
163
    PSINGLE_LIST_ENTRY iter;
164
    BOOLEAN found = FALSE;
165

166
    WdfSpinLockAcquire(Context->RequestsLock);
167
    iter = &Context->RequestsList;
168
    while (iter->Next != NULL)
169
    {
170
        PVIRTIO_FS_REQUEST removed = CONTAINING_RECORD(iter->Next,
171
            VIRTIO_FS_REQUEST, ListEntry);
172

173
        if (Req == removed)
174
        {
175
            TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC,
176
                "Delete %p Request: %p", removed, removed->Request);
177
            iter->Next = removed->ListEntry.Next;
178
            found = TRUE;
179
            break;
180
        }
181
        else
182
        {
183
            iter = iter->Next;
184
        }
185
    };
186
    WdfSpinLockRelease(Context->RequestsLock);
187
    return found;
188
}
189

190
static VOID VirtFsReadFromQueue(PDEVICE_CONTEXT context,
191
                                struct virtqueue *vq,
192
                                WDFSPINLOCK vq_lock)
193
{
194
    PVIRTIO_FS_REQUEST fs_req;
195
    NTSTATUS status = STATUS_SUCCESS;
196
    unsigned int length;
197

198
    for (;;)
199
    {
200
        WdfSpinLockAcquire(vq_lock);
201

202
        fs_req = virtqueue_get_buf(vq, &length);
203
        if (fs_req == NULL)
204
        {
205
            WdfSpinLockRelease(vq_lock);
206
            break;
207
        }
208

209
        WdfSpinLockRelease(vq_lock);
210

211
        TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC,
212
            "Got %p Request: %p", fs_req, fs_req->Request);
213

214
        VirtFsDequeueRequest(context, fs_req);
215

216
        if (fs_req->Request != NULL)
217
        {
218
            // TODO: why are we sure the wdfReq is valid and not destroyed yet?
219
            NTSTATUS status2 = WdfRequestUnmarkCancelable(fs_req->Request);
220
            TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC,
221
                "request %p -> uncancellable = %X", fs_req->Request, status2);
222
            if (status2 == STATUS_CANCELLED)
223
            {
224
                fs_req->Request = NULL;
225
            }
226
        }
227

228
        if (fs_req->Request != NULL)
229
        {
230
#if !VIRT_FS_DMAR
231
            PUCHAR out_buf;
232
            size_t out_len;
233
            PVOID out_buf_va;
234

235
            status = WdfRequestRetrieveOutputBuffer(fs_req->Request, length,
236
                &out_buf, &out_len);
237

238
            if (NT_SUCCESS(status))
239
            {
240
                length = min(length, (unsigned)out_len);
241

242
                out_buf_va = MmMapLockedPagesSpecifyCache(
243
                    fs_req->OutputBuffer, KernelMode, MmNonCached, NULL,
244
                    FALSE, NormalPagePriority);
245

246
                if (out_buf_va != NULL)
247
                {
248
                    RtlCopyMemory(out_buf, out_buf_va, length);
249
                    MmUnmapLockedPages(out_buf_va, fs_req->OutputBuffer);
250
                }
251
                else
252
                {
253
                    TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL,
254
                        "MmMapLockedPages failed");
255
                    status = STATUS_INSUFFICIENT_RESOURCES;
256
                    length = 0;
257
                }
258
            }
259
            else
260
            {
261
                TraceEvents(TRACE_LEVEL_ERROR, DBG_DPC,
262
                    "WdfRequestRetrieveOutputBuffer failed");
263
            }
264
#else
265
            VirtIOWdfDeviceDmaTxComplete(&context->VDevice.VIODevice,
266
                                         fs_req->H2D_Params.transaction);
267
            VirtIOWdfDeviceDmaRxComplete(&context->VDevice.VIODevice,
268
                                         fs_req->D2H_Params.transaction,
269
                                         length);
270
#endif
271

272
            TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC,
273
                "Complete Request: %p Status: %!STATUS! Length: %d",
274
                fs_req->Request, status, length);
275

276
            WdfRequestCompleteWithInformation(fs_req->Request, status,
277
                (ULONG_PTR)length);
278
        }
279

280
        FreeVirtFsRequest(fs_req);
281
    }
282
}
283

284
VOID VirtFsEvtInterruptDpc(IN WDFINTERRUPT Interrupt,
285
                           IN WDFOBJECT AssociatedObject)
286
{
287
    PDEVICE_CONTEXT context;
288
    WDF_INTERRUPT_INFO info;
289
    struct virtqueue *vq = NULL;
290
    WDFSPINLOCK vq_lock = NULL;
291
    ULONG i;
292

293
    UNREFERENCED_PARAMETER(AssociatedObject);
294

295
    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC,
296
        "--> %!FUNC! Interrupt: %p", Interrupt);
297

298
    context = GetDeviceContext(WdfInterruptGetDevice(Interrupt));
299

300
    WDF_INTERRUPT_INFO_INIT(&info);
301
    WdfInterruptGetInfo(Interrupt, &info);
302

303
    if ((info.MessageSignaled == TRUE) &&
304
        (info.MessageNumber < VQ_TYPE_MAX))
305
    {
306
        vq = context->VirtQueues[info.MessageNumber];
307
        vq_lock = context->VirtQueueLocks[info.MessageNumber];
308
    }
309

310
    if (vq != NULL)
311
    {
312
        VirtFsReadFromQueue(context, vq, vq_lock);
313
    }
314
    else
315
    {
316
        for (i = 0; i < context->NumQueues; i++)
317
        {
318
            vq = context->VirtQueues[i];
319
            vq_lock = context->VirtQueueLocks[i];
320

321
            VirtFsReadFromQueue(context, vq, vq_lock);
322
        }
323
    }
324

325
    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "<-- %!FUNC!");
326
}
327

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.