win-pvdrivers

view xenpci/evtchn.c @ 1054:471c94d04d8a

Refactoring to support xencache (tmem)
author James Harper <james.harper@bendigoit.com.au>
date Sun Jun 02 16:37:21 2013 +1000 (2013-06-02)
parents 37c0c84a42e8
children 27bd2a5a4704
line source
1 /*
2 PV Drivers for Windows Xen HVM Domains
3 Copyright (C) 2007 James Harper
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
20 #include "xenpci.h"
22 /* Not really necessary but keeps PREfast happy */
23 #if (VER_PRODUCTBUILD >= 7600)
24 static KDEFERRED_ROUTINE EvtChn_DpcBounce;
25 #endif
27 #if defined(_X86_)
28 #define xchg(p1, p2) InterlockedExchange(p1, p2)
29 #define synch_clear_bit(p1, p2) InterlockedBitTestAndReset(p2, p1)
30 #define synch_set_bit(p1, p2) InterlockedBitTestAndSet(p2, p1)
31 #define bit_scan_forward(p1, p2) _BitScanForward(p1, p2)
32 #else
33 #define xchg(p1, p2) InterlockedExchange64(p1, p2)
34 #define synch_clear_bit(p1, p2) InterlockedBitTestAndReset64(p2, p1)
35 #define synch_set_bit(p1, p2) InterlockedBitTestAndSet64(p2, p1)
36 #define bit_scan_forward(p1, p2) _BitScanForward64(p1, p2)
37 #endif
39 #define BITS_PER_LONG (sizeof(xen_ulong_t) * 8)
40 #define BITS_PER_LONG_SHIFT (5 + (sizeof(xen_ulong_t) >> 3))
42 static VOID
43 EvtChn_DpcBounce(PRKDPC Dpc, PVOID Context, PVOID SystemArgument1, PVOID SystemArgument2) {
44 ev_action_t *action = Context;
46 UNREFERENCED_PARAMETER(Dpc);
47 UNREFERENCED_PARAMETER(SystemArgument1);
48 UNREFERENCED_PARAMETER(SystemArgument2);
50 //FUNCTION_ENTER();
52 if (action->type != EVT_ACTION_TYPE_EMPTY) {
53 action->ServiceRoutine(action->ServiceContext);
54 }
55 //FUNCTION_EXIT();
56 }
58 #if 0
59 /* Called at DIRQL */
60 BOOLEAN
61 EvtChn_AckEvent(PVOID context, evtchn_port_t port, BOOLEAN *last_interrupt) {
62 PXENPCI_DEVICE_DATA xpdd = context;
63 ULONG pcpu = KeGetCurrentProcessorNumber() & 0xff;
64 ULONG evt_word;
65 ULONG evt_bit;
66 xen_ulong_t val;
67 int i;
69 evt_bit = port & (BITS_PER_LONG - 1);
70 evt_word = port >> BITS_PER_LONG_SHIFT;
72 val = synch_clear_bit(evt_bit, (volatile xen_long_t *)&xpdd->evtchn_pending_pvt[pcpu][evt_word]);
73 *last_interrupt = TRUE;
74 for (i = 0; i < ARRAY_SIZE(xpdd->evtchn_pending_pvt[pcpu]); i++)
75 {
76 if (xpdd->evtchn_pending_pvt[pcpu][i])
77 {
78 *last_interrupt = FALSE;
79 break;
80 }
81 }
83 return (BOOLEAN)!!val;
84 }
85 #endif
87 //volatile ULONG in_inq = 0;
89 BOOLEAN
90 EvtChn_EvtInterruptIsr(WDFINTERRUPT interrupt, ULONG message_id)
91 {
92 /*
93 For HVM domains, Xen always triggers the event on CPU0. Because the
94 interrupt is delivered via the virtual PCI device it might get delivered
95 to CPU != 0, but we should always use vcpu_info[0]
96 */
97 int vcpu = 0;
98 ULONG pcpu = KeGetCurrentProcessorNumber() & 0xff;
99 vcpu_info_t *vcpu_info;
100 PXENPCI_DEVICE_DATA xpdd = GetXpdd(WdfInterruptGetDevice(interrupt));
101 shared_info_t *shared_info_area = xpdd->shared_info_area;
102 xen_ulong_t evt_words;
103 unsigned long evt_word;
104 unsigned long evt_bit;
105 unsigned int port;
106 ev_action_t *ev_action;
107 BOOLEAN handled = FALSE;
108 BOOLEAN deferred = FALSE;
109 int i;
111 UNREFERENCED_PARAMETER(message_id);
113 if (xpdd->interrupts_masked) {
114 FUNCTION_MSG("unhandled interrupt\n");
115 }
117 if (xpdd->hibernated) {
118 FUNCTION_MSG("interrupt while hibernated\n");
119 }
121 for (i = 0; i < ARRAY_SIZE(xpdd->evtchn_pending_pvt[pcpu]); i++) {
122 if (xpdd->evtchn_pending_pvt[pcpu][i]) {
123 FUNCTION_MSG("Unacknowledged event word = %d, val = %p\n", i, xpdd->evtchn_pending_pvt[pcpu][i]);
124 xpdd->evtchn_pending_pvt[pcpu][i] = 0;
125 }
126 }
128 vcpu_info = &shared_info_area->vcpu_info[vcpu];
130 vcpu_info->evtchn_upcall_pending = 0;
132 if (xpdd->interrupts_masked)
133 {
134 return TRUE;
135 }
137 evt_words = (xen_ulong_t)xchg((volatile xen_long_t *)&vcpu_info->evtchn_pending_sel, 0);
139 while (bit_scan_forward(&evt_word, evt_words))
140 {
141 evt_words &= ~(1 << evt_word);
142 while (bit_scan_forward(&evt_bit, shared_info_area->evtchn_pending[evt_word] & ~shared_info_area->evtchn_mask[evt_word]))
143 {
144 synch_clear_bit(evt_bit, (volatile xen_long_t *)&shared_info_area->evtchn_pending[evt_word]);
145 handled = TRUE;
146 port = (evt_word << BITS_PER_LONG_SHIFT) + evt_bit;
147 ev_action = &xpdd->ev_actions[port];
148 ev_action->count++;
149 switch (ev_action->type)
150 {
151 case EVT_ACTION_TYPE_NORMAL:
152 //FUNCTION_MSG("EVT_ACTION_TYPE_NORMAL port = %d\n", port);
153 ev_action->ServiceRoutine(ev_action->ServiceContext);
154 break;
155 case EVT_ACTION_TYPE_DPC:
156 //FUNCTION_MSG("EVT_ACTION_TYPE_DPC port = %d\n", port);
157 KeInsertQueueDpc(&ev_action->Dpc, NULL, NULL);
158 break;
159 #if 0
160 case EVT_ACTION_TYPE_SUSPEND:
161 FUNCTION_MSG("EVT_ACTION_TYPE_SUSPEND\n");
162 for (i = 0; i < NR_EVENTS; i++)
163 {
164 if (!(xpdd->ev_actions[i].flags & EVT_ACTION_FLAGS_NO_SUSPEND))
165 {
166 switch(xpdd->ev_actions[i].type)
167 {
168 #if 0
169 case EVT_ACTION_TYPE_IRQ:
170 {
171 int suspend_bit = i & (BITS_PER_LONG - 1);
172 int suspend_word = i >> BITS_PER_LONG_SHIFT;
173 synch_set_bit(suspend_bit, (volatile xen_long_t *)&xpdd->evtchn_pending_pvt[pcpu][suspend_word]);
174 }
175 break;
176 #endif
177 case EVT_ACTION_TYPE_NORMAL:
178 if (xpdd->ev_actions[i].ServiceRoutine)
179 {
180 xpdd->ev_actions[i].ServiceRoutine(xpdd->ev_actions[i].ServiceContext);
181 }
182 break;
183 case EVT_ACTION_TYPE_DPC:
184 KeInsertQueueDpc(&xpdd->ev_actions[i].Dpc, NULL, NULL);
185 break;
186 }
187 }
188 }
189 KeInsertQueueDpc(&ev_action->Dpc, NULL, NULL);
190 deferred = TRUE;
191 break;
192 #endif
193 default:
194 FUNCTION_MSG("Unhandled Event!!! port=%d\n", port);
195 break;
196 }
197 }
198 }
200 return handled && !deferred;
201 }
203 NTSTATUS
204 EvtChn_EvtInterruptEnable(WDFINTERRUPT interrupt, WDFDEVICE device)
205 {
206 NTSTATUS status = STATUS_SUCCESS;
208 UNREFERENCED_PARAMETER(interrupt);
209 UNREFERENCED_PARAMETER(device);
211 FUNCTION_ENTER();
212 FUNCTION_EXIT();
214 return status;
215 }
217 NTSTATUS
218 EvtChn_EvtInterruptDisable(WDFINTERRUPT interrupt, WDFDEVICE device)
219 {
220 NTSTATUS status = STATUS_SUCCESS;
222 UNREFERENCED_PARAMETER(interrupt);
223 UNREFERENCED_PARAMETER(device);
225 FUNCTION_ENTER();
226 FUNCTION_EXIT();
228 return status;
229 }
231 NTSTATUS
232 EvtChn_Bind(PVOID Context, evtchn_port_t port, PXN_EVENT_CALLBACK ServiceRoutine, PVOID ServiceContext, ULONG flags)
233 {
234 PXENPCI_DEVICE_DATA xpdd = Context;
235 ev_action_t *action = &xpdd->ev_actions[port];
237 FUNCTION_ENTER();
239 if (InterlockedCompareExchange((volatile LONG *)&action->type, EVT_ACTION_TYPE_NEW, EVT_ACTION_TYPE_EMPTY) != EVT_ACTION_TYPE_EMPTY) {
240 FUNCTION_MSG("Handler for port %d already registered\n", port);
241 return STATUS_UNSUCCESSFUL;
242 }
244 xpdd->ev_actions[port].ServiceRoutine = ServiceRoutine;
245 xpdd->ev_actions[port].ServiceContext = ServiceContext;
246 xpdd->ev_actions[port].xpdd = xpdd;
247 xpdd->ev_actions[port].flags = flags;
248 KeMemoryBarrier();
249 xpdd->ev_actions[port].type = EVT_ACTION_TYPE_NORMAL;
251 EvtChn_Unmask(Context, port);
253 FUNCTION_EXIT();
255 return STATUS_SUCCESS;
256 }
258 NTSTATUS
259 EvtChn_BindDpc(PVOID Context, evtchn_port_t port, PXN_EVENT_CALLBACK ServiceRoutine, PVOID ServiceContext, ULONG flags) {
260 PXENPCI_DEVICE_DATA xpdd = Context;
261 ev_action_t *action = &xpdd->ev_actions[port];
263 FUNCTION_ENTER();
265 if (InterlockedCompareExchange((volatile LONG *)&action->type, EVT_ACTION_TYPE_NEW, EVT_ACTION_TYPE_EMPTY) != EVT_ACTION_TYPE_EMPTY) {
266 FUNCTION_MSG(" Handler for port %d already registered\n", port);
267 return STATUS_UNSUCCESSFUL;
268 }
270 xpdd->ev_actions[port].ServiceRoutine = ServiceRoutine;
271 xpdd->ev_actions[port].ServiceContext = ServiceContext;
272 xpdd->ev_actions[port].xpdd = xpdd;
273 xpdd->ev_actions[port].flags = flags;
274 KeMemoryBarrier(); // make sure that the new service routine is only called once the context is set up
275 InterlockedExchange((volatile LONG *)&action->type, EVT_ACTION_TYPE_DPC);
277 EvtChn_Unmask(Context, port);
279 FUNCTION_EXIT();
281 return STATUS_SUCCESS;
282 }
284 #if 0
285 NTSTATUS
286 EvtChn_BindIrq(PVOID Context, evtchn_port_t port, ULONG vector, PCHAR description, ULONG flags)
287 {
288 PXENPCI_DEVICE_DATA xpdd = Context;
289 ev_action_t *action = &xpdd->ev_actions[port];
291 FUNCTION_ENTER();
293 if (InterlockedCompareExchange((volatile LONG *)&action->type, EVT_ACTION_TYPE_NEW, EVT_ACTION_TYPE_EMPTY) != EVT_ACTION_TYPE_EMPTY)
294 {
295 FUNCTION_MSG("Handler for port %d already registered\n", port);
296 return STATUS_UNSUCCESSFUL;
297 }
299 xpdd->ev_actions[port].vector = vector;
300 xpdd->ev_actions[port].xpdd = xpdd;
301 KeMemoryBarrier();
302 xpdd->ev_actions[port].type = EVT_ACTION_TYPE_IRQ;
303 RtlStringCbCopyA(xpdd->ev_actions[port].description, 128, description);
304 xpdd->ev_actions[port].flags = flags;
306 EvtChn_Unmask(Context, port);
308 FUNCTION_EXIT();
310 return STATUS_SUCCESS;
311 }
312 #endif
314 NTSTATUS
315 EvtChn_Unbind(PVOID Context, evtchn_port_t port) {
316 PXENPCI_DEVICE_DATA xpdd = Context;
317 ev_action_t *action = &xpdd->ev_actions[port];
318 int old_type;
320 EvtChn_Mask(Context, port);
321 old_type = InterlockedExchange((volatile LONG *)&action->type, EVT_ACTION_TYPE_EMPTY);
323 if (old_type == EVT_ACTION_TYPE_DPC) { // || old_type == EVT_ACTION_TYPE_SUSPEND) {
324 KeRemoveQueueDpc(&xpdd->ev_actions[port].Dpc);
325 #if (NTDDI_VERSION >= NTDDI_WINXP)
326 KeFlushQueuedDpcs();
327 #endif
328 }
330 KeMemoryBarrier(); // make sure we don't call the old Service Routine with the new data...
331 xpdd->ev_actions[port].ServiceRoutine = NULL;
332 xpdd->ev_actions[port].ServiceContext = NULL;
334 return STATUS_SUCCESS;
335 }
337 NTSTATUS
338 EvtChn_Mask(PVOID Context, evtchn_port_t port) {
339 PXENPCI_DEVICE_DATA xpdd = Context;
341 synch_set_bit(port & (BITS_PER_LONG - 1),
342 (volatile xen_long_t *)&xpdd->shared_info_area->evtchn_mask[port >> BITS_PER_LONG_SHIFT]);
343 return STATUS_SUCCESS;
344 }
346 NTSTATUS
347 EvtChn_Unmask(PVOID context, evtchn_port_t port) {
348 PXENPCI_DEVICE_DATA xpdd = context;
350 synch_clear_bit(port & (BITS_PER_LONG - 1),
351 (volatile xen_long_t *)&xpdd->shared_info_area->evtchn_mask[port >> BITS_PER_LONG_SHIFT]);
352 return STATUS_SUCCESS;
353 }
355 NTSTATUS
356 EvtChn_Notify(PVOID context, evtchn_port_t port) {
357 struct evtchn_send send;
359 UNREFERENCED_PARAMETER(context);
360 send.port = port;
361 (void)HYPERVISOR_event_channel_op(EVTCHNOP_send, &send);
362 return STATUS_SUCCESS;
363 }
365 evtchn_port_t
366 EvtChn_AllocIpi(PVOID context, ULONG vcpu) {
367 evtchn_bind_ipi_t op;
369 UNREFERENCED_PARAMETER(context);
370 FUNCTION_ENTER();
371 op.vcpu = vcpu;
372 op.port = 0;
373 HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, &op);
374 FUNCTION_EXIT();
375 return op.port;
376 }
378 evtchn_port_t
379 EvtChn_AllocUnbound(PVOID context, domid_t Domain) {
380 evtchn_alloc_unbound_t op;
382 UNREFERENCED_PARAMETER(context);
383 op.dom = DOMID_SELF;
384 op.remote_dom = Domain;
385 HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &op);
386 return op.port;
387 }
389 VOID
390 EvtChn_Close(PVOID context, evtchn_port_t port) {
391 evtchn_close_t op;
393 UNREFERENCED_PARAMETER(context);
394 op.port = port;
395 HYPERVISOR_event_channel_op(EVTCHNOP_close, &op);
396 return;
397 }
399 #if 0
400 VOID
401 EvtChn_PdoEventChannelDpc(PVOID context)
402 {
403 PXENPCI_DEVICE_DATA xpdd = context;
405 FUNCTION_ENTER();
406 KeSetEvent(&xpdd->pdo_suspend_event, IO_NO_INCREMENT, FALSE);
407 FUNCTION_EXIT();
408 }
409 #endif
411 NTSTATUS
412 EvtChn_Init(PXENPCI_DEVICE_DATA xpdd)
413 {
414 ULONGLONG result;
415 ev_action_t *action;
416 int i;
418 FUNCTION_ENTER();
420 for (i = 0; i < NR_EVENTS; i++)
421 {
422 EvtChn_Mask(xpdd, i);
423 action = &xpdd->ev_actions[i];
424 action->type = EVT_ACTION_TYPE_EMPTY;
425 //action->port = -1;
426 action->count = 0;
427 KeInitializeDpc(&action->Dpc, EvtChn_DpcBounce, action);
428 }
430 for (i = 0; i < 8; i++) {
431 xpdd->shared_info_area->evtchn_pending[i] = 0;
432 }
434 for (i = 0; i < MAX_VIRT_CPUS; i++) {
435 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_pending = 0;
436 xpdd->shared_info_area->vcpu_info[i].evtchn_pending_sel = 0;
437 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 1; /* apparantly this doesn't do anything */
438 }
440 KeMemoryBarrier();
442 result = hvm_set_parameter(HVM_PARAM_CALLBACK_IRQ, xpdd->irq_number);
443 FUNCTION_MSG("hvm_set_parameter(HVM_PARAM_CALLBACK_IRQ, %d) = %d\n", xpdd->irq_number, (ULONG)result);
445 for (i = 0; i < MAX_VIRT_CPUS; i++)
446 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 0;
447 xpdd->interrupts_masked = FALSE;
448 KeMemoryBarrier();
450 #if 0
451 KeInitializeEvent(&xpdd->pdo_suspend_event, SynchronizationEvent, FALSE);
452 xpdd->pdo_event_channel = EvtChn_AllocIpi(xpdd, 0);
453 EvtChn_BindDpc(xpdd, xpdd->pdo_event_channel, EvtChn_PdoEventChannelDpc, xpdd, EVT_ACTION_FLAGS_DEFAULT);
454 xpdd->ev_actions[xpdd->pdo_event_channel].type = EVT_ACTION_TYPE_SUSPEND; /* override dpc type */
455 FUNCTION_MSG("pdo_event_channel = %d\n", xpdd->pdo_event_channel);
456 #endif
458 FUNCTION_EXIT();
460 return STATUS_SUCCESS;
461 }
463 NTSTATUS
464 EvtChn_Suspend(PXENPCI_DEVICE_DATA xpdd)
465 {
466 int i;
468 xpdd->interrupts_masked = TRUE;
469 for (i = 0; i < MAX_VIRT_CPUS; i++)
470 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 1;
471 KeMemoryBarrier();
472 hvm_set_parameter(HVM_PARAM_CALLBACK_IRQ, 0);
474 for (i = 0; i < NR_EVENTS; i++) {
475 if (xpdd->ev_actions[i].type == EVT_ACTION_TYPE_DPC) {
476 KeRemoveQueueDpc(&xpdd->ev_actions[i].Dpc);
477 }
478 }
479 #if (NTDDI_VERSION >= NTDDI_WINXP)
480 KeFlushQueuedDpcs();
481 #endif
482 return STATUS_SUCCESS;
483 }
485 NTSTATUS
486 EvtChn_Resume(PXENPCI_DEVICE_DATA xpdd)
487 {
488 return EvtChn_Init(xpdd);