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