aboutsummaryrefslogtreecommitdiffstats
path: root/os/common/ext/ARM
diff options
context:
space:
mode:
authorGiovanni Di Sirio <gdisirio@gmail.com>2017-11-08 10:10:31 +0000
committerGiovanni Di Sirio <gdisirio@gmail.com>2017-11-08 10:10:31 +0000
commit46e19e85b9bd101ffa51a6118cafc8ab30a3dc9a (patch)
treef5f7fb6decf03819c8c7d8f23dadceed7fb664e7 /os/common/ext/ARM
parentd375f4e9ac2891a5037f8acc0ebf484ce424386d (diff)
downloadChibiOS-46e19e85b9bd101ffa51a6118cafc8ab30a3dc9a.tar.gz
ChibiOS-46e19e85b9bd101ffa51a6118cafc8ab30a3dc9a.tar.bz2
ChibiOS-46e19e85b9bd101ffa51a6118cafc8ab30a3dc9a.zip
Experimental.
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@10972 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'os/common/ext/ARM')
0 files changed, 0 insertions, 0 deletions
' href='#n95'>95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
/******************************************************************************
 * evtchn.c
 *
 * A simplified event channel for para-drivers in unmodified linux
 *
 * Copyright (c) 2002-2005, K A Fraser
 * Copyright (c) 2005, Intel Corporation <xiaofeng.ling@intel.com>
 *
 * This file may be distributed separately from the Linux kernel, or
 * incorporated into other software packages, subject to the following license:
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this source file (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <xen/evtchn.h>
#include <xen/interface/hvm/ioreq.h>
#include <xen/features.h>
#include "platform-pci.h"

#ifdef HAVE_XEN_PLATFORM_COMPAT_H
#include <xen/platform-compat.h>
#endif

void *shared_info_area;

#define MAX_EVTCHN 256
static struct {
	irqreturn_t(*handler) (int, void *, struct pt_regs *);
	void *dev_id;
	int close; /* close on unbind_from_irqhandler()? */
} evtchns[MAX_EVTCHN];

int irq_to_evtchn_port(int irq)
{
	return irq;
}
EXPORT_SYMBOL(irq_to_evtchn_port);

void mask_evtchn(int port)
{
	shared_info_t *s = shared_info_area;
	synch_set_bit(port, &s->evtchn_mask[0]);
}
EXPORT_SYMBOL(mask_evtchn);

void unmask_evtchn(int port)
{
	unsigned int cpu;
	shared_info_t *s = shared_info_area;
	vcpu_info_t *vcpu_info;

	preempt_disable();
	cpu = smp_processor_id();
	vcpu_info = &s->vcpu_info[cpu];

	/* Slow path (hypercall) if this is a non-local port.  We only
	   ever bind event channels to vcpu 0 in HVM guests. */
	if (unlikely(cpu != 0)) {
		evtchn_unmask_t op = { .port = port };
		(void)HYPERVISOR_event_channel_op(EVTCHNOP_unmask,
						  &op);
		preempt_enable();
		return;
	}

	synch_clear_bit(port, &s->evtchn_mask[0]);

	/*
	 * The following is basically the equivalent of
	 * 'hw_resend_irq'. Just like a real IO-APIC we 'lose the
	 * interrupt edge' if the channel is masked.
	 */
	if (synch_test_bit(port, &s->evtchn_pending[0]) &&
	    !synch_test_and_set_bit(port / BITS_PER_LONG,
				    &vcpu_info->evtchn_pending_sel)) {
		vcpu_info->evtchn_upcall_pending = 1;
		if (!vcpu_info->evtchn_upcall_mask)
			force_evtchn_callback();
	}
	preempt_enable();
}
EXPORT_SYMBOL(unmask_evtchn);

int bind_listening_port_to_irqhandler(
	unsigned int remote_domain,
	irqreturn_t (*handler)(int, void *, struct pt_regs *),
	unsigned long irqflags,
	const char *devname,
	void *dev_id)
{
	struct evtchn_alloc_unbound alloc_unbound;
	int err;

	alloc_unbound.dom        = DOMID_SELF;
	alloc_unbound.remote_dom = remote_domain;

	err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound,
					  &alloc_unbound);
	if (err)
		return err;

	evtchns[alloc_unbound.port].handler = handler;
	evtchns[alloc_unbound.port].dev_id  = dev_id;
	evtchns[alloc_unbound.port].close   = 1;
	unmask_evtchn(alloc_unbound.port);
	return alloc_unbound.port;
}
EXPORT_SYMBOL(bind_listening_port_to_irqhandler);

int bind_caller_port_to_irqhandler(
	unsigned int caller_port,
	irqreturn_t (*handler)(int, void *, struct pt_regs *),
	unsigned long irqflags,
	const char *devname,
	void *dev_id)
{
	if (caller_port >= MAX_EVTCHN)
		return -EINVAL;
	evtchns[caller_port].handler = handler;
	evtchns[caller_port].dev_id  = dev_id;
	evtchns[caller_port].close   = 0;
	unmask_evtchn(caller_port);
	return caller_port;
}
EXPORT_SYMBOL(bind_caller_port_to_irqhandler);

void unbind_from_irqhandler(unsigned int evtchn, void *dev_id)
{
	if (evtchn >= MAX_EVTCHN)
		return;

	mask_evtchn(evtchn);
	evtchns[evtchn].handler = NULL;

	if (evtchns[evtchn].close) {
		struct evtchn_close close = { .port = evtchn };
		HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
	}
}
EXPORT_SYMBOL(unbind_from_irqhandler);

void notify_remote_via_irq(int irq)
{
	int evtchn = irq;
	notify_remote_via_evtchn(evtchn);
}
EXPORT_SYMBOL(notify_remote_via_irq);

irqreturn_t evtchn_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
	unsigned int l1i, port;
	int cpu = smp_processor_id();
	irqreturn_t(*handler) (int, void *, struct pt_regs *);
	shared_info_t *s = shared_info_area;
	vcpu_info_t *v = &s->vcpu_info[cpu];
	unsigned long l1, l2;

	v->evtchn_upcall_pending = 0;
	/* NB. No need for a barrier here -- XCHG is a barrier on x86. */
	l1 = xchg(&v->evtchn_pending_sel, 0);
	while (l1 != 0) {
		l1i = __ffs(l1);
		l1 &= ~(1 << l1i);
		while ((l2 = s->evtchn_pending[l1i] & ~s->evtchn_mask[l1i])) {
			port = (l1i * BITS_PER_LONG) + __ffs(l2);
			synch_clear_bit(port, &s->evtchn_pending[0]);
			if ((handler = evtchns[port].handler) != NULL)
				handler(port, evtchns[port].dev_id,
					regs);
			else
				printk(KERN_WARNING "unexpected event channel "
				       "upcall on port %d!\n", port);
		}
	}

	return IRQ_HANDLED;
}

void force_evtchn_callback(void)
{
	(void)HYPERVISOR_xen_version(0, NULL);
}
EXPORT_SYMBOL(force_evtchn_callback);