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_flex(*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 75 trace_mshv_update_routing_table(partition->pt_id, 76 old, new, numents); 77 78 new = old; 79 80 out: 81 kfree(new); 82 83 return r; 84 } 85 86 /* vm is going away, kfree the irq routing table */ 87 void mshv_free_routing_table(struct mshv_partition *partition) 88 { 89 struct mshv_girq_routing_table *rt = 90 rcu_access_pointer(partition->pt_girq_tbl); 91 92 kfree(rt); 93 } 94 95 struct mshv_guest_irq_ent 96 mshv_ret_girq_entry(struct mshv_partition *partition, u32 irqnum) 97 { 98 struct mshv_guest_irq_ent entry = { 0 }; 99 struct mshv_girq_routing_table *girq_tbl; 100 101 girq_tbl = srcu_dereference_check(partition->pt_girq_tbl, 102 &partition->pt_irq_srcu, 103 lockdep_is_held(&partition->pt_irq_lock)); 104 if (!girq_tbl || irqnum >= girq_tbl->num_rt_entries) { 105 /* 106 * Premature register_irqfd, setting valid_entry = 0 107 * would ignore this entry anyway 108 */ 109 entry.guest_irq_num = irqnum; 110 return entry; 111 } 112 113 return girq_tbl->mshv_girq_info_tbl[irqnum]; 114 } 115 116 void mshv_copy_girq_info(struct mshv_guest_irq_ent *ent, 117 struct mshv_lapic_irq *lirq) 118 { 119 memset(lirq, 0, sizeof(*lirq)); 120 if (!ent || !ent->girq_entry_valid) 121 return; 122 123 lirq->lapic_vector = ent->girq_irq_data & 0xFF; 124 lirq->lapic_apic_id = (ent->girq_addr_lo >> 12) & 0xFF; 125 lirq->lapic_control.interrupt_type = (ent->girq_irq_data & 0x700) >> 8; 126 #if IS_ENABLED(CONFIG_X86) 127 lirq->lapic_control.level_triggered = (ent->girq_irq_data >> 15) & 0x1; 128 lirq->lapic_control.logical_dest_mode = (ent->girq_addr_lo >> 2) & 0x1; 129 #elif IS_ENABLED(CONFIG_ARM64) 130 lirq->lapic_control.asserted = 1; 131 #endif 132 } 133