18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * irqchip.c: Common API for in kernel interrupt controllers
48c2ecf20Sopenharmony_ci * Copyright (c) 2007, Intel Corporation.
58c2ecf20Sopenharmony_ci * Copyright 2010 Red Hat, Inc. and/or its affiliates.
68c2ecf20Sopenharmony_ci * Copyright (c) 2013, Alexander Graf <agraf@suse.de>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This file is derived from virt/kvm/irq_comm.c.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Authors:
118c2ecf20Sopenharmony_ci *   Yaozu (Eddie) Dong <Eddie.dong@intel.com>
128c2ecf20Sopenharmony_ci *   Alexander Graf <agraf@suse.de>
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/srcu.h>
188c2ecf20Sopenharmony_ci#include <linux/export.h>
198c2ecf20Sopenharmony_ci#include <trace/events/kvm.h>
208c2ecf20Sopenharmony_ci#include "irq.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#if defined(CONFIG_CPU_LOONGSON64)
238c2ecf20Sopenharmony_ci#include "ls_irq.h"
248c2ecf20Sopenharmony_ci#endif
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ciint kvm_irq_map_gsi(struct kvm *kvm,
278c2ecf20Sopenharmony_ci		    struct kvm_kernel_irq_routing_entry *entries, int gsi)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct kvm_irq_routing_table *irq_rt;
308c2ecf20Sopenharmony_ci	struct kvm_kernel_irq_routing_entry *e;
318c2ecf20Sopenharmony_ci	int n = 0;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	irq_rt = srcu_dereference_check(kvm->irq_routing, &kvm->irq_srcu,
348c2ecf20Sopenharmony_ci					lockdep_is_held(&kvm->irq_lock));
358c2ecf20Sopenharmony_ci	if (irq_rt && gsi < irq_rt->nr_rt_entries) {
368c2ecf20Sopenharmony_ci		hlist_for_each_entry(e, &irq_rt->map[gsi], link) {
378c2ecf20Sopenharmony_ci			entries[n] = *e;
388c2ecf20Sopenharmony_ci			++n;
398c2ecf20Sopenharmony_ci		}
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	return n;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciint kvm_irq_map_chip_pin(struct kvm *kvm, unsigned irqchip, unsigned pin)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct kvm_irq_routing_table *irq_rt;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	irq_rt = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu);
508c2ecf20Sopenharmony_ci	return irq_rt->chip[irqchip][pin];
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ciint kvm_send_userspace_msi(struct kvm *kvm, struct kvm_msi *msi)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct kvm_kernel_irq_routing_entry route;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (!irqchip_in_kernel(kvm) || (msi->flags & ~KVM_MSI_VALID_DEVID))
588c2ecf20Sopenharmony_ci		return -EINVAL;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	route.msi.address_lo = msi->address_lo;
618c2ecf20Sopenharmony_ci	route.msi.address_hi = msi->address_hi;
628c2ecf20Sopenharmony_ci	route.msi.data = msi->data;
638c2ecf20Sopenharmony_ci	route.msi.flags = msi->flags;
648c2ecf20Sopenharmony_ci	route.msi.devid = msi->devid;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return kvm_set_msi(&route, kvm, KVM_USERSPACE_IRQ_SOURCE_ID, 1, false);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci * Return value:
718c2ecf20Sopenharmony_ci *  < 0   Interrupt was ignored (masked or not delivered for other reasons)
728c2ecf20Sopenharmony_ci *  = 0   Interrupt was coalesced (previous irq is still pending)
738c2ecf20Sopenharmony_ci *  > 0   Number of CPUs interrupt was delivered to
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_ciint kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level,
768c2ecf20Sopenharmony_ci		bool line_status)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct kvm_kernel_irq_routing_entry irq_set[KVM_NR_IRQCHIPS];
798c2ecf20Sopenharmony_ci	int ret = -1, i, idx;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	trace_kvm_set_irq(irq, level, irq_source_id);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* Not possible to detect if the guest uses the PIC or the
848c2ecf20Sopenharmony_ci	 * IOAPIC.  So set the bit in both. The guest will ignore
858c2ecf20Sopenharmony_ci	 * writes to the unused one.
868c2ecf20Sopenharmony_ci	 */
878c2ecf20Sopenharmony_ci	idx = srcu_read_lock(&kvm->irq_srcu);
888c2ecf20Sopenharmony_ci	i = kvm_irq_map_gsi(kvm, irq_set, irq);
898c2ecf20Sopenharmony_ci	srcu_read_unlock(&kvm->irq_srcu, idx);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	while (i--) {
928c2ecf20Sopenharmony_ci		int r;
938c2ecf20Sopenharmony_ci		r = irq_set[i].set(&irq_set[i], kvm, irq_source_id, level,
948c2ecf20Sopenharmony_ci				   line_status);
958c2ecf20Sopenharmony_ci		if (r < 0)
968c2ecf20Sopenharmony_ci			continue;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		ret = r + ((ret < 0) ? 0 : ret);
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return ret;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void free_irq_routing_table(struct kvm_irq_routing_table *rt)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	int i;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (!rt)
1098c2ecf20Sopenharmony_ci		return;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	for (i = 0; i < rt->nr_rt_entries; ++i) {
1128c2ecf20Sopenharmony_ci		struct kvm_kernel_irq_routing_entry *e;
1138c2ecf20Sopenharmony_ci		struct hlist_node *n;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci		hlist_for_each_entry_safe(e, n, &rt->map[i], link) {
1168c2ecf20Sopenharmony_ci			hlist_del(&e->link);
1178c2ecf20Sopenharmony_ci			kfree(e);
1188c2ecf20Sopenharmony_ci		}
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	kfree(rt);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_civoid kvm_free_irq_routing(struct kvm *kvm)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	/* Called only during vm destruction. Nobody can use the pointer
1278c2ecf20Sopenharmony_ci	   at this stage */
1288c2ecf20Sopenharmony_ci	struct kvm_irq_routing_table *rt = rcu_access_pointer(kvm->irq_routing);
1298c2ecf20Sopenharmony_ci	free_irq_routing_table(rt);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic int setup_routing_entry(struct kvm *kvm,
1338c2ecf20Sopenharmony_ci			       struct kvm_irq_routing_table *rt,
1348c2ecf20Sopenharmony_ci			       struct kvm_kernel_irq_routing_entry *e,
1358c2ecf20Sopenharmony_ci			       const struct kvm_irq_routing_entry *ue)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct kvm_kernel_irq_routing_entry *ei;
1388c2ecf20Sopenharmony_ci	int r;
1398c2ecf20Sopenharmony_ci	u32 gsi = array_index_nospec(ue->gsi, KVM_MAX_IRQ_ROUTES);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/*
1428c2ecf20Sopenharmony_ci	 * Do not allow GSI to be mapped to the same irqchip more than once.
1438c2ecf20Sopenharmony_ci	 * Allow only one to one mapping between GSI and non-irqchip routing.
1448c2ecf20Sopenharmony_ci	 */
1458c2ecf20Sopenharmony_ci	hlist_for_each_entry(ei, &rt->map[gsi], link)
1468c2ecf20Sopenharmony_ci		if (ei->type != KVM_IRQ_ROUTING_IRQCHIP ||
1478c2ecf20Sopenharmony_ci		    ue->type != KVM_IRQ_ROUTING_IRQCHIP ||
1488c2ecf20Sopenharmony_ci		    ue->u.irqchip.irqchip == ei->irqchip.irqchip)
1498c2ecf20Sopenharmony_ci			return -EINVAL;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	e->gsi = gsi;
1528c2ecf20Sopenharmony_ci	e->type = ue->type;
1538c2ecf20Sopenharmony_ci	r = kvm_set_routing_entry(kvm, e, ue);
1548c2ecf20Sopenharmony_ci	if (r)
1558c2ecf20Sopenharmony_ci		return r;
1568c2ecf20Sopenharmony_ci	if (e->type == KVM_IRQ_ROUTING_IRQCHIP)
1578c2ecf20Sopenharmony_ci		rt->chip[e->irqchip.irqchip][e->irqchip.pin] = e->gsi;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	hlist_add_head(&e->link, &rt->map[e->gsi]);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return 0;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_civoid __attribute__((weak)) kvm_arch_irq_routing_update(struct kvm *kvm)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cibool __weak kvm_arch_can_set_irq_routing(struct kvm *kvm)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	return true;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ciint kvm_set_irq_routing(struct kvm *kvm,
1748c2ecf20Sopenharmony_ci			const struct kvm_irq_routing_entry *ue,
1758c2ecf20Sopenharmony_ci			unsigned nr,
1768c2ecf20Sopenharmony_ci			unsigned flags)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct kvm_irq_routing_table *new, *old;
1798c2ecf20Sopenharmony_ci	struct kvm_kernel_irq_routing_entry *e;
1808c2ecf20Sopenharmony_ci	u32 i, j, nr_rt_entries = 0;
1818c2ecf20Sopenharmony_ci	int r;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	for (i = 0; i < nr; ++i) {
1848c2ecf20Sopenharmony_ci		if (ue[i].gsi >= KVM_MAX_IRQ_ROUTES)
1858c2ecf20Sopenharmony_ci			return -EINVAL;
1868c2ecf20Sopenharmony_ci		nr_rt_entries = max(nr_rt_entries, ue[i].gsi);
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	nr_rt_entries += 1;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	new = kzalloc(struct_size(new, map, nr_rt_entries), GFP_KERNEL_ACCOUNT);
1928c2ecf20Sopenharmony_ci	if (!new)
1938c2ecf20Sopenharmony_ci		return -ENOMEM;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	new->nr_rt_entries = nr_rt_entries;
1968c2ecf20Sopenharmony_ci	for (i = 0; i < KVM_NR_IRQCHIPS; i++)
1978c2ecf20Sopenharmony_ci		for (j = 0; j < KVM_IRQCHIP_NUM_PINS; j++)
1988c2ecf20Sopenharmony_ci			new->chip[i][j] = -1;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	for (i = 0; i < nr; ++i) {
2018c2ecf20Sopenharmony_ci		r = -ENOMEM;
2028c2ecf20Sopenharmony_ci		e = kzalloc(sizeof(*e), GFP_KERNEL_ACCOUNT);
2038c2ecf20Sopenharmony_ci		if (!e)
2048c2ecf20Sopenharmony_ci			goto out;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		r = -EINVAL;
2078c2ecf20Sopenharmony_ci		switch (ue->type) {
2088c2ecf20Sopenharmony_ci		case KVM_IRQ_ROUTING_MSI:
2098c2ecf20Sopenharmony_ci			if (ue->flags & ~KVM_MSI_VALID_DEVID)
2108c2ecf20Sopenharmony_ci				goto free_entry;
2118c2ecf20Sopenharmony_ci			break;
2128c2ecf20Sopenharmony_ci		default:
2138c2ecf20Sopenharmony_ci			if (ue->flags)
2148c2ecf20Sopenharmony_ci				goto free_entry;
2158c2ecf20Sopenharmony_ci			break;
2168c2ecf20Sopenharmony_ci		}
2178c2ecf20Sopenharmony_ci		r = setup_routing_entry(kvm, new, e, ue);
2188c2ecf20Sopenharmony_ci		if (r)
2198c2ecf20Sopenharmony_ci			goto free_entry;
2208c2ecf20Sopenharmony_ci		++ue;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	mutex_lock(&kvm->irq_lock);
2248c2ecf20Sopenharmony_ci	old = rcu_dereference_protected(kvm->irq_routing, 1);
2258c2ecf20Sopenharmony_ci	rcu_assign_pointer(kvm->irq_routing, new);
2268c2ecf20Sopenharmony_ci	kvm_irq_routing_update(kvm);
2278c2ecf20Sopenharmony_ci	kvm_arch_irq_routing_update(kvm);
2288c2ecf20Sopenharmony_ci	mutex_unlock(&kvm->irq_lock);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	kvm_arch_post_irq_routing_update(kvm);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	synchronize_srcu_expedited(&kvm->irq_srcu);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	new = old;
2358c2ecf20Sopenharmony_ci	r = 0;
2368c2ecf20Sopenharmony_ci	goto out;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cifree_entry:
2398c2ecf20Sopenharmony_ci	kfree(e);
2408c2ecf20Sopenharmony_ciout:
2418c2ecf20Sopenharmony_ci	free_irq_routing_table(new);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return r;
2448c2ecf20Sopenharmony_ci}
245