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