arm64: vmm: Split out VGIC register handling to a common file

Reviewed by:	Sarah Walker <[email protected]>
Sponsored by:	Arm Ltd
This commit is contained in:
Andrew Turner committed 2026-08-26 12:17:18 +01:00
1 parent 7823d306d3
commit 1ecf7d5b38
3 files changed
+136 -129

No files matched your search

+78 -1
View File
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2023 Arm Ltd
* Copyright (c) 2023,2026 Arm Ltd
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,9 +32,86 @@
#include "vgic.h"
#include "vgic_if.h"
#include "vgic_internal.h"
device_t vgic_dev;
/* Common register read-only/write-ignored helpers */
void
vgic_zero_read(struct hypctx *hypctx, u_int reg, uint64_t *rval,
void *arg)
{
*rval = 0;
}
void
vgic_ignore_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
uint64_t wval, void *arg)
{
/* Nothing to do */
}
bool
vgic_register_read(struct hypctx *hypctx, struct vgic_register *reg_list,
u_int reg_list_size, u_int reg, u_int size, uint64_t *rval, void *arg)
{
u_int i, offset;
for (i = 0; i < reg_list_size; i++) {
if (reg_list[i].start <= reg && reg_list[i].end >= reg + size) {
offset = reg & (reg_list[i].size - 1);
reg -= offset;
if ((reg_list[i].flags & size) != 0) {
reg_list[i].read(hypctx, reg, rval, NULL);
/* Move the bits into the correct place */
*rval >>= (offset * 8);
if (size < 8) {
*rval &= (1ul << (size * 8)) - 1;
}
} else {
/*
* The access is an invalid size. Section
* 12.1.3 "GIC memory-mapped register access"
* of the GICv3 and GICv4 spec issue H
* (IHI0069) lists the options. For a read
* the controller returns unknown data, in
* this case it is zero.
*/
*rval = 0;
}
return (true);
}
}
return (false);
}
bool
vgic_register_write(struct hypctx *hypctx, struct vgic_register *reg_list,
u_int reg_list_size, u_int reg, u_int size, uint64_t wval, void *arg)
{
u_int i, offset;
for (i = 0; i < reg_list_size; i++) {
if (reg_list[i].start <= reg && reg_list[i].end >= reg + size) {
offset = reg & (reg_list[i].size - 1);
reg -= offset;
if ((reg_list[i].flags & size) != 0) {
reg_list[i].write(hypctx, reg, offset,
size, wval, NULL);
} else {
/*
* See the comment in vgic_register_read.
* For writes the controller ignores the
* operation.
*/
}
return (true);
}
}
return (false);
}
bool
vgic_present(void)
{
+49
View File
@@ -80,4 +80,53 @@ struct vgic_v3_cpu {
u_int ich_lr_used;
};
typedef void (register_read)(struct hypctx *, u_int, uint64_t *, void *);
typedef void (register_write)(struct hypctx *, u_int, u_int, u_int,
uint64_t, void *);
register_read vgic_zero_read;
register_write vgic_ignore_write;
#define VGIC_8_BIT (1 << 0)
/* (1 << 1) is reserved for 16 bit accesses */
#define VGIC_32_BIT (1 << 2)
#define VGIC_64_BIT (1 << 3)
struct vgic_register {
u_int start; /* Start within a memory region */
u_int end;
u_int size;
u_int flags;
register_read *read;
register_write *write;
};
#define VGIC_REGISTER_RANGE(reg_start, reg_end, reg_size, reg_flags, readf, \
writef) \
{ \
.start = (reg_start), \
.end = (reg_end), \
.size = (reg_size), \
.flags = (reg_flags), \
.read = (readf), \
.write = (writef), \
}
#define VGIC_REGISTER_RANGE_RAZ_WI(reg_start, reg_end, reg_size, reg_flags) \
VGIC_REGISTER_RANGE(reg_start, reg_end, reg_size, reg_flags, \
vgic_zero_read, vgic_ignore_write)
#define VGIC_REGISTER(start_addr, reg_size, reg_flags, readf, writef) \
VGIC_REGISTER_RANGE(start_addr, (start_addr) + (reg_size), \
reg_size, reg_flags, readf, writef)
#define VGIC_REGISTER_RAZ_WI(start_addr, reg_size, reg_flags) \
VGIC_REGISTER_RANGE_RAZ_WI(start_addr, \
(start_addr) + (reg_size), reg_size, reg_flags)
bool vgic_register_read(struct hypctx *, struct vgic_register *, u_int, u_int,
u_int, uint64_t *, void *);
bool vgic_register_write(struct hypctx *, struct vgic_register *, u_int, u_int,
u_int, uint64_t, void *);
#endif /* _VGIC_INTERNAL_H_ */
+9 -128
View File
@@ -107,50 +107,7 @@ static int vgic_v3_max_cpu_count(device_t dev, struct hyp *hyp);
#define INJECT_IRQ(hyp, vcpuid, irqid, level) \
vgic_v3_inject_irq(NULL, (hyp), (vcpuid), (irqid), (level))
typedef void (register_read)(struct hypctx *, u_int, uint64_t *, void *);
typedef void (register_write)(struct hypctx *, u_int, u_int, u_int,
uint64_t, void *);
#define VGIC_8_BIT (1 << 0)
/* (1 << 1) is reserved for 16 bit accesses */
#define VGIC_32_BIT (1 << 2)
#define VGIC_64_BIT (1 << 3)
struct vgic_register {
u_int start; /* Start within a memory region */
u_int end;
u_int size;
u_int flags;
register_read *read;
register_write *write;
};
#define VGIC_REGISTER_RANGE(reg_start, reg_end, reg_size, reg_flags, readf, \
writef) \
{ \
.start = (reg_start), \
.end = (reg_end), \
.size = (reg_size), \
.flags = (reg_flags), \
.read = (readf), \
.write = (writef), \
}
#define VGIC_REGISTER_RANGE_RAZ_WI(reg_start, reg_end, reg_size, reg_flags) \
VGIC_REGISTER_RANGE(reg_start, reg_end, reg_size, reg_flags, \
gic_zero_read, gic_ignore_write)
#define VGIC_REGISTER(start_addr, reg_size, reg_flags, readf, writef) \
VGIC_REGISTER_RANGE(start_addr, (start_addr) + (reg_size), \
reg_size, reg_flags, readf, writef)
#define VGIC_REGISTER_RAZ_WI(start_addr, reg_size, reg_flags) \
VGIC_REGISTER_RANGE_RAZ_WI(start_addr, \
(start_addr) + (reg_size), reg_size, reg_flags)
static register_read gic_pidr2_read;
static register_read gic_zero_read;
static register_write gic_ignore_write;
/* GICD_CTLR */
static register_read dist_ctlr_read;
@@ -203,13 +160,13 @@ static struct vgic_register dist_registers[] = {
VGIC_REGISTER(GICD_CTLR, 4, VGIC_32_BIT, dist_ctlr_read,
dist_ctlr_write),
VGIC_REGISTER(GICD_TYPER, 4, VGIC_32_BIT, dist_typer_read,
gic_ignore_write),
vgic_ignore_write),
VGIC_REGISTER(GICD_IIDR, 4, VGIC_32_BIT, dist_iidr_read,
gic_ignore_write),
vgic_ignore_write),
VGIC_REGISTER_RAZ_WI(GICD_STATUSR, 4, VGIC_32_BIT),
VGIC_REGISTER(GICD_SETSPI_NSR, 4, VGIC_32_BIT, gic_zero_read,
VGIC_REGISTER(GICD_SETSPI_NSR, 4, VGIC_32_BIT, vgic_zero_read,
dist_setclrspi_nsr_write),
VGIC_REGISTER(GICD_CLRSPI_NSR, 4, VGIC_32_BIT, gic_zero_read,
VGIC_REGISTER(GICD_CLRSPI_NSR, 4, VGIC_32_BIT, vgic_zero_read,
dist_setclrspi_nsr_write),
VGIC_REGISTER_RAZ_WI(GICD_SETSPI_SR, 4, VGIC_32_BIT),
VGIC_REGISTER_RAZ_WI(GICD_CLRSPI_SR, 4, VGIC_32_BIT),
@@ -273,7 +230,7 @@ static struct vgic_register dist_registers[] = {
VGIC_REGISTER_RANGE_RAZ_WI(GICD_PIDR4, GICD_PIDR2, 4, VGIC_32_BIT),
VGIC_REGISTER(GICD_PIDR2, 4, VGIC_32_BIT, gic_pidr2_read,
gic_ignore_write),
vgic_ignore_write),
VGIC_REGISTER_RANGE_RAZ_WI(GICD_PIDR2 + 4, GICD_SIZE, 4, VGIC_32_BIT),
};
@@ -295,11 +252,11 @@ static register_read redist_typer_read;
static struct vgic_register redist_rd_registers[] = {
VGIC_REGISTER(GICR_CTLR, 4, VGIC_32_BIT, redist_ctlr_read,
gic_ignore_write),
vgic_ignore_write),
VGIC_REGISTER(GICR_IIDR, 4, VGIC_32_BIT, redist_iidr_read,
gic_ignore_write),
vgic_ignore_write),
VGIC_REGISTER(GICR_TYPER, 8, VGIC_64_BIT | VGIC_32_BIT,
redist_typer_read, gic_ignore_write),
redist_typer_read, vgic_ignore_write),
VGIC_REGISTER_RAZ_WI(GICR_STATUSR, 4, VGIC_32_BIT),
VGIC_REGISTER_RAZ_WI(GICR_WAKER, 4, VGIC_32_BIT),
VGIC_REGISTER_RAZ_WI(GICR_SETLPIR, 8, VGIC_64_BIT | VGIC_32_BIT),
@@ -313,7 +270,7 @@ static struct vgic_register redist_rd_registers[] = {
/* These are identical to the dist registers */
VGIC_REGISTER_RANGE_RAZ_WI(GICD_PIDR4, GICD_PIDR2, 4, VGIC_32_BIT),
VGIC_REGISTER(GICD_PIDR2, 4, VGIC_32_BIT, gic_pidr2_read,
gic_ignore_write),
vgic_ignore_write),
VGIC_REGISTER_RANGE_RAZ_WI(GICD_PIDR2 + 4, GICD_SIZE, 4,
VGIC_32_BIT),
};
@@ -597,21 +554,6 @@ gic_pidr2_read(struct hypctx *hypctx, u_int reg, uint64_t *rval,
*rval = GICR_PIDR2_ARCH_GICv3 << GICR_PIDR2_ARCH_SHIFT;
}
/* Common read-only/write-ignored helpers */
static void
gic_zero_read(struct hypctx *hypctx, u_int reg, uint64_t *rval,
void *arg)
{
*rval = 0;
}
static void
gic_ignore_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
uint64_t wval, void *arg)
{
/* Nothing to do */
}
static uint64_t
read_enabler(struct hypctx *hypctx, int n)
{
@@ -1262,67 +1204,6 @@ dist_irouter_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
write_route(hypctx, n, wval, offset, size);
}
static bool
vgic_register_read(struct hypctx *hypctx, struct vgic_register *reg_list,
u_int reg_list_size, u_int reg, u_int size, uint64_t *rval, void *arg)
{
u_int i, offset;
for (i = 0; i < reg_list_size; i++) {
if (reg_list[i].start <= reg && reg_list[i].end >= reg + size) {
offset = reg & (reg_list[i].size - 1);
reg -= offset;
if ((reg_list[i].flags & size) != 0) {
reg_list[i].read(hypctx, reg, rval, NULL);
/* Move the bits into the correct place */
*rval >>= (offset * 8);
if (size < 8) {
*rval &= (1ul << (size * 8)) - 1;
}
} else {
/*
* The access is an invalid size. Section
* 12.1.3 "GIC memory-mapped register access"
* of the GICv3 and GICv4 spec issue H
* (IHI0069) lists the options. For a read
* the controller returns unknown data, in
* this case it is zero.
*/
*rval = 0;
}
return (true);
}
}
return (false);
}
static bool
vgic_register_write(struct hypctx *hypctx, struct vgic_register *reg_list,
u_int reg_list_size, u_int reg, u_int size, uint64_t wval, void *arg)
{
u_int i, offset;
for (i = 0; i < reg_list_size; i++) {
if (reg_list[i].start <= reg && reg_list[i].end >= reg + size) {
offset = reg & (reg_list[i].size - 1);
reg -= offset;
if ((reg_list[i].flags & size) != 0) {
reg_list[i].write(hypctx, reg, offset,
size, wval, NULL);
} else {
/*
* See the comment in vgic_register_read.
* For writes the controller ignores the
* operation.
*/
}
return (true);
}
}
return (false);
}
static int
dist_read(struct vcpu *vcpu, uint64_t fault_ipa, uint64_t *rval,
int size, void *arg)