xref: /kvmtool/arm/gicv2m.c (revision f6108d72e977cce00e7bc824acd1d73da8cc9729)
1*f6108d72SJean-Philippe Brucker #include <errno.h>
2*f6108d72SJean-Philippe Brucker #include <stdlib.h>
3*f6108d72SJean-Philippe Brucker 
4*f6108d72SJean-Philippe Brucker #include "kvm/irq.h"
5*f6108d72SJean-Philippe Brucker #include "kvm/kvm.h"
6*f6108d72SJean-Philippe Brucker #include "kvm/util.h"
7*f6108d72SJean-Philippe Brucker 
8*f6108d72SJean-Philippe Brucker #include "arm-common/gic.h"
9*f6108d72SJean-Philippe Brucker 
10*f6108d72SJean-Philippe Brucker #define GICV2M_MSI_TYPER	0x008
11*f6108d72SJean-Philippe Brucker #define GICV2M_MSI_SETSPI	0x040
12*f6108d72SJean-Philippe Brucker #define GICV2M_MSI_IIDR		0xfcc
13*f6108d72SJean-Philippe Brucker 
14*f6108d72SJean-Philippe Brucker #define GICV2M_SPI_MASK		0x3ff
15*f6108d72SJean-Philippe Brucker #define GICV2M_MSI_TYPER_VAL(start, nr)	\
16*f6108d72SJean-Philippe Brucker 	(((start) & GICV2M_SPI_MASK) << 16 | ((nr) & GICV2M_SPI_MASK))
17*f6108d72SJean-Philippe Brucker 
18*f6108d72SJean-Philippe Brucker struct gicv2m_chip {
19*f6108d72SJean-Philippe Brucker 	int	first_spi;
20*f6108d72SJean-Philippe Brucker 	int	num_spis;
21*f6108d72SJean-Philippe Brucker 	int	*spis;
22*f6108d72SJean-Philippe Brucker 	u64	base;
23*f6108d72SJean-Philippe Brucker 	u64	size;
24*f6108d72SJean-Philippe Brucker };
25*f6108d72SJean-Philippe Brucker 
26*f6108d72SJean-Philippe Brucker static struct gicv2m_chip v2m;
27*f6108d72SJean-Philippe Brucker 
28*f6108d72SJean-Philippe Brucker /*
29*f6108d72SJean-Philippe Brucker  * MSI routing is setup lazily, when the guest writes the MSI tables. The guest
30*f6108d72SJean-Philippe Brucker  * writes which SPI is associated to an MSI vector into the message data field.
31*f6108d72SJean-Philippe Brucker  * The IRQ code notifies us of any change to MSI routing via this callback.
32*f6108d72SJean-Philippe Brucker  * Store the MSI->SPI translation for later.
33*f6108d72SJean-Philippe Brucker  *
34*f6108d72SJean-Philippe Brucker  * Data is the GIC interrupt ID, that includes SGIs and PPIs. SGIs at 0-15, PPIs
35*f6108d72SJean-Philippe Brucker  * are 16-31 and SPIs are 32-1019. What we're saving for later is the MSI's GSI
36*f6108d72SJean-Philippe Brucker  * number, a logical ID used by KVM for routing. The GSI of an SPI is implicitly
37*f6108d72SJean-Philippe Brucker  * defined by KVM to be its pin number (SPI index), and the GSI of an MSI is
38*f6108d72SJean-Philippe Brucker  * allocated by kvmtool.
39*f6108d72SJean-Philippe Brucker  */
40*f6108d72SJean-Philippe Brucker static int gicv2m_update_routing(struct kvm *kvm,
41*f6108d72SJean-Philippe Brucker 				 struct kvm_irq_routing_entry *entry)
42*f6108d72SJean-Philippe Brucker {
43*f6108d72SJean-Philippe Brucker 	int spi;
44*f6108d72SJean-Philippe Brucker 
45*f6108d72SJean-Philippe Brucker 	if (entry->type != KVM_IRQ_ROUTING_MSI)
46*f6108d72SJean-Philippe Brucker 		return -EINVAL;
47*f6108d72SJean-Philippe Brucker 
48*f6108d72SJean-Philippe Brucker 	if (!entry->u.msi.address_hi && !entry->u.msi.address_lo)
49*f6108d72SJean-Philippe Brucker 		return 0;
50*f6108d72SJean-Philippe Brucker 
51*f6108d72SJean-Philippe Brucker 	spi = entry->u.msi.data & GICV2M_SPI_MASK;
52*f6108d72SJean-Philippe Brucker 	if (spi < v2m.first_spi || spi >= v2m.first_spi + v2m.num_spis) {
53*f6108d72SJean-Philippe Brucker 		pr_err("invalid SPI number %d", spi);
54*f6108d72SJean-Philippe Brucker 		return -EINVAL;
55*f6108d72SJean-Philippe Brucker 	}
56*f6108d72SJean-Philippe Brucker 
57*f6108d72SJean-Philippe Brucker 	v2m.spis[spi - v2m.first_spi] = entry->gsi;
58*f6108d72SJean-Philippe Brucker 
59*f6108d72SJean-Philippe Brucker 	return 0;
60*f6108d72SJean-Philippe Brucker }
61*f6108d72SJean-Philippe Brucker 
62*f6108d72SJean-Philippe Brucker /*
63*f6108d72SJean-Philippe Brucker  * Find SPI bound to the given MSI and return the associated GSI.
64*f6108d72SJean-Philippe Brucker  */
65*f6108d72SJean-Philippe Brucker static int gicv2m_translate_gsi(struct kvm *kvm, u32 gsi)
66*f6108d72SJean-Philippe Brucker {
67*f6108d72SJean-Philippe Brucker 	int i;
68*f6108d72SJean-Philippe Brucker 
69*f6108d72SJean-Philippe Brucker 	for (i = 0; i < v2m.num_spis; i++) {
70*f6108d72SJean-Philippe Brucker 		if (v2m.spis[i] == (int)gsi)
71*f6108d72SJean-Philippe Brucker 			return i + v2m.first_spi - KVM_IRQ_OFFSET;
72*f6108d72SJean-Philippe Brucker 	}
73*f6108d72SJean-Philippe Brucker 
74*f6108d72SJean-Philippe Brucker 	/* Not an MSI */
75*f6108d72SJean-Philippe Brucker 	return gsi;
76*f6108d72SJean-Philippe Brucker }
77*f6108d72SJean-Philippe Brucker 
78*f6108d72SJean-Philippe Brucker static bool gicv2m_can_signal_msi(struct kvm *kvm)
79*f6108d72SJean-Philippe Brucker {
80*f6108d72SJean-Philippe Brucker 	return true;
81*f6108d72SJean-Philippe Brucker }
82*f6108d72SJean-Philippe Brucker 
83*f6108d72SJean-Philippe Brucker /*
84*f6108d72SJean-Philippe Brucker  * Instead of setting up MSI routes, virtual devices can also trigger them
85*f6108d72SJean-Philippe Brucker  * manually (like a direct write to MSI_SETSPI). In this case, trigger the SPI
86*f6108d72SJean-Philippe Brucker  * directly.
87*f6108d72SJean-Philippe Brucker  */
88*f6108d72SJean-Philippe Brucker static int gicv2m_signal_msi(struct kvm *kvm, struct kvm_msi *msi)
89*f6108d72SJean-Philippe Brucker {
90*f6108d72SJean-Philippe Brucker 	int spi = msi->data & GICV2M_SPI_MASK;
91*f6108d72SJean-Philippe Brucker 
92*f6108d72SJean-Philippe Brucker 	if (spi < v2m.first_spi || spi >= v2m.first_spi + v2m.num_spis) {
93*f6108d72SJean-Philippe Brucker 		pr_err("invalid SPI number %d", spi);
94*f6108d72SJean-Philippe Brucker 		return -EINVAL;
95*f6108d72SJean-Philippe Brucker 	}
96*f6108d72SJean-Philippe Brucker 
97*f6108d72SJean-Philippe Brucker 	kvm__irq_trigger(kvm, spi);
98*f6108d72SJean-Philippe Brucker 	return 0;
99*f6108d72SJean-Philippe Brucker }
100*f6108d72SJean-Philippe Brucker 
101*f6108d72SJean-Philippe Brucker static struct msi_routing_ops gicv2m_routing = {
102*f6108d72SJean-Philippe Brucker 	.update_route	= gicv2m_update_routing,
103*f6108d72SJean-Philippe Brucker 	.translate_gsi	= gicv2m_translate_gsi,
104*f6108d72SJean-Philippe Brucker 	.can_signal_msi	= gicv2m_can_signal_msi,
105*f6108d72SJean-Philippe Brucker 	.signal_msi	= gicv2m_signal_msi,
106*f6108d72SJean-Philippe Brucker };
107*f6108d72SJean-Philippe Brucker 
108*f6108d72SJean-Philippe Brucker static void gicv2m_mmio_callback(struct kvm_cpu *vcpu, u64 addr, u8 *data,
109*f6108d72SJean-Philippe Brucker 				  u32 len, u8 is_write, void *ptr)
110*f6108d72SJean-Philippe Brucker {
111*f6108d72SJean-Philippe Brucker 	if (is_write)
112*f6108d72SJean-Philippe Brucker 		return;
113*f6108d72SJean-Philippe Brucker 
114*f6108d72SJean-Philippe Brucker 	addr -= v2m.base;
115*f6108d72SJean-Philippe Brucker 
116*f6108d72SJean-Philippe Brucker 	switch (addr) {
117*f6108d72SJean-Philippe Brucker 		case GICV2M_MSI_TYPER:
118*f6108d72SJean-Philippe Brucker 			*(u32 *)data = GICV2M_MSI_TYPER_VAL(v2m.first_spi,
119*f6108d72SJean-Philippe Brucker 							    v2m.num_spis);
120*f6108d72SJean-Philippe Brucker 			break;
121*f6108d72SJean-Philippe Brucker 		case GICV2M_MSI_IIDR:
122*f6108d72SJean-Philippe Brucker 			*(u32 *)data = 0x0;
123*f6108d72SJean-Philippe Brucker 			break;
124*f6108d72SJean-Philippe Brucker 	}
125*f6108d72SJean-Philippe Brucker }
126*f6108d72SJean-Philippe Brucker 
127*f6108d72SJean-Philippe Brucker int gic__create_gicv2m_frame(struct kvm *kvm, u64 base)
128*f6108d72SJean-Philippe Brucker {
129*f6108d72SJean-Philippe Brucker 	int i;
130*f6108d72SJean-Philippe Brucker 	int irq = irq__alloc_line();
131*f6108d72SJean-Philippe Brucker 
132*f6108d72SJean-Philippe Brucker 	v2m = (struct gicv2m_chip) {
133*f6108d72SJean-Philippe Brucker 		.first_spi	= irq,	/* Includes GIC_SPI_IRQ_BASE */
134*f6108d72SJean-Philippe Brucker 		.num_spis	= 64,	/* arbitrary */
135*f6108d72SJean-Philippe Brucker 		.base		= base,
136*f6108d72SJean-Philippe Brucker 		.size		= KVM_VGIC_V2M_SIZE,
137*f6108d72SJean-Philippe Brucker 	};
138*f6108d72SJean-Philippe Brucker 
139*f6108d72SJean-Philippe Brucker 	v2m.spis = calloc(v2m.num_spis, sizeof(int));
140*f6108d72SJean-Philippe Brucker 	if (!v2m.spis)
141*f6108d72SJean-Philippe Brucker 		return -ENOMEM;
142*f6108d72SJean-Philippe Brucker 
143*f6108d72SJean-Philippe Brucker 	v2m.spis[0] = -1;
144*f6108d72SJean-Philippe Brucker 	for (i = 1; i < v2m.num_spis; i++) {
145*f6108d72SJean-Philippe Brucker 		irq__alloc_line();
146*f6108d72SJean-Philippe Brucker 		v2m.spis[i] = -1;
147*f6108d72SJean-Philippe Brucker 	}
148*f6108d72SJean-Philippe Brucker 
149*f6108d72SJean-Philippe Brucker 	msi_routing_ops = &gicv2m_routing;
150*f6108d72SJean-Philippe Brucker 
151*f6108d72SJean-Philippe Brucker 	return kvm__register_mmio(kvm, base, KVM_VGIC_V2M_SIZE, false,
152*f6108d72SJean-Philippe Brucker 				  gicv2m_mmio_callback, kvm);
153*f6108d72SJean-Philippe Brucker }
154