Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F173939724
D60113.id187977.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D60113.id187977.diff
View Options
diff --git a/sys/arm64/arm64/gicv5.c b/sys/arm64/arm64/gicv5.c
--- a/sys/arm64/arm64/gicv5.c
+++ b/sys/arm64/arm64/gicv5.c
@@ -29,6 +29,7 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/bitset.h>
#include <sys/bus.h>
#include <sys/cpuset.h>
#include <sys/intr.h>
@@ -39,6 +40,7 @@
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/proc.h>
+#include <sys/queue.h>
#include <sys/rman.h>
#include <sys/smp.h>
@@ -91,11 +93,18 @@
#define IRS_CFG_WRITE_8(_irs, _reg, _val) \
bus_write_8((_irs)->irs_cfg, (_reg), (_val))
+#define NR_VM_IDS (MAXCPU * 2)
+BITSET_DEFINE(_vm_ids, NR_VM_IDS);
+
+SLIST_HEAD(vm_data_head, gicv5_vm_data);
+
struct gicv5_irs {
cpuset_t irs_cpus;
struct resource *irs_cfg;
uint64_t *ist_base;
+ uint64_t *vmt_base;
+
u_int irs_next_irq_cpu;
u_int irs_parange;
@@ -112,6 +121,30 @@
#ifdef INVARIANTS
bool irs_ready;
#endif
+
+ bool has_virt;
+
+ u_int vm_id_count;
+ u_int vpe_id_count;
+
+ struct mtx vm_lock;
+ struct _vm_ids vm_ids;
+ struct vm_data_head vm_data_head;
+
+ size_t vmd_size;
+ size_t vped_size;
+};
+
+struct gicv5_vm_data {
+ SLIST_ENTRY(gicv5_vm_data) node;
+
+ int vm_id;
+
+ uint64_t *vpet;
+ void *vpete;
+ uint32_t *lpi_ist;
+ uint32_t *spi_ist;
+ void *vmd;
};
struct gicv5_irqsrc {
@@ -250,6 +283,8 @@
{
irs->ist_base = contigmalloc(size, M_DEVBUF, M_WAITOK | M_ZERO, 0,
(1ul << irs->irs_parange) - 1, size, 0);
+ KASSERT(irs->ist_base != NULL,
+ ("%s: Failed to alloc %zu bytes", __func__, size));
if (sc->gic_coherent)
/* Ensure the IRS observed zeroed memory */
dsb(ishst);
@@ -379,6 +414,42 @@
mtx_unlock_spin(&irs->irs_lock);
}
+static void
+gicv5_irs_init_vmt(struct gicv5_softc *sc, struct gicv5_irs *irs,
+ int vm_id_count)
+{
+ uint64_t cfgr;
+
+ cfgr = (ilog2_int(vm_id_count) << IRS_VMT_VM_ID_BITS_SHIFT) &
+ IRS_VMT_VM_ID_BITS_MASK;
+ IRS_CFG_WRITE_4(irs, IRS_VMT_CFGR, cfgr);
+
+ KASSERT((vtophys(irs->vmt_base) & ~IRS_IST_BASER_ADDR_MASK) == 0,
+ ("%s: Invalid VMT base address %lx", __func__,
+ vtophys(irs->vmt_base)));
+ IRS_CFG_WRITE_8(irs, IRS_VMT_BASER, vtophys(irs->vmt_base) |
+ IRS_VMT_BASER_VALID);
+
+ mtx_lock_spin(&irs->irs_lock);
+ gicv5_wait_for_op(irs, IRS_VMT_STATUSR, IRS_VMT_STATUSR_IDLE, NULL);
+ mtx_unlock_spin(&irs->irs_lock);
+}
+
+static void
+gicv5_irs_alloc_vmt(struct gicv5_softc *sc, struct gicv5_irs *irs,
+ size_t size)
+{
+ irs->vmt_base = contigmalloc(size, M_DEVBUF, M_WAITOK | M_ZERO, 0,
+ IRS_VMT_BASER_ADDR_LIMIT, size, 0);
+ KASSERT(irs->vmt_base != NULL,
+ ("%s: Failed to alloc %zu bytes", __func__, size));
+ if (sc->gic_coherent)
+ /* Ensure the IRS observed zeroed memory */
+ dsb(ishst);
+ else
+ cpu_dcache_wbinv_range(irs->vmt_base, size);
+}
+
void
gicv5_irs_init(device_t dev, u_int idx, cpuset_t *cpuset)
{
@@ -394,6 +465,9 @@
M_ZERO | M_WAITOK);
CPU_COPY(cpuset, &sc->gic_irs[idx]->irs_cpus);
sc->gic_irs[idx]->irs_cfg_rid = idx;
+
+ BIT_FILL(NR_VM_IDS, &sc->gic_irs[idx]->vm_ids);
+ SLIST_INIT(&sc->gic_irs[idx]->vm_data_head);
}
static void
@@ -402,10 +476,11 @@
struct gicv5_softc *sc;
const char *name;
uint64_t icc_idr0;
- uint32_t cfgr, cr1, idr2;
+ uint32_t cfgr, cr1, idr0, idr2, idr3, idr4;
u_int istsz, lpi_id_bits, l2sz, spi_count, spi_end;
int error, iaffid;
bool two_levels;
+ int vm_id_bits;
sc = device_get_softc(dev);
name = device_get_nameunit(dev);
@@ -417,6 +492,7 @@
irs->irs_ready = false;
#endif
mtx_init(&irs->irs_lock, "GICv5 IRS lock", NULL, MTX_SPIN);
+ mtx_init(&irs->vm_lock, "GICv5 IRS VM lock", NULL, MTX_DEF);
irs->irs_cfg = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&irs->irs_cfg_rid, RF_ACTIVE);
@@ -452,6 +528,8 @@
break;
}
+ sc->has_virt = IRS_CFG_READ_4(irs, IRS_IDR0) & 0x40;
+
/* Set the control registers */
if (sc->gic_coherent) {
cr1 = IRS_CR1_VPED_WA |
@@ -502,6 +580,9 @@
}
icc_idr0 = READ_SPECIALREG(ICC_IDR0_EL1);
+ if ((icc_idr0 & ICC_IDR0_GCIE_LEGACY_MASK) != 0)
+ device_printf(dev, "Have VGICv3\n");
+
switch(icc_idr0 & ICC_IDR0_ID_BITS_MASK) {
case ICC_IDR0_ID_BITS_16:
lpi_id_bits = MIN(lpi_id_bits, 16);
@@ -512,6 +593,8 @@
break;
}
+ sc->gic_lpi_id_bits = lpi_id_bits;
+
/* The IST entries contain metadata so the size will be larger */
if ((idr2 & IRS_IDR2_ISTMD) != 0) {
uint64_t istmd_sz;
@@ -621,6 +704,215 @@
#ifdef INVARIANTS
irs->irs_ready = true;
#endif
+
+ idr0 = IRS_CFG_READ_4(irs, IRS_IDR0);
+ if (idr0 & IRS_IDR0_VIRT) {
+ idr3 = IRS_CFG_READ_4(irs, IRS_IDR3);
+ idr4 = IRS_CFG_READ_4(irs, IRS_IDR4);
+
+ if (idr3 & IRS_IDR3_VMD)
+ irs->vmd_size = 1 << ((idr4 & IRS_IDR3_VMD_SZ_MASK) >>
+ IRS_IDR3_VMD_SZ_SHIFT);
+ else
+ irs->vmd_size = 0;
+
+ vm_id_bits = (idr3 & IRS_IDR3_VM_ID_BITS_MASK)
+ >> IRS_IDR3_VM_ID_BITS_SHIFT;
+ irs->vm_id_count = MIN(1 << vm_id_bits, NR_VM_IDS);
+
+ irs->vped_size = 1 << (idr4 & IRS_IDR4_VPED_SZ_MASK);
+ irs->vpe_id_count = 2 << ((idr4 & IRS_IDR4_VPE_ID_BITS_MASK)
+ >> IRS_IDR4_VPE_ID_BITS_SHIFT);
+
+ gicv5_irs_alloc_vmt(sc, irs, irs->vm_id_count * L2_VMTE_SIZE);
+ gicv5_irs_init_vmt(sc, irs, irs->vm_id_count);
+ }
+}
+
+int
+gicv5_alloc_vm(device_t dev, int nr_vpes)
+{
+ struct gicv5_vm_data *vm_data;
+ struct gicv5_softc *sc;
+ struct gicv5_irs *irs;
+ size_t lpi_ist_align;
+ size_t spi_ist_align;
+ uintptr_t vpete_p;
+ uint64_t *vmte;
+ int i;
+
+ int spi_id_bits = 16;
+ int lpi_id_bits;
+
+ sc = device_get_softc(dev);
+ irs = sc->gic_irs[0];
+
+ if (nr_vpes > irs->vpe_id_count)
+ return (EINVAL);
+
+ lpi_id_bits = sc->gic_lpi_id_bits;
+
+ lpi_ist_align = MAX(L2_ISTE_SIZE * (1 << lpi_id_bits), 64);
+ spi_ist_align = MAX(L2_ISTE_SIZE * (1 << spi_id_bits), 64);
+
+ vm_data = malloc(sizeof(*vm_data), M_DEVBUF, M_ZERO | M_WAITOK);
+
+ vm_data->vpet = contigmalloc(VPETE_SIZE * nr_vpes,
+ M_DEVBUF, M_ZERO | M_WAITOK, 0, IRS_VMT_BASER_ADDR_LIMIT,
+ VPETE_SIZE * nr_vpes, 0);
+ vm_data->vpete = contigmalloc(irs->vped_size * nr_vpes,
+ M_DEVBUF, M_ZERO | M_WAITOK, 0, IRS_VMT_BASER_ADDR_LIMIT,
+ irs->vped_size, 0);
+ vm_data->lpi_ist = contigmalloc(L2_ISTE_SIZE * (1 << lpi_id_bits),
+ M_DEVBUF, M_ZERO | M_WAITOK, 0, IRS_VMT_BASER_ADDR_LIMIT,
+ lpi_ist_align, 0);
+ vm_data->spi_ist = contigmalloc(L2_ISTE_SIZE * (1 << spi_id_bits),
+ M_DEVBUF, M_ZERO | M_WAITOK, 0, IRS_VMT_BASER_ADDR_LIMIT,
+ spi_ist_align, 0);
+ if (irs->vmd_size != 0)
+ vm_data->vmd = contigmalloc(irs->vmd_size, M_DEVBUF,
+ M_ZERO | M_WAITOK, 0, IRS_VMT_BASER_ADDR_LIMIT,
+ irs->vmd_size, 0);
+
+ vpete_p = (uintptr_t)vm_data->vpete;
+
+ for (i = 0; i < nr_vpes; i++) {
+ vm_data->vpet[i] = (vtophys(vpete_p) & VPETE_VPED_ADDR_MASK) |
+ VPETE_VALID;
+ vpete_p += VPE_DESC_SIZE;
+ }
+
+ /* Allocate VM ID */
+ mtx_lock(&irs->vm_lock);
+
+ vm_data->vm_id = BIT_FFS(irs->vm_id_count, &irs->vm_ids) - 1;
+ if (vm_data->vm_id < 0) {
+ mtx_unlock(&irs->vm_lock);
+
+ free(vm_data->vmd, M_DEVBUF);
+ free(vm_data->spi_ist, M_DEVBUF);
+ free(vm_data->lpi_ist, M_DEVBUF);
+ free(vm_data->vpete, M_DEVBUF);
+ free(vm_data->vpet, M_DEVBUF);
+ free(vm_data, M_DEVBUF);
+
+ return (ENOMEM);
+ }
+
+ BIT_CLR(irs->vm_id_count, vm_data->vm_id, &irs->vm_ids);
+ SLIST_INSERT_HEAD(&irs->vm_data_head, vm_data, node);
+
+ mtx_unlock(&irs->vm_lock);
+
+ /* Fill in VMT entry now we have a VM ID */
+ vmte = &irs->vmt_base[vm_data->vm_id * 4];
+
+ vmte[L2_VMTE_VPE_ADDR_IDX] =
+ (vtophys(vm_data->vpet) & L2_VMTE_VPET_ADDR_MASK) |
+ ((uint64_t)ilog2_int(nr_vpes) << L2_VMTE_VPE_ID_BITS_SHIFT);
+ vmte[L2_VMTE_SPI_ADDR_IDX] =
+ (vtophys(vm_data->spi_ist) & L2_VMTE_SPI_IST_ADDR_MASK) |
+ ((uint64_t)spi_id_bits << L2_VMTE_SPI_ID_BITS_SHIFT) |
+ L2_VMTE_SPI_IST_VALID;
+ vmte[L2_VMTE_LPI_ADDR_IDX] =
+ (vtophys(vm_data->lpi_ist) & L2_VMTE_LPI_IST_ADDR_MASK) |
+ ((uint64_t)lpi_id_bits << L2_VMTE_LPI_ID_BITS_SHIFT) |
+ L2_VMTE_LPI_IST_VALID;
+ if (irs->vmd_size)
+ vmte[L2_VMTE_VMD_ADDR_IDX] =
+ (vtophys(vm_data->vmd) & L2_VMTE_VMD_ADDR_MASK) |
+ L2_VMTE_VALID;
+ else
+ vmte[L2_VMTE_VMD_ADDR_IDX] = L2_VMTE_VALID;
+
+ if (sc->gic_coherent) {
+ /* Ensure the IRS observed zeroed memory */
+ dsb(ishst);
+ } else {
+ cpu_dcache_wbinv_range(vmte, sizeof(L2_VMTE_SIZE));
+ cpu_dcache_wbinv_range(vm_data->vpet,
+ sizeof(VPETE_SIZE) * nr_vpes);
+ cpu_dcache_wbinv_range(vm_data->vpete,
+ sizeof(VPE_DESC_SIZE) * nr_vpes);
+ cpu_dcache_wbinv_range(vm_data->lpi_ist,
+ sizeof(L2_ISTE_SIZE) * (1 << lpi_id_bits));
+ cpu_dcache_wbinv_range(vm_data->spi_ist,
+ sizeof(L2_ISTE_SIZE) * (1 << spi_id_bits));
+ if (irs->vmd_size)
+ cpu_dcache_wbinv_range(vm_data->vmd, irs->vmd_size);
+ }
+
+ IRS_CFG_WRITE_8(irs, IRS_VMAP_VMR,
+ IRS_VMAP_VMR_M | (vm_data->vm_id & IRS_VMAP_VMR_VM_ID_MASK));
+
+ mtx_lock_spin(&irs->irs_lock);
+ gicv5_wait_for_op(irs, IRS_VMT_STATUSR, IRS_VMT_STATUSR_IDLE, NULL);
+ mtx_unlock_spin(&irs->irs_lock);
+
+ return (0);
+}
+
+int
+gicv5_free_vm(device_t dev, int vm_id)
+{
+ struct gicv5_vm_data *vm_data;
+ struct gicv5_softc *sc;
+ struct gicv5_irs *irs;
+
+ sc = device_get_softc(dev);
+ irs = sc->gic_irs[0];
+
+ KASSERT(vm_id < irs->vm_id_count, ("%s: VM ID out of range", __func__));
+ KASSERT(!BIT_ISSET(irs->vm_id_count, vm_id, &sc->gic_irs[0]->vm_ids),
+ ("%s: VM ID not allocated", __func__));
+
+ mtx_lock(&irs->vm_lock);
+
+ BIT_SET(irs->vm_id_count, vm_id, &sc->gic_irs[0]->vm_ids);
+
+ /* Make VM invalid */
+ IRS_CFG_WRITE_8(irs, IRS_VMAP_VMR,
+ IRS_VMAP_VMR_M | IRS_VMAP_VMR_U |
+ (vm_id & IRS_VMAP_VMR_VM_ID_MASK));
+
+ mtx_lock_spin(&irs->irs_lock);
+ gicv5_wait_for_op(irs, IRS_VMT_STATUSR, IRS_VMT_STATUSR_IDLE, NULL);
+ mtx_unlock_spin(&irs->irs_lock);
+
+ SLIST_FOREACH(vm_data, &irs->vm_data_head, node) {
+ if (vm_data->vm_id == vm_id) {
+ SLIST_REMOVE(&irs->vm_data_head, vm_data,
+ gicv5_vm_data, node);
+
+ free(vm_data->vpet, M_DEVBUF);
+ free(vm_data->vpete, M_DEVBUF);
+ free(vm_data->lpi_ist, M_DEVBUF);
+ free(vm_data->spi_ist, M_DEVBUF);
+ free(vm_data->vmd, M_DEVBUF);
+
+ free(vm_data, M_DEVBUF);
+
+ mtx_unlock(&irs->vm_lock);
+
+ return (0);
+ }
+ }
+
+ mtx_unlock(&irs->vm_lock);
+
+ return (ENOENT);
+}
+
+int
+gicv5_get_max_vpe_count(device_t dev)
+{
+ struct gicv5_softc *sc;
+ struct gicv5_irs *irs;
+
+ sc = device_get_softc(dev);
+ irs = sc->gic_irs[0];
+
+ return (irs->vpe_id_count);
}
void
diff --git a/sys/arm64/arm64/gicv5var.h b/sys/arm64/arm64/gicv5var.h
--- a/sys/arm64/arm64/gicv5var.h
+++ b/sys/arm64/arm64/gicv5var.h
@@ -50,6 +50,9 @@
u_int gic_nchildren;
/* Number of LPIs, including IPIs */
u_int gic_nlpis;
+ u_int gic_lpi_id_bits;
+
+ bool has_virt;
};
struct gicv5_devinfo {
@@ -70,4 +73,9 @@
void gicv5_attach_iwb(device_t, device_t);
+int gicv5_alloc_vm(device_t, int);
+int gicv5_free_vm(device_t, int);
+
+int gicv5_get_max_vpe_count(device_t);
+
#endif /* _ARM64_GICV5VAR_H_ */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Sep 30, 12:41 PM (8 m, 59 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39945300
Default Alt Text
D60113.id187977.diff (10 KB)
Attached To
Mode
D60113: arm64: gicv5: Add GICv5 virtual machine support
Attached
Detach File
Event Timeline
Log In to Comment