Skip to content

Commit 2828165

Browse files
rananta468Marc Zyngier
authored andcommitted
KVM: arm64: selftests: Add basic GICv3 support
Add basic support for ARM Generic Interrupt Controller v3. The support provides guests to setup interrupts. The work is inspired from kvm-unit-tests and the kernel's GIC driver (drivers/irqchip/irq-gic-v3.c). Signed-off-by: Raghavendra Rao Ananta <[email protected]> Reviewed-by: Andrew Jones <[email protected]> Reviewed-by: Ricardo Koller <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 414de89 commit 2828165

File tree

6 files changed

+448
-1
lines changed

6 files changed

+448
-1
lines changed

tools/testing/selftests/kvm/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ endif
3535

3636
LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/rbtree.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c
3737
LIBKVM_x86_64 = lib/x86_64/apic.c lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S
38-
LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c lib/aarch64/handlers.S lib/aarch64/spinlock.c
38+
LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c lib/aarch64/handlers.S lib/aarch64/spinlock.c lib/aarch64/gic.c lib/aarch64/gic_v3.c
3939
LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c
4040

4141
TEST_GEN_PROGS_x86_64 = x86_64/cr4_cpuid_sync_test
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* ARM Generic Interrupt Controller (GIC) specific defines
4+
*/
5+
6+
#ifndef SELFTEST_KVM_GIC_H
7+
#define SELFTEST_KVM_GIC_H
8+
9+
enum gic_type {
10+
GIC_V3,
11+
GIC_TYPE_MAX,
12+
};
13+
14+
void gic_init(enum gic_type type, unsigned int nr_cpus,
15+
void *dist_base, void *redist_base);
16+
void gic_irq_enable(unsigned int intid);
17+
void gic_irq_disable(unsigned int intid);
18+
unsigned int gic_get_and_ack_irq(void);
19+
void gic_set_eoi(unsigned int intid);
20+
21+
#endif /* SELFTEST_KVM_GIC_H */
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* ARM Generic Interrupt Controller (GIC) support
4+
*/
5+
6+
#include <errno.h>
7+
#include <linux/bits.h>
8+
#include <linux/sizes.h>
9+
10+
#include "kvm_util.h"
11+
12+
#include <gic.h>
13+
#include "gic_private.h"
14+
#include "processor.h"
15+
#include "spinlock.h"
16+
17+
static const struct gic_common_ops *gic_common_ops;
18+
static struct spinlock gic_lock;
19+
20+
static void gic_cpu_init(unsigned int cpu, void *redist_base)
21+
{
22+
gic_common_ops->gic_cpu_init(cpu, redist_base);
23+
}
24+
25+
static void
26+
gic_dist_init(enum gic_type type, unsigned int nr_cpus, void *dist_base)
27+
{
28+
const struct gic_common_ops *gic_ops = NULL;
29+
30+
spin_lock(&gic_lock);
31+
32+
/* Distributor initialization is needed only once per VM */
33+
if (gic_common_ops) {
34+
spin_unlock(&gic_lock);
35+
return;
36+
}
37+
38+
if (type == GIC_V3)
39+
gic_ops = &gicv3_ops;
40+
41+
GUEST_ASSERT(gic_ops);
42+
43+
gic_ops->gic_init(nr_cpus, dist_base);
44+
gic_common_ops = gic_ops;
45+
46+
/* Make sure that the initialized data is visible to all the vCPUs */
47+
dsb(sy);
48+
49+
spin_unlock(&gic_lock);
50+
}
51+
52+
void gic_init(enum gic_type type, unsigned int nr_cpus,
53+
void *dist_base, void *redist_base)
54+
{
55+
uint32_t cpu = guest_get_vcpuid();
56+
57+
GUEST_ASSERT(type < GIC_TYPE_MAX);
58+
GUEST_ASSERT(dist_base);
59+
GUEST_ASSERT(redist_base);
60+
GUEST_ASSERT(nr_cpus);
61+
62+
gic_dist_init(type, nr_cpus, dist_base);
63+
gic_cpu_init(cpu, redist_base);
64+
}
65+
66+
void gic_irq_enable(unsigned int intid)
67+
{
68+
GUEST_ASSERT(gic_common_ops);
69+
gic_common_ops->gic_irq_enable(intid);
70+
}
71+
72+
void gic_irq_disable(unsigned int intid)
73+
{
74+
GUEST_ASSERT(gic_common_ops);
75+
gic_common_ops->gic_irq_disable(intid);
76+
}
77+
78+
unsigned int gic_get_and_ack_irq(void)
79+
{
80+
uint64_t irqstat;
81+
unsigned int intid;
82+
83+
GUEST_ASSERT(gic_common_ops);
84+
85+
irqstat = gic_common_ops->gic_read_iar();
86+
intid = irqstat & GENMASK(23, 0);
87+
88+
return intid;
89+
}
90+
91+
void gic_set_eoi(unsigned int intid)
92+
{
93+
GUEST_ASSERT(gic_common_ops);
94+
gic_common_ops->gic_write_eoir(intid);
95+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* ARM Generic Interrupt Controller (GIC) private defines that's only
4+
* shared among the GIC library code.
5+
*/
6+
7+
#ifndef SELFTEST_KVM_GIC_PRIVATE_H
8+
#define SELFTEST_KVM_GIC_PRIVATE_H
9+
10+
struct gic_common_ops {
11+
void (*gic_init)(unsigned int nr_cpus, void *dist_base);
12+
void (*gic_cpu_init)(unsigned int cpu, void *redist_base);
13+
void (*gic_irq_enable)(unsigned int intid);
14+
void (*gic_irq_disable)(unsigned int intid);
15+
uint64_t (*gic_read_iar)(void);
16+
void (*gic_write_eoir)(uint32_t irq);
17+
};
18+
19+
extern const struct gic_common_ops gicv3_ops;
20+
21+
#endif /* SELFTEST_KVM_GIC_PRIVATE_H */
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* ARM Generic Interrupt Controller (GIC) v3 support
4+
*/
5+
6+
#include <linux/sizes.h>
7+
8+
#include "kvm_util.h"
9+
#include "processor.h"
10+
#include "delay.h"
11+
12+
#include "gic_v3.h"
13+
#include "gic_private.h"
14+
15+
struct gicv3_data {
16+
void *dist_base;
17+
void *redist_base[GICV3_MAX_CPUS];
18+
unsigned int nr_cpus;
19+
unsigned int nr_spis;
20+
};
21+
22+
#define sgi_base_from_redist(redist_base) (redist_base + SZ_64K)
23+
24+
enum gicv3_intid_range {
25+
SGI_RANGE,
26+
PPI_RANGE,
27+
SPI_RANGE,
28+
INVALID_RANGE,
29+
};
30+
31+
static struct gicv3_data gicv3_data;
32+
33+
static void gicv3_gicd_wait_for_rwp(void)
34+
{
35+
unsigned int count = 100000; /* 1s */
36+
37+
while (readl(gicv3_data.dist_base + GICD_CTLR) & GICD_CTLR_RWP) {
38+
GUEST_ASSERT(count--);
39+
udelay(10);
40+
}
41+
}
42+
43+
static void gicv3_gicr_wait_for_rwp(void *redist_base)
44+
{
45+
unsigned int count = 100000; /* 1s */
46+
47+
while (readl(redist_base + GICR_CTLR) & GICR_CTLR_RWP) {
48+
GUEST_ASSERT(count--);
49+
udelay(10);
50+
}
51+
}
52+
53+
static enum gicv3_intid_range get_intid_range(unsigned int intid)
54+
{
55+
switch (intid) {
56+
case 0 ... 15:
57+
return SGI_RANGE;
58+
case 16 ... 31:
59+
return PPI_RANGE;
60+
case 32 ... 1019:
61+
return SPI_RANGE;
62+
}
63+
64+
/* We should not be reaching here */
65+
GUEST_ASSERT(0);
66+
67+
return INVALID_RANGE;
68+
}
69+
70+
static uint64_t gicv3_read_iar(void)
71+
{
72+
uint64_t irqstat = read_sysreg_s(SYS_ICC_IAR1_EL1);
73+
74+
dsb(sy);
75+
return irqstat;
76+
}
77+
78+
static void gicv3_write_eoir(uint32_t irq)
79+
{
80+
write_sysreg_s(irq, SYS_ICC_EOIR1_EL1);
81+
isb();
82+
}
83+
84+
static void
85+
gicv3_config_irq(unsigned int intid, unsigned int offset)
86+
{
87+
uint32_t cpu = guest_get_vcpuid();
88+
uint32_t mask = 1 << (intid % 32);
89+
enum gicv3_intid_range intid_range = get_intid_range(intid);
90+
void *reg;
91+
92+
/* We care about 'cpu' only for SGIs or PPIs */
93+
if (intid_range == SGI_RANGE || intid_range == PPI_RANGE) {
94+
GUEST_ASSERT(cpu < gicv3_data.nr_cpus);
95+
96+
reg = sgi_base_from_redist(gicv3_data.redist_base[cpu]) +
97+
offset;
98+
writel(mask, reg);
99+
gicv3_gicr_wait_for_rwp(gicv3_data.redist_base[cpu]);
100+
} else if (intid_range == SPI_RANGE) {
101+
reg = gicv3_data.dist_base + offset + (intid / 32) * 4;
102+
writel(mask, reg);
103+
gicv3_gicd_wait_for_rwp();
104+
} else {
105+
GUEST_ASSERT(0);
106+
}
107+
}
108+
109+
static void gicv3_irq_enable(unsigned int intid)
110+
{
111+
gicv3_config_irq(intid, GICD_ISENABLER);
112+
}
113+
114+
static void gicv3_irq_disable(unsigned int intid)
115+
{
116+
gicv3_config_irq(intid, GICD_ICENABLER);
117+
}
118+
119+
static void gicv3_enable_redist(void *redist_base)
120+
{
121+
uint32_t val = readl(redist_base + GICR_WAKER);
122+
unsigned int count = 100000; /* 1s */
123+
124+
val &= ~GICR_WAKER_ProcessorSleep;
125+
writel(val, redist_base + GICR_WAKER);
126+
127+
/* Wait until the processor is 'active' */
128+
while (readl(redist_base + GICR_WAKER) & GICR_WAKER_ChildrenAsleep) {
129+
GUEST_ASSERT(count--);
130+
udelay(10);
131+
}
132+
}
133+
134+
static inline void *gicr_base_cpu(void *redist_base, uint32_t cpu)
135+
{
136+
/* Align all the redistributors sequentially */
137+
return redist_base + cpu * SZ_64K * 2;
138+
}
139+
140+
static void gicv3_cpu_init(unsigned int cpu, void *redist_base)
141+
{
142+
void *sgi_base;
143+
unsigned int i;
144+
void *redist_base_cpu;
145+
146+
GUEST_ASSERT(cpu < gicv3_data.nr_cpus);
147+
148+
redist_base_cpu = gicr_base_cpu(redist_base, cpu);
149+
sgi_base = sgi_base_from_redist(redist_base_cpu);
150+
151+
gicv3_enable_redist(redist_base_cpu);
152+
153+
/*
154+
* Mark all the SGI and PPI interrupts as non-secure Group-1.
155+
* Also, deactivate and disable them.
156+
*/
157+
writel(~0, sgi_base + GICR_IGROUPR0);
158+
writel(~0, sgi_base + GICR_ICACTIVER0);
159+
writel(~0, sgi_base + GICR_ICENABLER0);
160+
161+
/* Set a default priority for all the SGIs and PPIs */
162+
for (i = 0; i < 32; i += 4)
163+
writel(GICD_INT_DEF_PRI_X4,
164+
sgi_base + GICR_IPRIORITYR0 + i);
165+
166+
gicv3_gicr_wait_for_rwp(redist_base_cpu);
167+
168+
/* Enable the GIC system register (ICC_*) access */
169+
write_sysreg_s(read_sysreg_s(SYS_ICC_SRE_EL1) | ICC_SRE_EL1_SRE,
170+
SYS_ICC_SRE_EL1);
171+
172+
/* Set a default priority threshold */
173+
write_sysreg_s(ICC_PMR_DEF_PRIO, SYS_ICC_PMR_EL1);
174+
175+
/* Enable non-secure Group-1 interrupts */
176+
write_sysreg_s(ICC_IGRPEN1_EL1_ENABLE, SYS_ICC_GRPEN1_EL1);
177+
178+
gicv3_data.redist_base[cpu] = redist_base_cpu;
179+
}
180+
181+
static void gicv3_dist_init(void)
182+
{
183+
void *dist_base = gicv3_data.dist_base;
184+
unsigned int i;
185+
186+
/* Disable the distributor until we set things up */
187+
writel(0, dist_base + GICD_CTLR);
188+
gicv3_gicd_wait_for_rwp();
189+
190+
/*
191+
* Mark all the SPI interrupts as non-secure Group-1.
192+
* Also, deactivate and disable them.
193+
*/
194+
for (i = 32; i < gicv3_data.nr_spis; i += 32) {
195+
writel(~0, dist_base + GICD_IGROUPR + i / 8);
196+
writel(~0, dist_base + GICD_ICACTIVER + i / 8);
197+
writel(~0, dist_base + GICD_ICENABLER + i / 8);
198+
}
199+
200+
/* Set a default priority for all the SPIs */
201+
for (i = 32; i < gicv3_data.nr_spis; i += 4)
202+
writel(GICD_INT_DEF_PRI_X4,
203+
dist_base + GICD_IPRIORITYR + i);
204+
205+
/* Wait for the settings to sync-in */
206+
gicv3_gicd_wait_for_rwp();
207+
208+
/* Finally, enable the distributor globally with ARE */
209+
writel(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A |
210+
GICD_CTLR_ENABLE_G1, dist_base + GICD_CTLR);
211+
gicv3_gicd_wait_for_rwp();
212+
}
213+
214+
static void gicv3_init(unsigned int nr_cpus, void *dist_base)
215+
{
216+
GUEST_ASSERT(nr_cpus <= GICV3_MAX_CPUS);
217+
218+
gicv3_data.nr_cpus = nr_cpus;
219+
gicv3_data.dist_base = dist_base;
220+
gicv3_data.nr_spis = GICD_TYPER_SPIS(
221+
readl(gicv3_data.dist_base + GICD_TYPER));
222+
if (gicv3_data.nr_spis > 1020)
223+
gicv3_data.nr_spis = 1020;
224+
225+
/*
226+
* Initialize only the distributor for now.
227+
* The redistributor and CPU interfaces are initialized
228+
* later for every PE.
229+
*/
230+
gicv3_dist_init();
231+
}
232+
233+
const struct gic_common_ops gicv3_ops = {
234+
.gic_init = gicv3_init,
235+
.gic_cpu_init = gicv3_cpu_init,
236+
.gic_irq_enable = gicv3_irq_enable,
237+
.gic_irq_disable = gicv3_irq_disable,
238+
.gic_read_iar = gicv3_read_iar,
239+
.gic_write_eoir = gicv3_write_eoir,
240+
};

0 commit comments

Comments
 (0)