1/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- 2 **************************************************************************** 3 * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge 4 * (C) 2005 - Grzegorz Milos - Intel Research Cambridge 5 **************************************************************************** 6 * 7 * File: events.c 8 * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk) 9 * Changes: Grzegorz Milos (gm281@cam.ac.uk) 10 * 11 * Date: Jul 2003, changes Jun 2005 12 * 13 * Environment: Xen Minimal OS 14 * Description: Deals with events recieved on event channels 15 * 16 **************************************************************************** 17 */ 18 19#include <mini-os/os.h> 20#include <mini-os/mm.h> 21#include <mini-os/hypervisor.h> 22#include <mini-os/events.h> 23#include <mini-os/lib.h> 24#include <mini-os/wait.h> 25 26#define NR_EVS 1024 27 28/* this represents a event handler. Chaining or sharing is not allowed */ 29typedef struct _ev_action_t { 30 spinlock_t lock; 31 evtchn_handler_t handler; 32 void *data; 33 uint32_t count; 34} ev_action_t; 35 36static ev_action_t ev_actions[NR_EVS]; 37void default_handler(evtchn_port_t port, struct pt_regs *regs, void *data); 38 39static unsigned long bound_ports[NR_EVS/(8*sizeof(unsigned long))]; 40 41static void (*rump_evtdev_callback)(u_int port); 42 43void minios_events_register_rump_callback(void (*cb)(u_int)) 44{ 45 BUG_ON(rump_evtdev_callback != NULL); 46 rump_evtdev_callback = cb; 47} 48 49/* interrupt handler queues events here */ 50DECLARE_WAIT_QUEUE_HEAD(minios_events_waitq); 51static DEFINE_SPINLOCK(evt_handler_lock); 52void minios_evtdev_handler(evtchn_port_t port, struct pt_regs * regs, 53 void *data) 54{ 55 unsigned long flags; 56 57 minios_mask_evtchn(port); 58 59 local_irq_save(flags); 60 spin_lock(&evt_handler_lock); 61 62 rump_evtdev_callback(port); 63 64 spin_unlock(&evt_handler_lock); 65 local_irq_restore(flags); 66} 67 68void unbind_all_ports(void) 69{ 70 int i; 71 int cpu = 0; 72 shared_info_t *s = HYPERVISOR_shared_info; 73 vcpu_info_t *vcpu_info = &s->vcpu_info[cpu]; 74 int rc; 75 76 for (i = 0; i < NR_EVS; i++) 77 { 78 if (i == start_info.console.domU.evtchn || 79 i == start_info.store_evtchn) 80 continue; 81 82 if (test_and_clear_bit(i, bound_ports)) 83 { 84 struct evtchn_close close; 85 minios_printk("port %d still bound!\n", i); 86 minios_mask_evtchn(i); 87 88 spin_lock(&ev_actions[i].lock); 89 ev_actions[i].handler = default_handler; 90 wmb(); 91 ev_actions[i].data = NULL; 92 spin_unlock(&ev_actions[i].lock); 93 94 close.port = i; 95 rc = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); 96 if (rc) 97 minios_printk("WARN: close_port %s failed rc=%d. ignored\n", i, rc); 98 minios_clear_evtchn(i); 99 } 100 } 101 vcpu_info->evtchn_upcall_pending = 0; 102 vcpu_info->evtchn_pending_sel = 0; 103} 104 105/* 106 * Demux events to different handlers. 107 */ 108int do_event(evtchn_port_t port, struct pt_regs *regs) 109{ 110 ev_action_t *action; 111 112 minios_clear_evtchn(port); 113 114 if (port >= NR_EVS) 115 { 116 minios_printk("WARN: do_event(): Port number too large: %d\n", port); 117 return 1; 118 } 119 120 spin_lock(&ev_actions[port].lock); 121 action = &ev_actions[port]; 122 action->count++; 123 124 /* call the handler */ 125 action->handler(port, regs, action->data); 126 spin_unlock(&ev_actions[port].lock); 127 128 return 1; 129} 130 131evtchn_port_t minios_bind_evtchn(evtchn_port_t port, evtchn_handler_t handler, 132 void *data) 133{ 134 spin_lock(&ev_actions[port].lock); 135 if (ev_actions[port].handler != default_handler) 136 minios_printk("WARN: Handler for port %d already registered, replacing\n", 137 port); 138 139 ev_actions[port].data = data; 140 wmb(); 141 ev_actions[port].handler = handler; 142 spin_unlock(&ev_actions[port].lock); 143 144 set_bit(port, bound_ports); 145 146 return port; 147} 148 149void minios_unbind_evtchn(evtchn_port_t port) 150{ 151 struct evtchn_close close; 152 int rc; 153 154 spin_lock(&ev_actions[port].lock); 155 if (ev_actions[port].handler == default_handler) 156 minios_printk("WARN: No handler for port %d when unbinding\n", port); 157 minios_mask_evtchn(port); 158 minios_clear_evtchn(port); 159 160 ev_actions[port].handler = default_handler; 161 wmb(); 162 ev_actions[port].data = NULL; 163 spin_unlock(&ev_actions[port].lock); 164 165 clear_bit(port, bound_ports); 166 167 close.port = port; 168 rc = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); 169 if (rc) 170 minios_printk("WARN: close_port %s failed rc=%d. ignored\n", port, rc); 171} 172 173evtchn_port_t minios_bind_virq(uint32_t virq, evtchn_handler_t handler, 174 void *data) 175{ 176 evtchn_bind_virq_t op; 177 int rc; 178 179 /* Try to bind the virq to a port */ 180 op.virq = virq; 181 op.vcpu = smp_processor_id(); 182 183 if ((rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &op)) != 0) 184 { 185 minios_printk("Failed to bind virtual IRQ %d with rc=%d\n", virq, rc); 186 return -1; 187 } 188 minios_bind_evtchn(op.port, handler, data); 189 return op.port; 190} 191 192evtchn_port_t minios_bind_pirq(uint32_t pirq, int will_share, 193 evtchn_handler_t handler, void *data) 194{ 195 evtchn_bind_pirq_t op; 196 int rc; 197 198 /* Try to bind the pirq to a port */ 199 op.pirq = pirq; 200 op.flags = will_share ? BIND_PIRQ__WILL_SHARE : 0; 201 202 if ((rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &op)) != 0) 203 { 204 minios_printk("Failed to bind physical IRQ %d with rc=%d\n", pirq, rc); 205 return -1; 206 } 207 minios_bind_evtchn(op.port, handler, data); 208 return op.port; 209} 210 211#if defined(__x86_64__) 212static char irqstack[2 * STACK_SIZE]; 213 214static struct pda 215{ 216 int irqcount; /* offset 0 (used in x86_64.S) */ 217 char *irqstackptr; /* 8 */ 218} cpu0_pda; 219#endif 220 221/* Just a simple wrapper for event channel hypercall. */ 222int minios_event_channel_op(int cmd, void *op) 223{ 224 return HYPERVISOR_event_channel_op(cmd, op); 225} 226 227/* 228 * Initially all events are without a handler and disabled 229 */ 230void init_events(void) 231{ 232 int i; 233#if defined(__x86_64__) 234 asm volatile("movl %0,%%fs ; movl %0,%%gs" :: "r" (0)); 235 wrmsrl(0xc0000101, &cpu0_pda); /* 0xc0000101 is MSR_GS_BASE */ 236 cpu0_pda.irqcount = -1; 237 cpu0_pda.irqstackptr = (void*) (((unsigned long)irqstack + 2 * STACK_SIZE) 238 & ~(STACK_SIZE - 1)); 239#endif 240 /* initialize event handler */ 241 for ( i = 0; i < NR_EVS; i++ ) 242 { 243 ev_actions[i].handler = default_handler; 244 spin_lock_init(&ev_actions[i].lock); 245 minios_mask_evtchn(i); 246 } 247} 248 249void fini_events(void) 250{ 251 /* Dealloc all events */ 252 unbind_all_ports(); 253#if defined(__x86_64__) 254 wrmsrl(0xc0000101, NULL); /* 0xc0000101 is MSR_GS_BASE */ 255#endif 256} 257 258void default_handler(evtchn_port_t port, struct pt_regs *regs, void *ignore) 259{ 260 minios_printk("[Port %d] - event received\n", port); 261} 262 263/* Create a port available to the pal for exchanging notifications. 264 Returns the result of the hypervisor call. */ 265 266/* Unfortunate confusion of terminology: the port is unbound as far 267 as Xen is concerned, but we automatically bind a handler to it 268 from inside mini-os. */ 269 270int minios_evtchn_alloc_unbound(domid_t pal, evtchn_handler_t handler, 271 void *data, evtchn_port_t *port) 272{ 273 int rc; 274 275 evtchn_alloc_unbound_t op; 276 op.dom = DOMID_SELF; 277 op.remote_dom = pal; 278 rc = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &op); 279 if (rc) 280 { 281 minios_printk("ERROR: alloc_unbound failed with rc=%d", rc); 282 return rc; 283 } 284 *port = minios_bind_evtchn(op.port, handler, data); 285 return rc; 286} 287 288/* Connect to a port so as to allow the exchange of notifications with 289 the pal. Returns the result of the hypervisor call. */ 290 291int minios_evtchn_bind_interdomain(domid_t pal, evtchn_port_t remote_port, 292 evtchn_handler_t handler, void *data, 293 evtchn_port_t *local_port) 294{ 295 int rc; 296 evtchn_port_t port; 297 evtchn_bind_interdomain_t op; 298 op.remote_dom = pal; 299 op.remote_port = remote_port; 300 rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, &op); 301 if (rc) 302 { 303 minios_printk("ERROR: bind_interdomain failed with rc=%d", rc); 304 return rc; 305 } 306 port = op.local_port; 307 *local_port = minios_bind_evtchn(port, handler, data); 308 return rc; 309} 310 311int minios_notify_remote_via_evtchn(evtchn_port_t port) 312{ 313 evtchn_send_t op; 314 op.port = port; 315 return HYPERVISOR_event_channel_op(EVTCHNOP_send, &op); 316} 317 318/* 319 * Local variables: 320 * mode: C 321 * c-set-style: "BSD" 322 * c-basic-offset: 4 323 * tab-width: 4 324 * indent-tabs-mode: nil 325 * End: 326 */ 327