win-pvdrivers

view xenpci/evtchn.c @ 464:4f1c7b79948b

Updates to support a different configuration method for xenscsi
author James Harper <james.harper@bendigoit.com.au>
date Thu Nov 27 09:28:00 2008 +1100 (2008-11-27)
parents 605747e0db9c
children dd2fefa6b374
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_DPC)
52 {
53 action->ServiceRoutine(NULL, 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)
61 {
62 PXENPCI_DEVICE_DATA xpdd = context;
63 ULONG evt_word;
64 ULONG evt_bit;
65 xen_ulong_t val;
67 evt_bit = port & (BITS_PER_LONG - 1);
68 evt_word = port >> BITS_PER_LONG_SHIFT;
70 val = synch_clear_bit(evt_bit, (volatile xen_long_t *)&xpdd->evtchn_pending_pvt[evt_word]);
72 return (BOOLEAN)!!val;
73 }
75 static DDKAPI BOOLEAN
76 EvtChn_Interrupt(PKINTERRUPT Interrupt, PVOID Context)
77 {
78 /*
79 For HVM domains, Xen always triggers the event on CPU0. Because the
80 interrupt is delivered via the virtual PCI device it might get delivered
81 to CPU != 0, but we should always use vcpu_info[0]
82 */
83 int cpu = 0;
84 vcpu_info_t *vcpu_info;
85 PXENPCI_DEVICE_DATA xpdd = (PXENPCI_DEVICE_DATA)Context;
86 shared_info_t *shared_info_area = xpdd->shared_info_area;
87 xen_ulong_t evt_words;
88 unsigned long evt_word;
89 unsigned long evt_bit;
90 unsigned int port;
91 ev_action_t *ev_action;
92 BOOLEAN handled = FALSE;
93 BOOLEAN deferred = FALSE;
94 int i;
96 if (xpdd->log_interrupts)
97 {
98 KdPrint((__DRIVER_NAME " --> " __FUNCTION__ " (cpu = %d)\n", KeGetCurrentProcessorNumber()));
99 }
101 UNREFERENCED_PARAMETER(Interrupt);
103 for (i = 0; i < ARRAY_SIZE(xpdd->evtchn_pending_pvt); i++)
104 {
105 if (xpdd->evtchn_pending_pvt[i])
106 {
107 KdPrint((__DRIVER_NAME " Unacknowledged event word = %d, val = %p\n", i, xpdd->evtchn_pending_pvt[i]));
108 xpdd->evtchn_pending_pvt[i] = 0;
109 }
110 }
112 vcpu_info = &shared_info_area->vcpu_info[cpu];
114 vcpu_info->evtchn_upcall_pending = 0;
116 if (xpdd->interrupts_masked)
117 {
118 KdPrint((__DRIVER_NAME " unhandled interrupt\n"));
119 return TRUE;
120 }
122 evt_words = (xen_ulong_t)xchg((volatile xen_long_t *)&vcpu_info->evtchn_pending_sel, 0);
124 while (bit_scan_forward(&evt_word, evt_words))
125 {
126 evt_words &= ~(1 << evt_word);
127 while (bit_scan_forward(&evt_bit, shared_info_area->evtchn_pending[evt_word] & ~shared_info_area->evtchn_mask[evt_word]))
128 {
129 synch_clear_bit(evt_bit, (volatile xen_long_t *)&shared_info_area->evtchn_pending[evt_word]);
130 handled = TRUE;
131 port = (evt_word << BITS_PER_LONG_SHIFT) + evt_bit;
132 ev_action = &xpdd->ev_actions[port];
133 ev_action->count++;
134 switch (ev_action->type)
135 {
136 case EVT_ACTION_TYPE_NORMAL:
137 ev_action->ServiceRoutine(NULL, ev_action->ServiceContext);
138 break;
139 case EVT_ACTION_TYPE_IRQ:
140 synch_set_bit(evt_bit, (volatile xen_long_t *)&xpdd->evtchn_pending_pvt[evt_word]);
141 deferred = TRUE;
142 break;
143 case EVT_ACTION_TYPE_DPC:
144 KeInsertQueueDpc(&ev_action->Dpc, NULL, NULL);
145 break;
146 case EVT_ACTION_TYPE_SUSPEND:
147 KdPrint((__DRIVER_NAME " EVT_ACTION_TYPE_SUSPEND\n"));
148 for (i = 0; i < ARRAY_SIZE(xpdd->evtchn_pending_pvt); i++)
149 {
150 if (xpdd->ev_actions[i].type == EVT_ACTION_TYPE_IRQ)
151 {
152 int suspend_bit = i & (BITS_PER_LONG - 1);
153 int suspend_word = i >> BITS_PER_LONG_SHIFT;
154 synch_set_bit(suspend_bit, (volatile xen_long_t *)&xpdd->evtchn_pending_pvt[suspend_word]);
155 }
156 }
157 deferred = TRUE;
158 break;
159 default:
160 KdPrint((__DRIVER_NAME " Unhandled Event!!!\n"));
161 break;
162 }
163 }
164 }
166 if (xpdd->log_interrupts)
167 {
168 KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
169 }
170 return handled && !deferred;
171 }
173 NTSTATUS
174 EvtChn_Bind(PVOID Context, evtchn_port_t Port, PKSERVICE_ROUTINE ServiceRoutine, PVOID ServiceContext)
175 {
176 PXENPCI_DEVICE_DATA xpdd = Context;
178 FUNCTION_ENTER();
180 if (xpdd->ev_actions[Port].type != EVT_ACTION_TYPE_EMPTY)
181 {
182 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_EMPTY;
183 KeMemoryBarrier(); // make sure we don't call the old Service Routine with the new data...
184 KdPrint((__DRIVER_NAME " Handler for port %d already registered, replacing\n", Port));
185 }
187 xpdd->ev_actions[Port].ServiceRoutine = ServiceRoutine;
188 xpdd->ev_actions[Port].ServiceContext = ServiceContext;
189 xpdd->ev_actions[Port].xpdd = xpdd;
190 KeMemoryBarrier();
191 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_NORMAL;
193 EvtChn_Unmask(Context, Port);
195 FUNCTION_EXIT();
197 return STATUS_SUCCESS;
198 }
200 NTSTATUS
201 EvtChn_BindDpc(PVOID Context, evtchn_port_t Port, PKSERVICE_ROUTINE ServiceRoutine, PVOID ServiceContext)
202 {
203 PXENPCI_DEVICE_DATA xpdd = Context;
205 FUNCTION_ENTER();
207 if (xpdd->ev_actions[Port].type != EVT_ACTION_TYPE_EMPTY)
208 {
209 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_EMPTY;
210 KeMemoryBarrier(); // make sure we don't call the old Service Routine with the new data...
211 KdPrint((__DRIVER_NAME " Handler for port %d already registered, replacing\n", Port));
212 }
214 xpdd->ev_actions[Port].ServiceRoutine = ServiceRoutine;
215 xpdd->ev_actions[Port].ServiceContext = ServiceContext;
216 xpdd->ev_actions[Port].xpdd = xpdd;
217 KeInitializeDpc(&xpdd->ev_actions[Port].Dpc, EvtChn_DpcBounce, &xpdd->ev_actions[Port]);
218 KeMemoryBarrier(); // make sure that the new service routine is only called once the context is set up
219 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_DPC;
221 EvtChn_Unmask(Context, Port);
223 FUNCTION_EXIT();
225 return STATUS_SUCCESS;
226 }
228 NTSTATUS
229 EvtChn_BindIrq(PVOID Context, evtchn_port_t Port, ULONG vector, PCHAR description)
230 {
231 PXENPCI_DEVICE_DATA xpdd = Context;
233 FUNCTION_ENTER();
235 if (xpdd->ev_actions[Port].type != EVT_ACTION_TYPE_EMPTY)
236 {
237 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_EMPTY;
238 KeMemoryBarrier(); // make sure we don't call the old Service Routine with the new data...
239 KdPrint((__DRIVER_NAME " Handler for port %d already registered, replacing\n", Port));
240 }
242 xpdd->ev_actions[Port].vector = vector;
243 xpdd->ev_actions[Port].xpdd = xpdd;
244 KeMemoryBarrier();
245 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_IRQ;
246 strncpy(xpdd->ev_actions[Port].description, description, 128);
248 EvtChn_Unmask(Context, Port);
250 FUNCTION_EXIT();
252 return STATUS_SUCCESS;
253 }
255 NTSTATUS
256 EvtChn_Unbind(PVOID Context, evtchn_port_t Port)
257 {
258 PXENPCI_DEVICE_DATA xpdd = Context;
259 int old_type;
261 EvtChn_Mask(Context, Port);
262 old_type = xpdd->ev_actions[Port].type;
263 xpdd->ev_actions[Port].type = EVT_ACTION_TYPE_EMPTY;
264 KeMemoryBarrier(); // make sure we don't call the old Service Routine with the new data...
265 xpdd->ev_actions[Port].ServiceRoutine = NULL;
266 xpdd->ev_actions[Port].ServiceContext = NULL;
268 if (old_type == EVT_ACTION_TYPE_DPC)
269 KeRemoveQueueDpc(&xpdd->ev_actions[Port].Dpc);
271 return STATUS_SUCCESS;
272 }
274 NTSTATUS
275 EvtChn_Mask(PVOID Context, evtchn_port_t port)
276 {
277 PXENPCI_DEVICE_DATA xpdd = Context;
279 synch_set_bit(port & (BITS_PER_LONG - 1),
280 (volatile xen_long_t *)&xpdd->shared_info_area->evtchn_mask[port >> BITS_PER_LONG_SHIFT]);
281 return STATUS_SUCCESS;
282 }
284 NTSTATUS
285 EvtChn_Unmask(PVOID context, evtchn_port_t port)
286 {
287 PXENPCI_DEVICE_DATA xpdd = context;
289 synch_clear_bit(port & (BITS_PER_LONG - 1),
290 (volatile xen_long_t *)&xpdd->shared_info_area->evtchn_mask[port >> BITS_PER_LONG_SHIFT]);
291 return STATUS_SUCCESS;
292 }
294 NTSTATUS
295 EvtChn_Notify(PVOID Context, evtchn_port_t Port)
296 {
297 PXENPCI_DEVICE_DATA xpdd = Context;
298 struct evtchn_send send;
300 send.port = Port;
301 (void)HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_send, &send);
302 return STATUS_SUCCESS;
303 }
305 evtchn_port_t
306 EvtChn_AllocIpi(PVOID context, ULONG vcpu)
307 {
308 PXENPCI_DEVICE_DATA xpdd = context;
309 evtchn_bind_ipi_t op;
311 FUNCTION_ENTER();
312 op.vcpu = vcpu;
313 op.port = 0;
314 HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_bind_ipi, &op);
315 FUNCTION_EXIT();
316 return op.port;
317 }
319 evtchn_port_t
320 EvtChn_AllocUnbound(PVOID Context, domid_t Domain)
321 {
322 PXENPCI_DEVICE_DATA xpdd = Context;
323 evtchn_alloc_unbound_t op;
324 op.dom = DOMID_SELF;
325 op.remote_dom = Domain;
326 HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_alloc_unbound, &op);
327 return op.port;
328 }
330 VOID
331 EvtChn_Close(PVOID Context, evtchn_port_t port )
332 {
333 PXENPCI_DEVICE_DATA xpdd = Context;
334 evtchn_close_t op;
335 op.port = port;
336 HYPERVISOR_event_channel_op(xpdd, EVTCHNOP_close, &op);
337 return;
338 }
340 NTSTATUS
341 EvtChn_Init(PXENPCI_DEVICE_DATA xpdd)
342 {
343 int i;
345 FUNCTION_ENTER();
347 for (i = 0; i < NR_EVENTS; i++)
348 {
349 EvtChn_Mask(xpdd, i);
350 xpdd->ev_actions[i].type = EVT_ACTION_TYPE_EMPTY;
351 xpdd->ev_actions[i].count = 0;
352 }
354 for (i = 0; i < 8; i++)
355 {
356 xpdd->shared_info_area->evtchn_pending[i] = 0;
357 }
359 for (i = 0; i < MAX_VIRT_CPUS; i++)
360 {
361 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_pending = 0;
362 xpdd->shared_info_area->vcpu_info[i].evtchn_pending_sel = 0;
363 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 1; /* apparantly this doesn't do anything */
364 }
366 KeMemoryBarrier();
368 hvm_set_parameter(xpdd, HVM_PARAM_CALLBACK_IRQ, xpdd->irq_number);
370 for (i = 0; i < MAX_VIRT_CPUS; i++)
371 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 0;
372 xpdd->interrupts_masked = FALSE;
373 KeMemoryBarrier();
375 xpdd->suspend_evtchn = EvtChn_AllocIpi(xpdd, 0);
376 xpdd->ev_actions[xpdd->suspend_evtchn].type = EVT_ACTION_TYPE_SUSPEND;
377 EvtChn_Unmask(xpdd, xpdd->suspend_evtchn);
379 KdPrint((__DRIVER_NAME " suspend_evtchn = %d\n", xpdd->suspend_evtchn));
381 FUNCTION_EXIT();
383 return STATUS_SUCCESS;
384 }
386 NTSTATUS
387 EvtChn_ConnectInterrupt(PXENPCI_DEVICE_DATA xpdd)
388 {
389 NTSTATUS status = STATUS_SUCCESS;
391 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
393 status = IoConnectInterrupt(
394 &xpdd->interrupt,
395 EvtChn_Interrupt,
396 xpdd,
397 NULL,
398 xpdd->irq_vector,
399 xpdd->irq_level,
400 xpdd->irq_level,
401 xpdd->irq_mode, //LevelSensitive,
402 TRUE,
403 xpdd->irq_affinity,
404 FALSE);
406 if (!NT_SUCCESS(status))
407 {
408 KdPrint((__DRIVER_NAME " IoConnectInterrupt failed 0x%08x\n", status));
409 return status;
410 }
412 return status;
413 }
415 NTSTATUS
416 EvtChn_Shutdown(PXENPCI_DEVICE_DATA xpdd)
417 {
418 int i;
419 // LARGE_INTEGER wait_time;
421 xpdd->interrupts_masked = TRUE;
422 for (i = 0; i < MAX_VIRT_CPUS; i++)
423 xpdd->shared_info_area->vcpu_info[i].evtchn_upcall_mask = 1;
424 KeMemoryBarrier();
425 hvm_set_parameter(xpdd, HVM_PARAM_CALLBACK_IRQ, 0);
427 for (i = 0; i < NR_EVENTS; i++)
428 {
429 if (xpdd->ev_actions[i].type == EVT_ACTION_TYPE_DPC)
430 {
431 KeRemoveQueueDpc(&xpdd->ev_actions[i].Dpc);
432 }
433 }
434 #if (NTDDI_VERSION >= NTDDI_WINXP)
435 KeFlushQueuedDpcs();
436 #endif
438 return STATUS_SUCCESS;
439 }