1*621191d7SNuno Das Neves // SPDX-License-Identifier: GPL-2.0-only 2*621191d7SNuno Das Neves /* 3*621191d7SNuno Das Neves * Copyright (c) 2023, Microsoft Corporation. 4*621191d7SNuno Das Neves * 5*621191d7SNuno Das Neves * Authors: Microsoft Linux virtualization team 6*621191d7SNuno Das Neves */ 7*621191d7SNuno Das Neves 8*621191d7SNuno Das Neves #include <linux/kernel.h> 9*621191d7SNuno Das Neves #include <linux/module.h> 10*621191d7SNuno Das Neves #include <linux/slab.h> 11*621191d7SNuno Das Neves #include <asm/mshyperv.h> 12*621191d7SNuno Das Neves 13*621191d7SNuno Das Neves #include "mshv_eventfd.h" 14*621191d7SNuno Das Neves #include "mshv.h" 15*621191d7SNuno Das Neves #include "mshv_root.h" 16*621191d7SNuno Das Neves 17*621191d7SNuno Das Neves /* called from the ioctl code, user wants to update the guest irq table */ 18*621191d7SNuno Das Neves int mshv_update_routing_table(struct mshv_partition *partition, 19*621191d7SNuno Das Neves const struct mshv_user_irq_entry *ue, 20*621191d7SNuno Das Neves unsigned int numents) 21*621191d7SNuno Das Neves { 22*621191d7SNuno Das Neves struct mshv_girq_routing_table *new = NULL, *old; 23*621191d7SNuno Das Neves u32 i, nr_rt_entries = 0; 24*621191d7SNuno Das Neves int r = 0; 25*621191d7SNuno Das Neves 26*621191d7SNuno Das Neves if (numents == 0) 27*621191d7SNuno Das Neves goto swap_routes; 28*621191d7SNuno Das Neves 29*621191d7SNuno Das Neves for (i = 0; i < numents; i++) { 30*621191d7SNuno Das Neves if (ue[i].gsi >= MSHV_MAX_GUEST_IRQS) 31*621191d7SNuno Das Neves return -EINVAL; 32*621191d7SNuno Das Neves 33*621191d7SNuno Das Neves if (ue[i].address_hi) 34*621191d7SNuno Das Neves return -EINVAL; 35*621191d7SNuno Das Neves 36*621191d7SNuno Das Neves nr_rt_entries = max(nr_rt_entries, ue[i].gsi); 37*621191d7SNuno Das Neves } 38*621191d7SNuno Das Neves nr_rt_entries += 1; 39*621191d7SNuno Das Neves 40*621191d7SNuno Das Neves new = kzalloc(struct_size(new, mshv_girq_info_tbl, nr_rt_entries), 41*621191d7SNuno Das Neves GFP_KERNEL_ACCOUNT); 42*621191d7SNuno Das Neves if (!new) 43*621191d7SNuno Das Neves return -ENOMEM; 44*621191d7SNuno Das Neves 45*621191d7SNuno Das Neves new->num_rt_entries = nr_rt_entries; 46*621191d7SNuno Das Neves for (i = 0; i < numents; i++) { 47*621191d7SNuno Das Neves struct mshv_guest_irq_ent *girq; 48*621191d7SNuno Das Neves 49*621191d7SNuno Das Neves girq = &new->mshv_girq_info_tbl[ue[i].gsi]; 50*621191d7SNuno Das Neves 51*621191d7SNuno Das Neves /* 52*621191d7SNuno Das Neves * Allow only one to one mapping between GSI and MSI routing. 53*621191d7SNuno Das Neves */ 54*621191d7SNuno Das Neves if (girq->guest_irq_num != 0) { 55*621191d7SNuno Das Neves r = -EINVAL; 56*621191d7SNuno Das Neves goto out; 57*621191d7SNuno Das Neves } 58*621191d7SNuno Das Neves 59*621191d7SNuno Das Neves girq->guest_irq_num = ue[i].gsi; 60*621191d7SNuno Das Neves girq->girq_addr_lo = ue[i].address_lo; 61*621191d7SNuno Das Neves girq->girq_addr_hi = ue[i].address_hi; 62*621191d7SNuno Das Neves girq->girq_irq_data = ue[i].data; 63*621191d7SNuno Das Neves girq->girq_entry_valid = true; 64*621191d7SNuno Das Neves } 65*621191d7SNuno Das Neves 66*621191d7SNuno Das Neves swap_routes: 67*621191d7SNuno Das Neves mutex_lock(&partition->pt_irq_lock); 68*621191d7SNuno Das Neves old = rcu_dereference_protected(partition->pt_girq_tbl, 1); 69*621191d7SNuno Das Neves rcu_assign_pointer(partition->pt_girq_tbl, new); 70*621191d7SNuno Das Neves mshv_irqfd_routing_update(partition); 71*621191d7SNuno Das Neves mutex_unlock(&partition->pt_irq_lock); 72*621191d7SNuno Das Neves 73*621191d7SNuno Das Neves synchronize_srcu_expedited(&partition->pt_irq_srcu); 74*621191d7SNuno Das Neves new = old; 75*621191d7SNuno Das Neves 76*621191d7SNuno Das Neves out: 77*621191d7SNuno Das Neves kfree(new); 78*621191d7SNuno Das Neves 79*621191d7SNuno Das Neves return r; 80*621191d7SNuno Das Neves } 81*621191d7SNuno Das Neves 82*621191d7SNuno Das Neves /* vm is going away, kfree the irq routing table */ 83*621191d7SNuno Das Neves void mshv_free_routing_table(struct mshv_partition *partition) 84*621191d7SNuno Das Neves { 85*621191d7SNuno Das Neves struct mshv_girq_routing_table *rt = 86*621191d7SNuno Das Neves rcu_access_pointer(partition->pt_girq_tbl); 87*621191d7SNuno Das Neves 88*621191d7SNuno Das Neves kfree(rt); 89*621191d7SNuno Das Neves } 90*621191d7SNuno Das Neves 91*621191d7SNuno Das Neves struct mshv_guest_irq_ent 92*621191d7SNuno Das Neves mshv_ret_girq_entry(struct mshv_partition *partition, u32 irqnum) 93*621191d7SNuno Das Neves { 94*621191d7SNuno Das Neves struct mshv_guest_irq_ent entry = { 0 }; 95*621191d7SNuno Das Neves struct mshv_girq_routing_table *girq_tbl; 96*621191d7SNuno Das Neves 97*621191d7SNuno Das Neves girq_tbl = srcu_dereference_check(partition->pt_girq_tbl, 98*621191d7SNuno Das Neves &partition->pt_irq_srcu, 99*621191d7SNuno Das Neves lockdep_is_held(&partition->pt_irq_lock)); 100*621191d7SNuno Das Neves if (!girq_tbl || irqnum >= girq_tbl->num_rt_entries) { 101*621191d7SNuno Das Neves /* 102*621191d7SNuno Das Neves * Premature register_irqfd, setting valid_entry = 0 103*621191d7SNuno Das Neves * would ignore this entry anyway 104*621191d7SNuno Das Neves */ 105*621191d7SNuno Das Neves entry.guest_irq_num = irqnum; 106*621191d7SNuno Das Neves return entry; 107*621191d7SNuno Das Neves } 108*621191d7SNuno Das Neves 109*621191d7SNuno Das Neves return girq_tbl->mshv_girq_info_tbl[irqnum]; 110*621191d7SNuno Das Neves } 111*621191d7SNuno Das Neves 112*621191d7SNuno Das Neves void mshv_copy_girq_info(struct mshv_guest_irq_ent *ent, 113*621191d7SNuno Das Neves struct mshv_lapic_irq *lirq) 114*621191d7SNuno Das Neves { 115*621191d7SNuno Das Neves memset(lirq, 0, sizeof(*lirq)); 116*621191d7SNuno Das Neves if (!ent || !ent->girq_entry_valid) 117*621191d7SNuno Das Neves return; 118*621191d7SNuno Das Neves 119*621191d7SNuno Das Neves lirq->lapic_vector = ent->girq_irq_data & 0xFF; 120*621191d7SNuno Das Neves lirq->lapic_apic_id = (ent->girq_addr_lo >> 12) & 0xFF; 121*621191d7SNuno Das Neves lirq->lapic_control.interrupt_type = (ent->girq_irq_data & 0x700) >> 8; 122*621191d7SNuno Das Neves lirq->lapic_control.level_triggered = (ent->girq_irq_data >> 15) & 0x1; 123*621191d7SNuno Das Neves lirq->lapic_control.logical_dest_mode = (ent->girq_addr_lo >> 2) & 0x1; 124*621191d7SNuno Das Neves } 125