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(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 */
mshv_free_routing_table(struct mshv_partition * partition)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
mshv_ret_girq_entry(struct mshv_partition * partition,u32 irqnum)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 
mshv_copy_girq_info(struct mshv_guest_irq_ent * ent,struct mshv_lapic_irq * lirq)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