win-pvdrivers

view xenpci/evtchn.c @ 1030:37c0c84a42e8

Tidy up KdPrints
author James Harper <james.harper@bendigoit.com.au>
date Wed Feb 20 20:49:26 2013 +1100 (2013-02-20)
parents 4f7d5a8636bd
children 471c94d04d8a
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 PXENPCI_DEVICE_DATA xpdd = Context;
358 struct evtchn_send send;
360 send.port = port;
361 (void)HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_send, &send);
362 return STATUS_SUCCESS;
363 }
365 evtchn_port_t
366 EvtChn_AllocIpi(PVOID context, ULONG vcpu) {
367 PXENPCI_DEVICE_DATA xpdd = context;
368 evtchn_bind_ipi_t op;
370 FUNCTION_ENTER();
371 op.vcpu = vcpu;
372 op.port = 0;
373 HYPERVISOR_event_channel_op(xpdd, 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 PXENPCI_DEVICE_DATA xpdd = Context;
381 evtchn_alloc_unbound_t op;
382 op.dom = DOMID_SELF;
383 op.remote_dom = Domain;
384 HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_alloc_unbound, &op);
385 return op.port;
386 }
388 VOID
389 EvtChn_Close(PVOID Context, evtchn_port_t port) {
390 PXENPCI_DEVICE_DATA xpdd = Context;
391 evtchn_close_t op;
392 op.port = port;
393 HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_close, &op);
394 return;
395 }
397 #if 0
398 VOID
399 EvtChn_PdoEventChannelDpc(PVOID context)
400 {
401 PXENPCI_DEVICE_DATA xpdd = context;
403 FUNCTION_ENTER();
404 KeSetEvent(&xpdd->pdo_suspend_event, IO_NO_INCREMENT, FALSE);
405 FUNCTION_EXIT();
406 }
407 #endif
409 NTSTATUS
410 EvtChn_Init(PXENPCI_DEVICE_DATA xpdd)
411 {
412 ULONGLONG result;
413 ev_action_t *action;
414 int i;
416 FUNCTION_ENTER();
418 for (i = 0; i < NR_EVENTS; i++)
419 {
420 EvtChn_Mask(xpdd, i);
421 action = &xpdd->ev_actions[i];
422 action->type = EVT_ACTION_TYPE_EMPTY;
423 //action->port = -1;
424 action->count = 0;
425 KeInitializeDpc(&action->Dpc, EvtChn_DpcBounce, action);
426 }
428 for (i = 0; i < 8; i++) {
429 xpdd->shared_info_area->evtchn_pending[i] = 0;
430 }
432 for (i = 0; i < MAX_VIRT_CPUS; i++) {
433 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_pending = 0;
434 xpdd->shared_info_area->vcpu_info[i].evtchn_pending_sel = 0;
435 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 1; /* apparantly this doesn't do anything */
436 }
438 KeMemoryBarrier();
440 result = hvm_set_parameter(xpdd, HVM_PARAM_CALLBACK_IRQ, xpdd->irq_number);
441 FUNCTION_MSG("hvm_set_parameter(HVM_PARAM_CALLBACK_IRQ, %d) = %d\n", xpdd->irq_number, (ULONG)result);
443 for (i = 0; i < MAX_VIRT_CPUS; i++)
444 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 0;
445 xpdd->interrupts_masked = FALSE;
446 KeMemoryBarrier();
448 #if 0
449 KeInitializeEvent(&xpdd->pdo_suspend_event, SynchronizationEvent, FALSE);
450 xpdd->pdo_event_channel = EvtChn_AllocIpi(xpdd, 0);
451 EvtChn_BindDpc(xpdd, xpdd->pdo_event_channel, EvtChn_PdoEventChannelDpc, xpdd, EVT_ACTION_FLAGS_DEFAULT);
452 xpdd->ev_actions[xpdd->pdo_event_channel].type = EVT_ACTION_TYPE_SUSPEND; /* override dpc type */
453 FUNCTION_MSG("pdo_event_channel = %d\n", xpdd->pdo_event_channel);
454 #endif
456 FUNCTION_EXIT();
458 return STATUS_SUCCESS;
459 }
461 NTSTATUS
462 EvtChn_Suspend(PXENPCI_DEVICE_DATA xpdd)
463 {
464 int i;
466 xpdd->interrupts_masked = TRUE;
467 for (i = 0; i < MAX_VIRT_CPUS; i++)
468 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 1;
469 KeMemoryBarrier();
470 hvm_set_parameter(xpdd, HVM_PARAM_CALLBACK_IRQ, 0);
472 for (i = 0; i < NR_EVENTS; i++) {
473 if (xpdd->ev_actions[i].type == EVT_ACTION_TYPE_DPC) {
474 KeRemoveQueueDpc(&xpdd->ev_actions[i].Dpc);
475 }
476 }
477 #if (NTDDI_VERSION >= NTDDI_WINXP)
478 KeFlushQueuedDpcs();
479 #endif
480 return STATUS_SUCCESS;
481 }
483 NTSTATUS
484 EvtChn_Resume(PXENPCI_DEVICE_DATA xpdd)
485 {
486 return EvtChn_Init(xpdd);