xref: /linux/drivers/hv/mshv_irq.c (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
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