1// SPDX-License-Identifier: GPL-2.0 2/* 3 * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge 4 * (C) 2005 - Grzegorz Milos - Intel Research Cambridge 5 * (C) 2020 - EPAM Systems Inc. 6 * 7 * File: events.c [1] 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 * Description: Deals with events received on event channels 14 * 15 * [1] - http://xenbits.xen.org/gitweb/?p=mini-os.git;a=summary 16 */ 17#include <common.h> 18#include <log.h> 19 20#include <asm/io.h> 21#include <asm/xen/system.h> 22 23#include <xen/events.h> 24#include <xen/hvm.h> 25 26#if IS_ENABLED(CONFIG_XEN_SERIAL) 27extern u32 console_evtchn; 28#endif /* IS_ENABLED(CONFIG_XEN_SERIAL) */ 29 30#define NR_EVS 1024 31 32/** 33 * struct _ev_action - represents a event handler. 34 * 35 * Chaining or sharing is not allowed 36 */ 37struct _ev_action { 38 void (*handler)(evtchn_port_t port, struct pt_regs *regs, void *data); 39 void *data; 40 u32 count; 41}; 42 43static struct _ev_action ev_actions[NR_EVS]; 44void default_handler(evtchn_port_t port, struct pt_regs *regs, void *data); 45 46static unsigned long bound_ports[NR_EVS / (8 * sizeof(unsigned long))]; 47 48void unbind_all_ports(void) 49{ 50 int i; 51 int cpu = 0; 52 struct shared_info *s = HYPERVISOR_shared_info; 53 struct vcpu_info *vcpu_info = &s->vcpu_info[cpu]; 54 55 for (i = 0; i < NR_EVS; i++) { 56#if IS_ENABLED(CONFIG_XEN_SERIAL) 57 if (i == console_evtchn) 58 continue; 59#endif /* IS_ENABLED(CONFIG_XEN_SERIAL) */ 60 61 if (test_and_clear_bit(i, bound_ports)) { 62 printf("port %d still bound!\n", i); 63 unbind_evtchn(i); 64 } 65 } 66 vcpu_info->evtchn_upcall_pending = 0; 67 vcpu_info->evtchn_pending_sel = 0; 68} 69 70int do_event(evtchn_port_t port, struct pt_regs *regs) 71{ 72 struct _ev_action *action; 73 74 clear_evtchn(port); 75 76 if (port >= NR_EVS) { 77 printk("WARN: do_event(): Port number too large: %d\n", port); 78 return 1; 79 } 80 81 action = &ev_actions[port]; 82 action->count++; 83 84 /* call the handler */ 85 action->handler(port, regs, action->data); 86 87 return 1; 88} 89 90evtchn_port_t bind_evtchn(evtchn_port_t port, 91 void (*handler)(evtchn_port_t, struct pt_regs *, void *), 92 void *data) 93{ 94 if (ev_actions[port].handler != default_handler) 95 printf("WARN: Handler for port %d already registered, replacing\n", 96 port); 97 98 ev_actions[port].data = data; 99 wmb(); 100 ev_actions[port].handler = handler; 101 synch_set_bit(port, bound_ports); 102 103 return port; 104} 105 106/** 107 * unbind_evtchn() - Unbind event channel for selected port 108 */ 109void unbind_evtchn(evtchn_port_t port) 110{ 111 struct evtchn_close close; 112 int rc; 113 114 if (ev_actions[port].handler == default_handler) 115 debug("Default handler for port %d when unbinding\n", port); 116 mask_evtchn(port); 117 clear_evtchn(port); 118 119 ev_actions[port].handler = default_handler; 120 wmb(); 121 ev_actions[port].data = NULL; 122 synch_clear_bit(port, bound_ports); 123 124 close.port = port; 125 rc = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); 126 if (rc) 127 printf("WARN: close_port %d failed rc=%d. ignored\n", port, rc); 128} 129 130void default_handler(evtchn_port_t port, struct pt_regs *regs, void *ignore) 131{ 132 debug("[Port %d] - event received\n", port); 133} 134 135/** 136 * evtchn_alloc_unbound() - Create a port available to the pal for 137 * exchanging notifications. 138 * 139 * Unfortunate confusion of terminology: the port is unbound as far 140 * as Xen is concerned, but we automatically bind a handler to it. 141 * 142 * Return: The result of the hypervisor call. 143 */ 144int evtchn_alloc_unbound(domid_t pal, 145 void (*handler)(evtchn_port_t, struct pt_regs *, void *), 146 void *data, evtchn_port_t *port) 147{ 148 int rc; 149 150 struct evtchn_alloc_unbound op; 151 152 op.dom = DOMID_SELF; 153 op.remote_dom = pal; 154 rc = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &op); 155 if (rc) { 156 printf("ERROR: alloc_unbound failed with rc=%d", rc); 157 return rc; 158 } 159 if (!handler) 160 handler = default_handler; 161 *port = bind_evtchn(op.port, handler, data); 162 return rc; 163} 164 165/** 166 * eventchn_poll() - Event channel polling function 167 * 168 * Check and process any pending events 169 */ 170void eventchn_poll(void) 171{ 172 do_hypervisor_callback(NULL); 173} 174 175/** 176 * init_events() - Initialize event handler 177 * 178 * Initially all events are without a handler and disabled. 179 */ 180void init_events(void) 181{ 182 int i; 183 184 debug("%s\n", __func__); 185 186 for (i = 0; i < NR_EVS; i++) { 187 ev_actions[i].handler = default_handler; 188 mask_evtchn(i); 189 } 190} 191 192/** 193 * fini_events() - Close all ports 194 * 195 * Mask and clear event channels. Close port using EVTCHNOP_close 196 * hypercall. 197 */ 198void fini_events(void) 199{ 200 debug("%s\n", __func__); 201 /* Dealloc all events */ 202 unbind_all_ports(); 203} 204