Skip to content

Commit 7450683

Browse files
Ricardo KollerMarc Zyngier
authored andcommitted
KVM: selftests: aarch64: Add function for accessing GICv3 dist and redist registers
Add a generic library function for reading and writing GICv3 distributor and redistributor registers. Then adapt some functions to use it; more will come and use it in the next commit. Signed-off-by: Ricardo Koller <[email protected]> Acked-by: Andrew Jones <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 33a1ca7 commit 7450683

File tree

1 file changed

+101
-23
lines changed
  • tools/testing/selftests/kvm/lib/aarch64

1 file changed

+101
-23
lines changed

tools/testing/selftests/kvm/lib/aarch64/gic_v3.c

Lines changed: 101 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ struct gicv3_data {
1919
unsigned int nr_spis;
2020
};
2121

22-
#define sgi_base_from_redist(redist_base) (redist_base + SZ_64K)
22+
#define sgi_base_from_redist(redist_base) (redist_base + SZ_64K)
23+
#define DIST_BIT (1U << 31)
2324

2425
enum gicv3_intid_range {
2526
SGI_RANGE,
@@ -50,6 +51,14 @@ static void gicv3_gicr_wait_for_rwp(void *redist_base)
5051
}
5152
}
5253

54+
static void gicv3_wait_for_rwp(uint32_t cpu_or_dist)
55+
{
56+
if (cpu_or_dist & DIST_BIT)
57+
gicv3_gicd_wait_for_rwp();
58+
else
59+
gicv3_gicr_wait_for_rwp(gicv3_data.redist_base[cpu_or_dist]);
60+
}
61+
5362
static enum gicv3_intid_range get_intid_range(unsigned int intid)
5463
{
5564
switch (intid) {
@@ -81,39 +90,108 @@ static void gicv3_write_eoir(uint32_t irq)
8190
isb();
8291
}
8392

84-
static void
85-
gicv3_config_irq(unsigned int intid, unsigned int offset)
93+
uint32_t gicv3_reg_readl(uint32_t cpu_or_dist, uint64_t offset)
94+
{
95+
void *base = cpu_or_dist & DIST_BIT ? gicv3_data.dist_base
96+
: sgi_base_from_redist(gicv3_data.redist_base[cpu_or_dist]);
97+
return readl(base + offset);
98+
}
99+
100+
void gicv3_reg_writel(uint32_t cpu_or_dist, uint64_t offset, uint32_t reg_val)
101+
{
102+
void *base = cpu_or_dist & DIST_BIT ? gicv3_data.dist_base
103+
: sgi_base_from_redist(gicv3_data.redist_base[cpu_or_dist]);
104+
writel(reg_val, base + offset);
105+
}
106+
107+
uint32_t gicv3_getl_fields(uint32_t cpu_or_dist, uint64_t offset, uint32_t mask)
108+
{
109+
return gicv3_reg_readl(cpu_or_dist, offset) & mask;
110+
}
111+
112+
void gicv3_setl_fields(uint32_t cpu_or_dist, uint64_t offset,
113+
uint32_t mask, uint32_t reg_val)
114+
{
115+
uint32_t tmp = gicv3_reg_readl(cpu_or_dist, offset) & ~mask;
116+
117+
tmp |= (reg_val & mask);
118+
gicv3_reg_writel(cpu_or_dist, offset, tmp);
119+
}
120+
121+
/*
122+
* We use a single offset for the distributor and redistributor maps as they
123+
* have the same value in both. The only exceptions are registers that only
124+
* exist in one and not the other, like GICR_WAKER that doesn't exist in the
125+
* distributor map. Such registers are conveniently marked as reserved in the
126+
* map that doesn't implement it; like GICR_WAKER's offset of 0x0014 being
127+
* marked as "Reserved" in the Distributor map.
128+
*/
129+
static void gicv3_access_reg(uint32_t intid, uint64_t offset,
130+
uint32_t reg_bits, uint32_t bits_per_field,
131+
bool write, uint32_t *val)
86132
{
87133
uint32_t cpu = guest_get_vcpuid();
88-
uint32_t mask = 1 << (intid % 32);
89134
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-
}
135+
uint32_t fields_per_reg, index, mask, shift;
136+
uint32_t cpu_or_dist;
137+
138+
GUEST_ASSERT(bits_per_field <= reg_bits);
139+
GUEST_ASSERT(*val < (1U << bits_per_field));
140+
/* Some registers like IROUTER are 64 bit long. Those are currently not
141+
* supported by readl nor writel, so just asserting here until then.
142+
*/
143+
GUEST_ASSERT(reg_bits == 32);
144+
145+
fields_per_reg = reg_bits / bits_per_field;
146+
index = intid % fields_per_reg;
147+
shift = index * bits_per_field;
148+
mask = ((1U << bits_per_field) - 1) << shift;
149+
150+
/* Set offset to the actual register holding intid's config. */
151+
offset += (intid / fields_per_reg) * (reg_bits / 8);
152+
153+
cpu_or_dist = (intid_range == SPI_RANGE) ? DIST_BIT : cpu;
154+
155+
if (write)
156+
gicv3_setl_fields(cpu_or_dist, offset, mask, *val << shift);
157+
*val = gicv3_getl_fields(cpu_or_dist, offset, mask) >> shift;
158+
}
159+
160+
static void gicv3_write_reg(uint32_t intid, uint64_t offset,
161+
uint32_t reg_bits, uint32_t bits_per_field, uint32_t val)
162+
{
163+
gicv3_access_reg(intid, offset, reg_bits,
164+
bits_per_field, true, &val);
165+
}
166+
167+
static uint32_t gicv3_read_reg(uint32_t intid, uint64_t offset,
168+
uint32_t reg_bits, uint32_t bits_per_field)
169+
{
170+
uint32_t val;
171+
172+
gicv3_access_reg(intid, offset, reg_bits,
173+
bits_per_field, false, &val);
174+
return val;
107175
}
108176

109177
static void gicv3_irq_enable(unsigned int intid)
110178
{
111-
gicv3_config_irq(intid, GICD_ISENABLER);
179+
bool is_spi = get_intid_range(intid) == SPI_RANGE;
180+
unsigned int val = 1;
181+
uint32_t cpu = guest_get_vcpuid();
182+
183+
gicv3_write_reg(intid, GICD_ISENABLER, 32, 1, val);
184+
gicv3_wait_for_rwp(is_spi ? DIST_BIT : cpu);
112185
}
113186

114187
static void gicv3_irq_disable(unsigned int intid)
115188
{
116-
gicv3_config_irq(intid, GICD_ICENABLER);
189+
bool is_spi = get_intid_range(intid) == SPI_RANGE;
190+
uint32_t val = 1;
191+
uint32_t cpu = guest_get_vcpuid();
192+
193+
gicv3_write_reg(intid, GICD_ICENABLER, 32, 1, val);
194+
gicv3_wait_for_rwp(is_spi ? DIST_BIT : cpu);
117195
}
118196

119197
static void gicv3_enable_redist(void *redist_base)

0 commit comments

Comments
 (0)