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 */
mshv_update_routing_table(struct mshv_partition * partition,const struct mshv_user_irq_entry * ue,unsigned int numents)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 */
mshv_free_routing_table(struct mshv_partition * partition)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
mshv_ret_girq_entry(struct mshv_partition * partition,u32 irqnum)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
mshv_copy_girq_info(struct mshv_guest_irq_ent * ent,struct mshv_lapic_irq * lirq)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