win-pvdrivers

view xenpci/evtchn.c @ 579:c5136ccae1f3

Use IA64 compatible InterlockedXXX functions
author James Harper <james.harper@bendigoit.com.au>
date Tue Jun 02 21:12:09 2009 +1000 (2009-06-02)
parents b162a1156e3f
children 19b4c4d2b621
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 #if defined(__MINGW32__)
23 #define xchg(p1, p2) InterlockedExchange((xen_long_t * volatile)p1, p2)
24 /* rest implemented in mingw_extras.c */
25 #elif defined(_X86_)
26 #define xchg(p1, p2) InterlockedExchange(p1, p2)
27 #define synch_clear_bit(p1, p2) InterlockedBitTestAndReset(p2, p1)
28 #define synch_set_bit(p1, p2) InterlockedBitTestAndSet(p2, p1)
29 #define bit_scan_forward(p1, p2) _BitScanForward(p1, p2)
30 #else
31 #define xchg(p1, p2) InterlockedExchange64(p1, p2)
32 #define synch_clear_bit(p1, p2) InterlockedBitTestAndReset64(p2, p1)
33 #define synch_set_bit(p1, p2) InterlockedBitTestAndSet64(p2, p1)
34 #define bit_scan_forward(p1, p2) _BitScanForward64(p1, p2)
35 #endif
37 #define BITS_PER_LONG (sizeof(xen_ulong_t) * 8)
38 #define BITS_PER_LONG_SHIFT (5 + (sizeof(xen_ulong_t) >> 3))
40 static DDKAPI VOID
41 EvtChn_DpcBounce(PRKDPC Dpc, PVOID Context, PVOID SystemArgument1, PVOID SystemArgument2)
42 {
43 ev_action_t *action = Context;
45 UNREFERENCED_PARAMETER(Dpc);
46 UNREFERENCED_PARAMETER(SystemArgument1);
47 UNREFERENCED_PARAMETER(SystemArgument2);
49 //KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
51 if (action->type != EVT_ACTION_TYPE_EMPTY)
52 {
53 action->ServiceRoutine(action->ServiceContext);
54 }
55 //KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
56 }
58 /* Called at DIRQL */
59 BOOLEAN
60 EvtChn_AckEvent(PVOID context, evtchn_port_t port, BOOLEAN *last_interrupt)
61 {
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 < sizeof(xen_ulong_t) * 8; i++)
75 {
76 if (xpdd->evtchn_pending_pvt[pcpu][i])
77 {
78 *last_interrupt = FALSE;
79 break;
80 }
81 }
83 return (BOOLEAN)!!val;
84 }
86 volatile ULONG in_inq = 0;
88 BOOLEAN
89 EvtChn_EvtInterruptIsr(WDFINTERRUPT interrupt, ULONG message_id)
90 {
91 /*
92 For HVM domains, Xen always triggers the event on CPU0. Because the
93 interrupt is delivered via the virtual PCI device it might get delivered
94 to CPU != 0, but we should always use vcpu_info[0]
95 */
96 int vcpu = 0;
97 ULONG pcpu = KeGetCurrentProcessorNumber() & 0xff;
98 vcpu_info_t *vcpu_info;
99 PXENPCI_DEVICE_DATA xpdd = GetXpdd(WdfInterruptGetDevice(interrupt));
100 shared_info_t *shared_info_area = xpdd->shared_info_area;
101 xen_ulong_t evt_words;
102 unsigned long evt_word;
103 unsigned long evt_bit;
104 unsigned int port;
105 ev_action_t *ev_action;
106 BOOLEAN handled = FALSE;
107 BOOLEAN deferred = FALSE;
108 int i;
110 UNREFERENCED_PARAMETER(message_id);
112 if (xpdd->interrupts_masked)
113 {
114 KdPrint((__DRIVER_NAME " unhandled interrupt\n"));
115 }
117 if (xpdd->hibernated)
118 {
119 KdPrint((__DRIVER_NAME " interrupt while hibernated\n"));
120 }
122 for (i = 0; i < ARRAY_SIZE(xpdd->evtchn_pending_pvt[pcpu]); i++)
123 {
124 if (xpdd->evtchn_pending_pvt[pcpu][i])
125 {
126 KdPrint((__DRIVER_NAME " Unacknowledged event word = %d, val = %p\n", i, xpdd->evtchn_pending_pvt[pcpu][i]));
127 xpdd->evtchn_pending_pvt[pcpu][i] = 0;
128 }
129 }
131 vcpu_info = &shared_info_area->vcpu_info[vcpu];
133 vcpu_info->evtchn_upcall_pending = 0;
135 if (xpdd->interrupts_masked)
136 {
137 return TRUE;
138 }
140 evt_words = (xen_ulong_t)xchg((volatile xen_long_t *)&vcpu_info->evtchn_pending_sel, 0);
142 while (bit_scan_forward(&evt_word, evt_words))
143 {
144 evt_words &= ~(1 << evt_word);
145 while (bit_scan_forward(&evt_bit, shared_info_area->evtchn_pending[evt_word] & ~shared_info_area->evtchn_mask[evt_word]))
146 {
147 synch_clear_bit(evt_bit, (volatile xen_long_t *)&shared_info_area->evtchn_pending[evt_word]);
148 handled = TRUE;
149 port = (evt_word << BITS_PER_LONG_SHIFT) + evt_bit;
150 ev_action = &xpdd->ev_actions[port];
151 ev_action->count++;
152 switch (ev_action->type)
153 {
154 case EVT_ACTION_TYPE_NORMAL:
155 //KdPrint((__DRIVER_NAME " EVT_ACTION_TYPE_NORMAL\n"));
156 ev_action->ServiceRoutine(ev_action->ServiceContext);
157 break;
158 case EVT_ACTION_TYPE_IRQ:
159 //KdPrint((__DRIVER_NAME " EVT_ACTION_TYPE_IRQ\n"));
160 synch_set_bit(evt_bit, (volatile xen_long_t *)&xpdd->evtchn_pending_pvt[pcpu][evt_word]);
161 deferred = TRUE;
162 break;
163 case EVT_ACTION_TYPE_DPC:
164 //KdPrint((__DRIVER_NAME " EVT_ACTION_TYPE_DPC\n"));
165 KeInsertQueueDpc(&ev_action->Dpc, NULL, NULL);
166 break;
167 case EVT_ACTION_TYPE_SUSPEND:
168 KdPrint((__DRIVER_NAME " EVT_ACTION_TYPE_SUSPEND\n"));
169 for (i = 0; i < ARRAY_SIZE(xpdd->evtchn_pending_pvt[pcpu]); i++)
170 {
171 if (xpdd->ev_actions[i].type == EVT_ACTION_TYPE_IRQ)
172 {
173 int suspend_bit = i & (BITS_PER_LONG - 1);
174 int suspend_word = i >> BITS_PER_LONG_SHIFT;
175 synch_set_bit(suspend_bit, (volatile xen_long_t *)&xpdd->evtchn_pending_pvt[pcpu][suspend_word]);
176 }
177 else if (xpdd->ev_actions[i].type == EVT_ACTION_TYPE_NORMAL && xpdd->ev_actions[i].ServiceRoutine)
178 {
179 xpdd->ev_actions[i].ServiceRoutine(xpdd->ev_actions[i].ServiceContext);
180 }
181 }
182 KeInsertQueueDpc(&ev_action->Dpc, NULL, NULL);
183 deferred = TRUE;
184 break;
185 default:
186 KdPrint((__DRIVER_NAME " Unhandled Event!!!\n"));
187 break;
188 }
189 }
190 }
192 return handled && !deferred;
193 }
195 NTSTATUS
196 EvtChn_EvtInterruptEnable(WDFINTERRUPT interrupt, WDFDEVICE device)
197 {
198 NTSTATUS status = STATUS_SUCCESS;
200 UNREFERENCED_PARAMETER(interrupt);
201 UNREFERENCED_PARAMETER(device);
203 FUNCTION_ENTER();
204 FUNCTION_EXIT();
206 return status;
207 }
209 NTSTATUS
210 EvtChn_EvtInterruptDisable(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_Bind(PVOID Context, evtchn_port_t Port, PXEN_EVTCHN_SERVICE_ROUTINE ServiceRoutine, PVOID ServiceContext)
225 {
226 PXENPCI_DEVICE_DATA xpdd = Context;
228 FUNCTION_ENTER();
230 if (xpdd->ev_actions[Port].type != EVT_ACTION_TYPE_EMPTY)
231 {
232 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_EMPTY;
233 KeMemoryBarrier(); // make sure we don't call the old Service Routine with the new data...
234 KdPrint((__DRIVER_NAME " Handler for port %d already registered, replacing\n", Port));
235 }
237 xpdd->ev_actions[Port].ServiceRoutine = ServiceRoutine;
238 xpdd->ev_actions[Port].ServiceContext = ServiceContext;
239 xpdd->ev_actions[Port].xpdd = xpdd;
240 KeMemoryBarrier();
241 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_NORMAL;
243 EvtChn_Unmask(Context, Port);
245 FUNCTION_EXIT();
247 return STATUS_SUCCESS;
248 }
250 NTSTATUS
251 EvtChn_BindDpc(PVOID Context, evtchn_port_t Port, PXEN_EVTCHN_SERVICE_ROUTINE ServiceRoutine, PVOID ServiceContext)
252 {
253 PXENPCI_DEVICE_DATA xpdd = Context;
255 FUNCTION_ENTER();
257 if (xpdd->ev_actions[Port].type != EVT_ACTION_TYPE_EMPTY)
258 {
259 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_EMPTY;
260 KeMemoryBarrier(); // make sure we don't call the old Service Routine with the new data...
261 KdPrint((__DRIVER_NAME " Handler for port %d already registered, replacing\n", Port));
262 }
264 xpdd->ev_actions[Port].ServiceRoutine = ServiceRoutine;
265 xpdd->ev_actions[Port].ServiceContext = ServiceContext;
266 xpdd->ev_actions[Port].xpdd = xpdd;
267 KeInitializeDpc(&xpdd->ev_actions[Port].Dpc, EvtChn_DpcBounce, &xpdd->ev_actions[Port]);
268 KeMemoryBarrier(); // make sure that the new service routine is only called once the context is set up
269 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_DPC;
271 EvtChn_Unmask(Context, Port);
273 FUNCTION_EXIT();
275 return STATUS_SUCCESS;
276 }
278 NTSTATUS
279 EvtChn_BindIrq(PVOID Context, evtchn_port_t Port, ULONG vector, PCHAR description)
280 {
281 PXENPCI_DEVICE_DATA xpdd = Context;
283 FUNCTION_ENTER();
285 if (xpdd->ev_actions[Port].type != EVT_ACTION_TYPE_EMPTY)
286 {
287 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_EMPTY;
288 KeMemoryBarrier(); // make sure we don't call the old Service Routine with the new data...
289 KdPrint((__DRIVER_NAME " Handler for port %d already registered, replacing\n", Port));
290 }
292 xpdd->ev_actions[Port].vector = vector;
293 xpdd->ev_actions[Port].xpdd = xpdd;
294 KeMemoryBarrier();
295 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_IRQ;
296 strncpy(xpdd->ev_actions[Port].description, description, 128);
298 EvtChn_Unmask(Context, Port);
300 FUNCTION_EXIT();
302 return STATUS_SUCCESS;
303 }
305 NTSTATUS
306 EvtChn_Unbind(PVOID Context, evtchn_port_t Port)
307 {
308 PXENPCI_DEVICE_DATA xpdd = Context;
309 int old_type;
311 EvtChn_Mask(Context, Port);
312 old_type = xpdd->ev_actions[Port].type;
313 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_EMPTY;
314 KeMemoryBarrier(); // make sure we don't call the old Service Routine with the new data...
315 xpdd->ev_actions[Port].ServiceRoutine = NULL;
316 xpdd->ev_actions[Port].ServiceContext = NULL;
318 if (old_type == EVT_ACTION_TYPE_DPC)
319 KeRemoveQueueDpc(&xpdd->ev_actions[Port].Dpc);
321 return STATUS_SUCCESS;
322 }
324 NTSTATUS
325 EvtChn_Mask(PVOID Context, evtchn_port_t port)
326 {
327 PXENPCI_DEVICE_DATA xpdd = Context;
329 synch_set_bit(port & (BITS_PER_LONG - 1),
330 (volatile xen_long_t *)&xpdd->shared_info_area->evtchn_mask[port >> BITS_PER_LONG_SHIFT]);
331 return STATUS_SUCCESS;
332 }
334 NTSTATUS
335 EvtChn_Unmask(PVOID context, evtchn_port_t port)
336 {
337 PXENPCI_DEVICE_DATA xpdd = context;
339 synch_clear_bit(port & (BITS_PER_LONG - 1),
340 (volatile xen_long_t *)&xpdd->shared_info_area->evtchn_mask[port >> BITS_PER_LONG_SHIFT]);
341 return STATUS_SUCCESS;
342 }
344 NTSTATUS
345 EvtChn_Notify(PVOID Context, evtchn_port_t Port)
346 {
347 PXENPCI_DEVICE_DATA xpdd = Context;
348 struct evtchn_send send;
350 send.port = Port;
351 (void)HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_send, &send);
352 return STATUS_SUCCESS;
353 }
355 evtchn_port_t
356 EvtChn_AllocIpi(PVOID context, ULONG vcpu)
357 {
358 PXENPCI_DEVICE_DATA xpdd = context;
359 evtchn_bind_ipi_t op;
361 FUNCTION_ENTER();
362 op.vcpu = vcpu;
363 op.port = 0;
364 HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_bind_ipi, &op);
365 FUNCTION_EXIT();
366 return op.port;
367 }
369 evtchn_port_t
370 EvtChn_AllocUnbound(PVOID Context, domid_t Domain)
371 {
372 PXENPCI_DEVICE_DATA xpdd = Context;
373 evtchn_alloc_unbound_t op;
374 op.dom = DOMID_SELF;
375 op.remote_dom = Domain;
376 HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_alloc_unbound, &op);
377 return op.port;
378 }
380 VOID
381 EvtChn_Close(PVOID Context, evtchn_port_t port )
382 {
383 PXENPCI_DEVICE_DATA xpdd = Context;
384 evtchn_close_t op;
385 op.port = port;
386 HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_close, &op);
387 return;
388 }
390 VOID
391 EvtChn_PdoEventChannelDpc(PVOID context)
392 {
393 PXENPCI_DEVICE_DATA xpdd = context;
395 FUNCTION_ENTER();
396 KeSetEvent(&xpdd->pdo_suspend_event, IO_NO_INCREMENT, FALSE);
397 FUNCTION_EXIT();
398 }
400 NTSTATUS
401 EvtChn_Init(PXENPCI_DEVICE_DATA xpdd)
402 {
403 ULONGLONG result;
404 int i;
406 FUNCTION_ENTER();
408 for (i = 0; i < NR_EVENTS; i++)
409 {
410 EvtChn_Mask(xpdd, i);
411 xpdd->ev_actions[i].type = EVT_ACTION_TYPE_EMPTY;
412 xpdd->ev_actions[i].count = 0;
413 }
415 for (i = 0; i < 8; i++)
416 {
417 xpdd->shared_info_area->evtchn_pending[i] = 0;
418 }
420 for (i = 0; i < MAX_VIRT_CPUS; i++)
421 {
422 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_pending = 0;
423 xpdd->shared_info_area->vcpu_info[i].evtchn_pending_sel = 0;
424 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 1; /* apparantly this doesn't do anything */
425 }
427 KeMemoryBarrier();
429 result = hvm_set_parameter(xpdd, HVM_PARAM_CALLBACK_IRQ, xpdd->irq_number);
430 KdPrint((__DRIVER_NAME " hvm_set_parameter(HVM_PARAM_CALLBACK_IRQ, %d) = %d\n", xpdd->irq_number, (ULONG)result));
432 for (i = 0; i < MAX_VIRT_CPUS; i++)
433 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 0;
434 xpdd->interrupts_masked = FALSE;
435 KeMemoryBarrier();
437 KeInitializeEvent(&xpdd->pdo_suspend_event, SynchronizationEvent, FALSE);
438 xpdd->pdo_event_channel = EvtChn_AllocIpi(xpdd, 0);
439 EvtChn_BindDpc(xpdd, xpdd->pdo_event_channel, EvtChn_PdoEventChannelDpc, xpdd);
440 xpdd->ev_actions[xpdd->pdo_event_channel].type = EVT_ACTION_TYPE_SUSPEND; /* override dpc type */
442 KdPrint((__DRIVER_NAME " pdo_event_channel = %d\n", xpdd->pdo_event_channel));
444 FUNCTION_EXIT();
446 return STATUS_SUCCESS;
447 }
449 NTSTATUS
450 EvtChn_Suspend(PXENPCI_DEVICE_DATA xpdd)
451 {
452 int i;
453 // LARGE_INTEGER wait_time;
455 xpdd->interrupts_masked = TRUE;
456 for (i = 0; i < MAX_VIRT_CPUS; i++)
457 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 1;
458 KeMemoryBarrier();
459 hvm_set_parameter(xpdd, HVM_PARAM_CALLBACK_IRQ, 0);
461 for (i = 0; i < NR_EVENTS; i++)
462 {
463 if (xpdd->ev_actions[i].type == EVT_ACTION_TYPE_DPC)
464 {
465 KeRemoveQueueDpc(&xpdd->ev_actions[i].Dpc);
466 }
467 }
468 #if (NTDDI_VERSION >= NTDDI_WINXP)
469 KeFlushQueuedDpcs();
470 #endif
472 return STATUS_SUCCESS;
473 }
475 NTSTATUS
476 EvtChn_Resume(PXENPCI_DEVICE_DATA xpdd)
477 {
478 return EvtChn_Init(xpdd);