win-pvdrivers

view xenpci/evtchn.c @ 880:e315b8490131

Make xenpci an export driver to remove some of the cumbersome function table stuff.
Cleanups
author James Harper <james.harper@bendigoit.com.au>
date Tue Mar 15 12:22:19 2011 +1100 (2011-03-15)
parents 2f4e64007b0f
children 1ee7940af105
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 (NTDDI_VERSION >= NTDDI_WINXP)
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 {
45 ev_action_t *action = Context;
47 UNREFERENCED_PARAMETER(Dpc);
48 UNREFERENCED_PARAMETER(SystemArgument1);
49 UNREFERENCED_PARAMETER(SystemArgument2);
51 //FUNCTION_ENTER();
53 if (action->type != EVT_ACTION_TYPE_EMPTY)
54 {
55 action->ServiceRoutine(action->ServiceContext);
56 }
57 //FUNCTION_EXIT();
58 }
60 /* Called at DIRQL */
61 BOOLEAN
62 EvtChn_AckEvent(PVOID context, evtchn_port_t port, BOOLEAN *last_interrupt)
63 {
64 PXENPCI_DEVICE_DATA xpdd = context;
65 ULONG pcpu = KeGetCurrentProcessorNumber() & 0xff;
66 ULONG evt_word;
67 ULONG evt_bit;
68 xen_ulong_t val;
69 int i;
71 evt_bit = port & (BITS_PER_LONG - 1);
72 evt_word = port >> BITS_PER_LONG_SHIFT;
74 val = synch_clear_bit(evt_bit, (volatile xen_long_t *)&xpdd->evtchn_pending_pvt[pcpu][evt_word]);
75 *last_interrupt = TRUE;
76 for (i = 0; i < sizeof(xen_ulong_t) * 8; i++)
77 {
78 if (xpdd->evtchn_pending_pvt[pcpu][i])
79 {
80 *last_interrupt = FALSE;
81 break;
82 }
83 }
85 return (BOOLEAN)!!val;
86 }
88 volatile ULONG in_inq = 0;
90 BOOLEAN
91 EvtChn_EvtInterruptIsr(WDFINTERRUPT interrupt, ULONG message_id)
92 {
93 /*
94 For HVM domains, Xen always triggers the event on CPU0. Because the
95 interrupt is delivered via the virtual PCI device it might get delivered
96 to CPU != 0, but we should always use vcpu_info[0]
97 */
98 int vcpu = 0;
99 ULONG pcpu = KeGetCurrentProcessorNumber() & 0xff;
100 vcpu_info_t *vcpu_info;
101 PXENPCI_DEVICE_DATA xpdd = GetXpdd(WdfInterruptGetDevice(interrupt));
102 shared_info_t *shared_info_area = xpdd->shared_info_area;
103 xen_ulong_t evt_words;
104 unsigned long evt_word;
105 unsigned long evt_bit;
106 unsigned int port;
107 ev_action_t *ev_action;
108 BOOLEAN handled = FALSE;
109 BOOLEAN deferred = FALSE;
110 int i;
112 UNREFERENCED_PARAMETER(message_id);
114 if (xpdd->interrupts_masked)
115 {
116 KdPrint((__DRIVER_NAME " unhandled interrupt\n"));
117 }
119 if (xpdd->hibernated)
120 {
121 KdPrint((__DRIVER_NAME " interrupt while hibernated\n"));
122 }
124 for (i = 0; i < ARRAY_SIZE(xpdd->evtchn_pending_pvt[pcpu]); i++)
125 {
126 if (xpdd->evtchn_pending_pvt[pcpu][i])
127 {
128 KdPrint((__DRIVER_NAME " Unacknowledged event word = %d, val = %p\n", i, xpdd->evtchn_pending_pvt[pcpu][i]));
129 xpdd->evtchn_pending_pvt[pcpu][i] = 0;
130 }
131 }
133 vcpu_info = &shared_info_area->vcpu_info[vcpu];
135 vcpu_info->evtchn_upcall_pending = 0;
137 if (xpdd->interrupts_masked)
138 {
139 return TRUE;
140 }
142 evt_words = (xen_ulong_t)xchg((volatile xen_long_t *)&vcpu_info->evtchn_pending_sel, 0);
144 while (bit_scan_forward(&evt_word, evt_words))
145 {
146 evt_words &= ~(1 << evt_word);
147 while (bit_scan_forward(&evt_bit, shared_info_area->evtchn_pending[evt_word] & ~shared_info_area->evtchn_mask[evt_word]))
148 {
149 synch_clear_bit(evt_bit, (volatile xen_long_t *)&shared_info_area->evtchn_pending[evt_word]);
150 handled = TRUE;
151 port = (evt_word << BITS_PER_LONG_SHIFT) + evt_bit;
152 ev_action = &xpdd->ev_actions[port];
153 ev_action->count++;
154 switch (ev_action->type)
155 {
156 case EVT_ACTION_TYPE_NORMAL:
157 //KdPrint((__DRIVER_NAME " EVT_ACTION_TYPE_NORMAL port = %d\n", port));
158 ev_action->ServiceRoutine(ev_action->ServiceContext);
159 break;
160 case EVT_ACTION_TYPE_IRQ:
161 //KdPrint((__DRIVER_NAME " EVT_ACTION_TYPE_IRQ port = %d\n", port));
162 synch_set_bit(evt_bit, (volatile xen_long_t *)&xpdd->evtchn_pending_pvt[pcpu][evt_word]);
163 deferred = TRUE;
164 break;
165 case EVT_ACTION_TYPE_DPC:
166 //KdPrint((__DRIVER_NAME " EVT_ACTION_TYPE_DPC port = %d\n", port));
167 KeInsertQueueDpc(&ev_action->Dpc, NULL, NULL);
168 break;
169 case EVT_ACTION_TYPE_SUSPEND:
170 KdPrint((__DRIVER_NAME " EVT_ACTION_TYPE_SUSPEND\n"));
171 for (i = 0; i < ARRAY_SIZE(xpdd->evtchn_pending_pvt[pcpu]); i++)
172 {
173 if (!(xpdd->ev_actions[i].flags & EVT_ACTION_FLAGS_NO_SUSPEND))
174 {
175 switch(xpdd->ev_actions[i].type)
176 {
177 case EVT_ACTION_TYPE_IRQ:
178 {
179 int suspend_bit = i & (BITS_PER_LONG - 1);
180 int suspend_word = i >> BITS_PER_LONG_SHIFT;
181 synch_set_bit(suspend_bit, (volatile xen_long_t *)&xpdd->evtchn_pending_pvt[pcpu][suspend_word]);
182 }
183 break;
184 case EVT_ACTION_TYPE_NORMAL:
185 if (xpdd->ev_actions[i].ServiceRoutine)
186 {
187 xpdd->ev_actions[i].ServiceRoutine(xpdd->ev_actions[i].ServiceContext);
188 }
189 break;
190 case EVT_ACTION_TYPE_DPC:
191 KeInsertQueueDpc(&xpdd->ev_actions[i].Dpc, NULL, NULL);
192 break;
193 }
194 }
195 }
196 KeInsertQueueDpc(&ev_action->Dpc, NULL, NULL);
197 deferred = TRUE;
198 break;
199 default:
200 KdPrint((__DRIVER_NAME " Unhandled Event!!! port=%d\n", port));
201 break;
202 }
203 }
204 }
206 return handled && !deferred;
207 }
209 NTSTATUS
210 EvtChn_EvtInterruptEnable(WDFINTERRUPT interrupt, WDFDEVICE device)
211 {
212 NTSTATUS status = STATUS_SUCCESS;
214 UNREFERENCED_PARAMETER(interrupt);
215 UNREFERENCED_PARAMETER(device);
217 FUNCTION_ENTER();
218 FUNCTION_EXIT();
220 return status;
221 }
223 NTSTATUS
224 EvtChn_EvtInterruptDisable(WDFINTERRUPT interrupt, WDFDEVICE device)
225 {
226 NTSTATUS status = STATUS_SUCCESS;
228 UNREFERENCED_PARAMETER(interrupt);
229 UNREFERENCED_PARAMETER(device);
231 FUNCTION_ENTER();
232 FUNCTION_EXIT();
234 return status;
235 }
237 NTSTATUS
238 EvtChn_Bind(PVOID Context, evtchn_port_t Port, PXEN_EVTCHN_SERVICE_ROUTINE ServiceRoutine, PVOID ServiceContext, ULONG flags)
239 {
240 PXENPCI_DEVICE_DATA xpdd = Context;
241 ev_action_t *action = &xpdd->ev_actions[Port];
243 FUNCTION_ENTER();
245 if (InterlockedCompareExchange((volatile LONG *)&action->type, EVT_ACTION_TYPE_NEW, EVT_ACTION_TYPE_EMPTY) != EVT_ACTION_TYPE_EMPTY)
246 {
247 KdPrint((__DRIVER_NAME " Handler for port %d already registered\n", Port));
248 return STATUS_UNSUCCESSFUL;
249 }
251 xpdd->ev_actions[Port].ServiceRoutine = ServiceRoutine;
252 xpdd->ev_actions[Port].ServiceContext = ServiceContext;
253 xpdd->ev_actions[Port].xpdd = xpdd;
254 xpdd->ev_actions[Port].flags = flags;
255 KeMemoryBarrier();
256 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_NORMAL;
258 EvtChn_Unmask(Context, Port);
260 FUNCTION_EXIT();
262 return STATUS_SUCCESS;
263 }
265 NTSTATUS
266 EvtChn_BindDpc(PVOID Context, evtchn_port_t Port, PXEN_EVTCHN_SERVICE_ROUTINE ServiceRoutine, PVOID ServiceContext, ULONG flags)
267 {
268 PXENPCI_DEVICE_DATA xpdd = Context;
269 ev_action_t *action = &xpdd->ev_actions[Port];
271 FUNCTION_ENTER();
273 if (InterlockedCompareExchange((volatile LONG *)&action->type, EVT_ACTION_TYPE_NEW, EVT_ACTION_TYPE_EMPTY) != EVT_ACTION_TYPE_EMPTY)
274 {
275 KdPrint((__DRIVER_NAME " Handler for port %d already registered\n", Port));
276 return STATUS_UNSUCCESSFUL;
277 }
279 xpdd->ev_actions[Port].ServiceRoutine = ServiceRoutine;
280 xpdd->ev_actions[Port].ServiceContext = ServiceContext;
281 xpdd->ev_actions[Port].xpdd = xpdd;
282 xpdd->ev_actions[Port].flags = flags;
283 KeMemoryBarrier(); // make sure that the new service routine is only called once the context is set up
284 InterlockedExchange((volatile LONG *)&action->type, EVT_ACTION_TYPE_DPC);
286 EvtChn_Unmask(Context, Port);
288 FUNCTION_EXIT();
290 return STATUS_SUCCESS;
291 }
293 NTSTATUS
294 EvtChn_BindIrq(PVOID Context, evtchn_port_t Port, ULONG vector, PCHAR description, ULONG flags)
295 {
296 PXENPCI_DEVICE_DATA xpdd = Context;
297 ev_action_t *action = &xpdd->ev_actions[Port];
299 FUNCTION_ENTER();
301 if (InterlockedCompareExchange((volatile LONG *)&action->type, EVT_ACTION_TYPE_NEW, EVT_ACTION_TYPE_EMPTY) != EVT_ACTION_TYPE_EMPTY)
302 {
303 KdPrint((__DRIVER_NAME " Handler for port %d already registered\n", Port));
304 return STATUS_UNSUCCESSFUL;
305 }
307 xpdd->ev_actions[Port].vector = vector;
308 xpdd->ev_actions[Port].xpdd = xpdd;
309 KeMemoryBarrier();
310 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_IRQ;
311 RtlStringCbCopyA(xpdd->ev_actions[Port].description, 128, description);
312 xpdd->ev_actions[Port].flags = flags;
314 EvtChn_Unmask(Context, Port);
316 FUNCTION_EXIT();
318 return STATUS_SUCCESS;
319 }
321 NTSTATUS
322 EvtChn_Unbind(PVOID Context, evtchn_port_t Port)
323 {
324 PXENPCI_DEVICE_DATA xpdd = Context;
325 ev_action_t *action = &xpdd->ev_actions[Port];
326 int old_type;
328 EvtChn_Mask(Context, Port);
329 old_type = InterlockedExchange((volatile LONG *)&action->type, EVT_ACTION_TYPE_EMPTY);
331 if (old_type == EVT_ACTION_TYPE_DPC || old_type == EVT_ACTION_TYPE_SUSPEND)
332 {
333 KeRemoveQueueDpc(&xpdd->ev_actions[Port].Dpc);
334 #if (NTDDI_VERSION >= NTDDI_WINXP)
335 KeFlushQueuedDpcs();
336 #endif
337 }
339 KeMemoryBarrier(); // make sure we don't call the old Service Routine with the new data...
340 xpdd->ev_actions[Port].ServiceRoutine = NULL;
341 xpdd->ev_actions[Port].ServiceContext = NULL;
343 return STATUS_SUCCESS;
344 }
346 NTSTATUS
347 EvtChn_Mask(PVOID Context, evtchn_port_t port)
348 {
349 PXENPCI_DEVICE_DATA xpdd = Context;
351 synch_set_bit(port & (BITS_PER_LONG - 1),
352 (volatile xen_long_t *)&xpdd->shared_info_area->evtchn_mask[port >> BITS_PER_LONG_SHIFT]);
353 return STATUS_SUCCESS;
354 }
356 NTSTATUS
357 EvtChn_Unmask(PVOID context, evtchn_port_t port)
358 {
359 PXENPCI_DEVICE_DATA xpdd = context;
361 synch_clear_bit(port & (BITS_PER_LONG - 1),
362 (volatile xen_long_t *)&xpdd->shared_info_area->evtchn_mask[port >> BITS_PER_LONG_SHIFT]);
363 return STATUS_SUCCESS;
364 }
366 NTSTATUS
367 EvtChn_Notify(PVOID Context, evtchn_port_t Port)
368 {
369 PXENPCI_DEVICE_DATA xpdd = Context;
370 struct evtchn_send send;
372 send.port = Port;
373 (void)HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_send, &send);
374 return STATUS_SUCCESS;
375 }
377 evtchn_port_t
378 EvtChn_AllocIpi(PVOID context, ULONG vcpu)
379 {
380 PXENPCI_DEVICE_DATA xpdd = context;
381 evtchn_bind_ipi_t op;
383 FUNCTION_ENTER();
384 op.vcpu = vcpu;
385 op.port = 0;
386 HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_bind_ipi, &op);
387 FUNCTION_EXIT();
388 return op.port;
389 }
391 evtchn_port_t
392 EvtChn_AllocUnbound(PVOID Context, domid_t Domain)
393 {
394 PXENPCI_DEVICE_DATA xpdd = Context;
395 evtchn_alloc_unbound_t op;
396 op.dom = DOMID_SELF;
397 op.remote_dom = Domain;
398 HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_alloc_unbound, &op);
399 return op.port;
400 }
402 VOID
403 EvtChn_Close(PVOID Context, evtchn_port_t port )
404 {
405 PXENPCI_DEVICE_DATA xpdd = Context;
406 evtchn_close_t op;
407 op.port = port;
408 HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_close, &op);
409 return;
410 }
412 VOID
413 EvtChn_PdoEventChannelDpc(PVOID context)
414 {
415 PXENPCI_DEVICE_DATA xpdd = context;
417 FUNCTION_ENTER();
418 KeSetEvent(&xpdd->pdo_suspend_event, IO_NO_INCREMENT, FALSE);
419 FUNCTION_EXIT();
420 }
422 NTSTATUS
423 EvtChn_Init(PXENPCI_DEVICE_DATA xpdd)
424 {
425 ULONGLONG result;
426 ev_action_t *action;
427 int i;
429 FUNCTION_ENTER();
431 for (i = 0; i < NR_EVENTS; i++)
432 {
433 EvtChn_Mask(xpdd, i);
434 action = &xpdd->ev_actions[i];
435 action->type = EVT_ACTION_TYPE_EMPTY;
436 action->count = 0;
437 KeInitializeDpc(&action->Dpc, EvtChn_DpcBounce, action);
438 }
440 for (i = 0; i < 8; i++)
441 {
442 xpdd->shared_info_area->evtchn_pending[i] = 0;
443 }
445 for (i = 0; i < MAX_VIRT_CPUS; i++)
446 {
447 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_pending = 0;
448 xpdd->shared_info_area->vcpu_info[i].evtchn_pending_sel = 0;
449 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 1; /* apparantly this doesn't do anything */
450 }
452 KeMemoryBarrier();
454 result = hvm_set_parameter(xpdd, HVM_PARAM_CALLBACK_IRQ, xpdd->irq_number);
455 KdPrint((__DRIVER_NAME " hvm_set_parameter(HVM_PARAM_CALLBACK_IRQ, %d) = %d\n", xpdd->irq_number, (ULONG)result));
457 for (i = 0; i < MAX_VIRT_CPUS; i++)
458 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 0;
459 xpdd->interrupts_masked = FALSE;
460 KeMemoryBarrier();
462 KeInitializeEvent(&xpdd->pdo_suspend_event, SynchronizationEvent, FALSE);
463 xpdd->pdo_event_channel = EvtChn_AllocIpi(xpdd, 0);
464 EvtChn_BindDpc(xpdd, xpdd->pdo_event_channel, EvtChn_PdoEventChannelDpc, xpdd, EVT_ACTION_FLAGS_DEFAULT);
465 xpdd->ev_actions[xpdd->pdo_event_channel].type = EVT_ACTION_TYPE_SUSPEND; /* override dpc type */
467 KdPrint((__DRIVER_NAME " pdo_event_channel = %d\n", xpdd->pdo_event_channel));
469 FUNCTION_EXIT();
471 return STATUS_SUCCESS;
472 }
474 NTSTATUS
475 EvtChn_Suspend(PXENPCI_DEVICE_DATA xpdd)
476 {
477 int i;
478 // LARGE_INTEGER wait_time;
480 xpdd->interrupts_masked = TRUE;
481 for (i = 0; i < MAX_VIRT_CPUS; i++)
482 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 1;
483 KeMemoryBarrier();
484 hvm_set_parameter(xpdd, HVM_PARAM_CALLBACK_IRQ, 0);
486 for (i = 0; i < NR_EVENTS; i++)
487 {
488 if (xpdd->ev_actions[i].type == EVT_ACTION_TYPE_DPC)
489 {
490 KeRemoveQueueDpc(&xpdd->ev_actions[i].Dpc);
491 }
492 }
493 #if (NTDDI_VERSION >= NTDDI_WINXP)
494 KeFlushQueuedDpcs();
495 #endif
496 return STATUS_SUCCESS;
497 }
499 NTSTATUS
500 EvtChn_Resume(PXENPCI_DEVICE_DATA xpdd)
501 {
502 return EvtChn_Init(xpdd);