Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,163 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ARM Generic Interrupt Controller (GIC) support
*/
#include <errno.h>
#include <linux/bits.h>
#include <linux/sizes.h>
#include "kvm_util.h"
#include <gic.h>
#include "gic_private.h"
#include "processor.h"
#include "spinlock.h"
static const struct gic_common_ops *gic_common_ops;
static struct spinlock gic_lock;
static void gic_cpu_init(unsigned int cpu)
{
gic_common_ops->gic_cpu_init(cpu);
}
static void gic_dist_init(enum gic_type type, unsigned int nr_cpus)
{
const struct gic_common_ops *gic_ops = NULL;
spin_lock(&gic_lock);
/* Distributor initialization is needed only once per VM */
if (gic_common_ops) {
spin_unlock(&gic_lock);
return;
}
if (type == GIC_V3)
gic_ops = &gicv3_ops;
GUEST_ASSERT(gic_ops);
gic_ops->gic_init(nr_cpus);
gic_common_ops = gic_ops;
/* Make sure that the initialized data is visible to all the vCPUs */
dsb(sy);
spin_unlock(&gic_lock);
}
void gic_init(enum gic_type type, unsigned int nr_cpus)
{
u32 cpu = guest_get_vcpuid();
GUEST_ASSERT(type < GIC_TYPE_MAX);
GUEST_ASSERT(nr_cpus);
gic_dist_init(type, nr_cpus);
gic_cpu_init(cpu);
}
void gic_irq_enable(unsigned int intid)
{
GUEST_ASSERT(gic_common_ops);
gic_common_ops->gic_irq_enable(intid);
}
void gic_irq_disable(unsigned int intid)
{
GUEST_ASSERT(gic_common_ops);
gic_common_ops->gic_irq_disable(intid);
}
unsigned int gic_get_and_ack_irq(void)
{
u64 irqstat;
unsigned int intid;
GUEST_ASSERT(gic_common_ops);
irqstat = gic_common_ops->gic_read_iar();
intid = irqstat & GENMASK(23, 0);
return intid;
}
void gic_set_eoi(unsigned int intid)
{
GUEST_ASSERT(gic_common_ops);
gic_common_ops->gic_write_eoir(intid);
}
void gic_set_dir(unsigned int intid)
{
GUEST_ASSERT(gic_common_ops);
gic_common_ops->gic_write_dir(intid);
}
void gic_set_eoi_split(bool split)
{
GUEST_ASSERT(gic_common_ops);
gic_common_ops->gic_set_eoi_split(split);
}
void gic_set_priority_mask(u64 pmr)
{
GUEST_ASSERT(gic_common_ops);
gic_common_ops->gic_set_priority_mask(pmr);
}
void gic_set_priority(unsigned int intid, unsigned int prio)
{
GUEST_ASSERT(gic_common_ops);
gic_common_ops->gic_set_priority(intid, prio);
}
void gic_irq_set_active(unsigned int intid)
{
GUEST_ASSERT(gic_common_ops);
gic_common_ops->gic_irq_set_active(intid);
}
void gic_irq_clear_active(unsigned int intid)
{
GUEST_ASSERT(gic_common_ops);
gic_common_ops->gic_irq_clear_active(intid);
}
bool gic_irq_get_active(unsigned int intid)
{
GUEST_ASSERT(gic_common_ops);
return gic_common_ops->gic_irq_get_active(intid);
}
void gic_irq_set_pending(unsigned int intid)
{
GUEST_ASSERT(gic_common_ops);
gic_common_ops->gic_irq_set_pending(intid);
}
void gic_irq_clear_pending(unsigned int intid)
{
GUEST_ASSERT(gic_common_ops);
gic_common_ops->gic_irq_clear_pending(intid);
}
bool gic_irq_get_pending(unsigned int intid)
{
GUEST_ASSERT(gic_common_ops);
return gic_common_ops->gic_irq_get_pending(intid);
}
void gic_irq_set_config(unsigned int intid, bool is_edge)
{
GUEST_ASSERT(gic_common_ops);
gic_common_ops->gic_irq_set_config(intid, is_edge);
}
void gic_irq_set_group(unsigned int intid, bool group)
{
GUEST_ASSERT(gic_common_ops);
gic_common_ops->gic_irq_set_group(intid, group);
}
@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* ARM Generic Interrupt Controller (GIC) private defines that's only
* shared among the GIC library code.
*/
#ifndef SELFTEST_KVM_GIC_PRIVATE_H
#define SELFTEST_KVM_GIC_PRIVATE_H
struct gic_common_ops {
void (*gic_init)(unsigned int nr_cpus);
void (*gic_cpu_init)(unsigned int cpu);
void (*gic_irq_enable)(unsigned int intid);
void (*gic_irq_disable)(unsigned int intid);
u64 (*gic_read_iar)(void);
void (*gic_write_eoir)(u32 irq);
void (*gic_write_dir)(u32 irq);
void (*gic_set_eoi_split)(bool split);
void (*gic_set_priority_mask)(u64 mask);
void (*gic_set_priority)(u32 intid, u32 prio);
void (*gic_irq_set_active)(u32 intid);
void (*gic_irq_clear_active)(u32 intid);
bool (*gic_irq_get_active)(u32 intid);
void (*gic_irq_set_pending)(u32 intid);
void (*gic_irq_clear_pending)(u32 intid);
bool (*gic_irq_get_pending)(u32 intid);
void (*gic_irq_set_config)(u32 intid, bool is_edge);
void (*gic_irq_set_group)(u32 intid, bool group);
};
extern const struct gic_common_ops gicv3_ops;
#endif /* SELFTEST_KVM_GIC_PRIVATE_H */
@@ -0,0 +1,449 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ARM Generic Interrupt Controller (GIC) v3 support
*/
#include <linux/sizes.h>
#include "kvm_util.h"
#include "processor.h"
#include "delay.h"
#include "gic.h"
#include "gic_v3.h"
#include "gic_private.h"
#define GICV3_MAX_CPUS 512
#define GICD_INT_DEF_PRI 0xa0
#define GICD_INT_DEF_PRI_X4 ((GICD_INT_DEF_PRI << 24) |\
(GICD_INT_DEF_PRI << 16) |\
(GICD_INT_DEF_PRI << 8) |\
GICD_INT_DEF_PRI)
#define ICC_PMR_DEF_PRIO 0xf0
struct gicv3_data {
unsigned int nr_cpus;
unsigned int nr_spis;
};
#define sgi_base_from_redist(redist_base) (redist_base + SZ_64K)
#define DIST_BIT (1U << 31)
enum gicv3_intid_range {
SGI_RANGE,
PPI_RANGE,
SPI_RANGE,
INVALID_RANGE,
};
static struct gicv3_data gicv3_data;
static void gicv3_gicd_wait_for_rwp(void)
{
unsigned int count = 100000; /* 1s */
while (readl(GICD_BASE_GVA + GICD_CTLR) & GICD_CTLR_RWP) {
GUEST_ASSERT(count--);
udelay(10);
}
}
static inline volatile void *gicr_base_cpu(u32 cpu)
{
/* Align all the redistributors sequentially */
return GICR_BASE_GVA + cpu * SZ_64K * 2;
}
static void gicv3_gicr_wait_for_rwp(u32 cpu)
{
unsigned int count = 100000; /* 1s */
while (readl(gicr_base_cpu(cpu) + GICR_CTLR) & GICR_CTLR_RWP) {
GUEST_ASSERT(count--);
udelay(10);
}
}
static void gicv3_wait_for_rwp(u32 cpu_or_dist)
{
if (cpu_or_dist & DIST_BIT)
gicv3_gicd_wait_for_rwp();
else
gicv3_gicr_wait_for_rwp(cpu_or_dist);
}
static enum gicv3_intid_range get_intid_range(unsigned int intid)
{
switch (intid) {
case 0 ... 15:
return SGI_RANGE;
case 16 ... 31:
return PPI_RANGE;
case 32 ... 1019:
return SPI_RANGE;
}
/* We should not be reaching here */
GUEST_ASSERT(0);
return INVALID_RANGE;
}
static u64 gicv3_read_iar(void)
{
u64 irqstat = read_sysreg_s(SYS_ICC_IAR1_EL1);
dsb(sy);
return irqstat;
}
static void gicv3_write_eoir(u32 irq)
{
write_sysreg_s(irq, SYS_ICC_EOIR1_EL1);
isb();
}
static void gicv3_write_dir(u32 irq)
{
write_sysreg_s(irq, SYS_ICC_DIR_EL1);
isb();
}
static void gicv3_set_priority_mask(u64 mask)
{
write_sysreg_s(mask, SYS_ICC_PMR_EL1);
}
static void gicv3_set_eoi_split(bool split)
{
u32 val;
/*
* All other fields are read-only, so no need to read CTLR first. In
* fact, the kernel does the same.
*/
val = split ? (1U << 1) : 0;
write_sysreg_s(val, SYS_ICC_CTLR_EL1);
isb();
}
u32 gicv3_reg_readl(u32 cpu_or_dist, u64 offset)
{
volatile void *base = cpu_or_dist & DIST_BIT ? GICD_BASE_GVA
: sgi_base_from_redist(gicr_base_cpu(cpu_or_dist));
return readl(base + offset);
}
void gicv3_reg_writel(u32 cpu_or_dist, u64 offset, u32 reg_val)
{
volatile void *base = cpu_or_dist & DIST_BIT ? GICD_BASE_GVA
: sgi_base_from_redist(gicr_base_cpu(cpu_or_dist));
writel(reg_val, base + offset);
}
u32 gicv3_getl_fields(u32 cpu_or_dist, u64 offset, u32 mask)
{
return gicv3_reg_readl(cpu_or_dist, offset) & mask;
}
void gicv3_setl_fields(u32 cpu_or_dist, u64 offset,
u32 mask, u32 reg_val)
{
u32 tmp = gicv3_reg_readl(cpu_or_dist, offset) & ~mask;
tmp |= (reg_val & mask);
gicv3_reg_writel(cpu_or_dist, offset, tmp);
}
/*
* We use a single offset for the distributor and redistributor maps as they
* have the same value in both. The only exceptions are registers that only
* exist in one and not the other, like GICR_WAKER that doesn't exist in the
* distributor map. Such registers are conveniently marked as reserved in the
* map that doesn't implement it; like GICR_WAKER's offset of 0x0014 being
* marked as "Reserved" in the Distributor map.
*/
static void gicv3_access_reg(u32 intid, u64 offset,
u32 reg_bits, u32 bits_per_field,
bool write, u32 *val)
{
u32 cpu = guest_get_vcpuid();
enum gicv3_intid_range intid_range = get_intid_range(intid);
u32 fields_per_reg, index, mask, shift;
u32 cpu_or_dist;
GUEST_ASSERT(bits_per_field <= reg_bits);
GUEST_ASSERT(!write || *val < (1U << bits_per_field));
/*
* This function does not support 64 bit accesses. Just asserting here
* until we implement readq/writeq.
*/
GUEST_ASSERT(reg_bits == 32);
fields_per_reg = reg_bits / bits_per_field;
index = intid % fields_per_reg;
shift = index * bits_per_field;
mask = ((1U << bits_per_field) - 1) << shift;
/* Set offset to the actual register holding intid's config. */
offset += (intid / fields_per_reg) * (reg_bits / 8);
cpu_or_dist = (intid_range == SPI_RANGE) ? DIST_BIT : cpu;
if (write)
gicv3_setl_fields(cpu_or_dist, offset, mask, *val << shift);
*val = gicv3_getl_fields(cpu_or_dist, offset, mask) >> shift;
}
static void gicv3_write_reg(u32 intid, u64 offset,
u32 reg_bits, u32 bits_per_field, u32 val)
{
gicv3_access_reg(intid, offset, reg_bits,
bits_per_field, true, &val);
}
static u32 gicv3_read_reg(u32 intid, u64 offset,
u32 reg_bits, u32 bits_per_field)
{
u32 val;
gicv3_access_reg(intid, offset, reg_bits,
bits_per_field, false, &val);
return val;
}
static void gicv3_set_priority(u32 intid, u32 prio)
{
gicv3_write_reg(intid, GICD_IPRIORITYR, 32, 8, prio);
}
/* Sets the intid to be level-sensitive or edge-triggered. */
static void gicv3_irq_set_config(u32 intid, bool is_edge)
{
u32 val;
/* N/A for private interrupts. */
GUEST_ASSERT(get_intid_range(intid) == SPI_RANGE);
val = is_edge ? 2 : 0;
gicv3_write_reg(intid, GICD_ICFGR, 32, 2, val);
}
static void gicv3_irq_enable(u32 intid)
{
bool is_spi = get_intid_range(intid) == SPI_RANGE;
u32 cpu = guest_get_vcpuid();
gicv3_write_reg(intid, GICD_ISENABLER, 32, 1, 1);
gicv3_wait_for_rwp(is_spi ? DIST_BIT : cpu);
}
static void gicv3_irq_disable(u32 intid)
{
bool is_spi = get_intid_range(intid) == SPI_RANGE;
u32 cpu = guest_get_vcpuid();
gicv3_write_reg(intid, GICD_ICENABLER, 32, 1, 1);
gicv3_wait_for_rwp(is_spi ? DIST_BIT : cpu);
}
static void gicv3_irq_set_active(u32 intid)
{
gicv3_write_reg(intid, GICD_ISACTIVER, 32, 1, 1);
}
static void gicv3_irq_clear_active(u32 intid)
{
gicv3_write_reg(intid, GICD_ICACTIVER, 32, 1, 1);
}
static bool gicv3_irq_get_active(u32 intid)
{
return gicv3_read_reg(intid, GICD_ISACTIVER, 32, 1);
}
static void gicv3_irq_set_pending(u32 intid)
{
gicv3_write_reg(intid, GICD_ISPENDR, 32, 1, 1);
}
static void gicv3_irq_clear_pending(u32 intid)
{
gicv3_write_reg(intid, GICD_ICPENDR, 32, 1, 1);
}
static bool gicv3_irq_get_pending(u32 intid)
{
return gicv3_read_reg(intid, GICD_ISPENDR, 32, 1);
}
static void gicv3_enable_redist(volatile void *redist_base)
{
u32 val = readl(redist_base + GICR_WAKER);
unsigned int count = 100000; /* 1s */
val &= ~GICR_WAKER_ProcessorSleep;
writel(val, redist_base + GICR_WAKER);
/* Wait until the processor is 'active' */
while (readl(redist_base + GICR_WAKER) & GICR_WAKER_ChildrenAsleep) {
GUEST_ASSERT(count--);
udelay(10);
}
}
static void gicv3_set_group(u32 intid, bool grp)
{
u32 cpu_or_dist;
u32 val;
cpu_or_dist = (get_intid_range(intid) == SPI_RANGE) ? DIST_BIT : guest_get_vcpuid();
val = gicv3_reg_readl(cpu_or_dist, GICD_IGROUPR + (intid / 32) * 4);
if (grp)
val |= BIT(intid % 32);
else
val &= ~BIT(intid % 32);
gicv3_reg_writel(cpu_or_dist, GICD_IGROUPR + (intid / 32) * 4, val);
}
static void gicv3_cpu_init(unsigned int cpu)
{
volatile void *sgi_base;
unsigned int i;
volatile void *redist_base_cpu;
u64 typer;
GUEST_ASSERT(cpu < gicv3_data.nr_cpus);
redist_base_cpu = gicr_base_cpu(cpu);
sgi_base = sgi_base_from_redist(redist_base_cpu);
/* Verify assumption that GICR_TYPER.Processor_number == cpu */
typer = readq_relaxed(redist_base_cpu + GICR_TYPER);
GUEST_ASSERT_EQ(GICR_TYPER_CPU_NUMBER(typer), cpu);
gicv3_enable_redist(redist_base_cpu);
/*
* Mark all the SGI and PPI interrupts as non-secure Group-1.
* Also, deactivate and disable them.
*/
writel(~0, sgi_base + GICR_IGROUPR0);
writel(~0, sgi_base + GICR_ICACTIVER0);
writel(~0, sgi_base + GICR_ICENABLER0);
/* Set a default priority for all the SGIs and PPIs */
for (i = 0; i < 32; i += 4)
writel(GICD_INT_DEF_PRI_X4,
sgi_base + GICR_IPRIORITYR0 + i);
gicv3_gicr_wait_for_rwp(cpu);
/* Enable the GIC system register (ICC_*) access */
write_sysreg_s(read_sysreg_s(SYS_ICC_SRE_EL1) | ICC_SRE_EL1_SRE,
SYS_ICC_SRE_EL1);
/* Set a default priority threshold */
write_sysreg_s(ICC_PMR_DEF_PRIO, SYS_ICC_PMR_EL1);
/* Disable Group-0 interrupts */
write_sysreg_s(ICC_IGRPEN0_EL1_MASK, SYS_ICC_IGRPEN1_EL1);
/* Enable non-secure Group-1 interrupts */
write_sysreg_s(ICC_IGRPEN1_EL1_MASK, SYS_ICC_IGRPEN1_EL1);
}
static void gicv3_dist_init(void)
{
unsigned int i;
/* Disable the distributor until we set things up */
writel(0, GICD_BASE_GVA + GICD_CTLR);
gicv3_gicd_wait_for_rwp();
/*
* Mark all the SPI interrupts as non-secure Group-1.
* Also, deactivate and disable them.
*/
for (i = 32; i < gicv3_data.nr_spis; i += 32) {
writel(~0, GICD_BASE_GVA + GICD_IGROUPR + i / 8);
writel(~0, GICD_BASE_GVA + GICD_ICACTIVER + i / 8);
writel(~0, GICD_BASE_GVA + GICD_ICENABLER + i / 8);
}
/* Set a default priority for all the SPIs */
for (i = 32; i < gicv3_data.nr_spis; i += 4)
writel(GICD_INT_DEF_PRI_X4,
GICD_BASE_GVA + GICD_IPRIORITYR + i);
/* Wait for the settings to sync-in */
gicv3_gicd_wait_for_rwp();
/* Finally, enable the distributor globally with ARE */
writel(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A |
GICD_CTLR_ENABLE_G1, GICD_BASE_GVA + GICD_CTLR);
gicv3_gicd_wait_for_rwp();
}
static void gicv3_init(unsigned int nr_cpus)
{
GUEST_ASSERT(nr_cpus <= GICV3_MAX_CPUS);
gicv3_data.nr_cpus = nr_cpus;
gicv3_data.nr_spis = GICD_TYPER_SPIS(
readl(GICD_BASE_GVA + GICD_TYPER));
if (gicv3_data.nr_spis > 1020)
gicv3_data.nr_spis = 1020;
/*
* Initialize only the distributor for now.
* The redistributor and CPU interfaces are initialized
* later for every PE.
*/
gicv3_dist_init();
}
const struct gic_common_ops gicv3_ops = {
.gic_init = gicv3_init,
.gic_cpu_init = gicv3_cpu_init,
.gic_irq_enable = gicv3_irq_enable,
.gic_irq_disable = gicv3_irq_disable,
.gic_read_iar = gicv3_read_iar,
.gic_write_eoir = gicv3_write_eoir,
.gic_write_dir = gicv3_write_dir,
.gic_set_priority_mask = gicv3_set_priority_mask,
.gic_set_eoi_split = gicv3_set_eoi_split,
.gic_set_priority = gicv3_set_priority,
.gic_irq_set_active = gicv3_irq_set_active,
.gic_irq_clear_active = gicv3_irq_clear_active,
.gic_irq_get_active = gicv3_irq_get_active,
.gic_irq_set_pending = gicv3_irq_set_pending,
.gic_irq_clear_pending = gicv3_irq_clear_pending,
.gic_irq_get_pending = gicv3_irq_get_pending,
.gic_irq_set_config = gicv3_irq_set_config,
.gic_irq_set_group = gicv3_set_group,
};
void gic_rdist_enable_lpis(gpa_t cfg_table, size_t cfg_table_size,
gpa_t pend_table)
{
volatile void *rdist_base = gicr_base_cpu(guest_get_vcpuid());
u32 ctlr;
u64 val;
val = (cfg_table |
GICR_PROPBASER_InnerShareable |
GICR_PROPBASER_RaWaWb |
((ilog2(cfg_table_size) - 1) & GICR_PROPBASER_IDBITS_MASK));
writeq_relaxed(val, rdist_base + GICR_PROPBASER);
val = (pend_table |
GICR_PENDBASER_InnerShareable |
GICR_PENDBASER_RaWaWb);
writeq_relaxed(val, rdist_base + GICR_PENDBASER);
ctlr = readl_relaxed(rdist_base + GICR_CTLR);
ctlr |= GICR_CTLR_ENABLE_LPIS;
writel_relaxed(ctlr, rdist_base + GICR_CTLR);
}
@@ -0,0 +1,264 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Guest ITS library, generously donated by drivers/irqchip/irq-gic-v3-its.c
* over in the kernel tree.
*/
#include <linux/kvm.h>
#include <linux/sizes.h>
#include <asm/kvm_para.h>
#include <asm/kvm.h>
#include "kvm_util.h"
#include "vgic.h"
#include "gic.h"
#include "gic_v3.h"
#include "processor.h"
#define GITS_COLLECTION_TARGET_SHIFT 16
static u64 its_read_u64(unsigned long offset)
{
return readq_relaxed(GITS_BASE_GVA + offset);
}
static void its_write_u64(unsigned long offset, u64 val)
{
writeq_relaxed(val, GITS_BASE_GVA + offset);
}
static u32 its_read_u32(unsigned long offset)
{
return readl_relaxed(GITS_BASE_GVA + offset);
}
static void its_write_u32(unsigned long offset, u32 val)
{
writel_relaxed(val, GITS_BASE_GVA + offset);
}
static unsigned long its_find_baser(unsigned int type)
{
int i;
for (i = 0; i < GITS_BASER_NR_REGS; i++) {
u64 baser;
unsigned long offset = GITS_BASER + (i * sizeof(baser));
baser = its_read_u64(offset);
if (GITS_BASER_TYPE(baser) == type)
return offset;
}
GUEST_FAIL("Couldn't find an ITS BASER of type %u", type);
return -1;
}
static void its_install_table(unsigned int type, gpa_t base, size_t size)
{
unsigned long offset = its_find_baser(type);
u64 baser;
baser = ((size / SZ_64K) - 1) |
GITS_BASER_PAGE_SIZE_64K |
GITS_BASER_InnerShareable |
base |
GITS_BASER_RaWaWb |
GITS_BASER_VALID;
its_write_u64(offset, baser);
}
static void its_install_cmdq(gpa_t base, size_t size)
{
u64 cbaser;
cbaser = ((size / SZ_4K) - 1) |
GITS_CBASER_InnerShareable |
base |
GITS_CBASER_RaWaWb |
GITS_CBASER_VALID;
its_write_u64(GITS_CBASER, cbaser);
}
void its_init(gpa_t coll_tbl, size_t coll_tbl_sz, gpa_t device_tbl,
size_t device_tbl_sz, gpa_t cmdq, size_t cmdq_size)
{
u32 ctlr;
its_install_table(GITS_BASER_TYPE_COLLECTION, coll_tbl, coll_tbl_sz);
its_install_table(GITS_BASER_TYPE_DEVICE, device_tbl, device_tbl_sz);
its_install_cmdq(cmdq, cmdq_size);
ctlr = its_read_u32(GITS_CTLR);
ctlr |= GITS_CTLR_ENABLE;
its_write_u32(GITS_CTLR, ctlr);
}
struct its_cmd_block {
union {
u64 raw_cmd[4];
__le64 raw_cmd_le[4];
};
};
static inline void its_fixup_cmd(struct its_cmd_block *cmd)
{
/* Let's fixup BE commands */
cmd->raw_cmd_le[0] = cpu_to_le64(cmd->raw_cmd[0]);
cmd->raw_cmd_le[1] = cpu_to_le64(cmd->raw_cmd[1]);
cmd->raw_cmd_le[2] = cpu_to_le64(cmd->raw_cmd[2]);
cmd->raw_cmd_le[3] = cpu_to_le64(cmd->raw_cmd[3]);
}
static void its_mask_encode(u64 *raw_cmd, u64 val, int h, int l)
{
u64 mask = GENMASK_ULL(h, l);
*raw_cmd &= ~mask;
*raw_cmd |= (val << l) & mask;
}
static void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr)
{
its_mask_encode(&cmd->raw_cmd[0], cmd_nr, 7, 0);
}
static void its_encode_devid(struct its_cmd_block *cmd, u32 devid)
{
its_mask_encode(&cmd->raw_cmd[0], devid, 63, 32);
}
static void its_encode_event_id(struct its_cmd_block *cmd, u32 id)
{
its_mask_encode(&cmd->raw_cmd[1], id, 31, 0);
}
static void its_encode_phys_id(struct its_cmd_block *cmd, u32 phys_id)
{
its_mask_encode(&cmd->raw_cmd[1], phys_id, 63, 32);
}
static void its_encode_size(struct its_cmd_block *cmd, u8 size)
{
its_mask_encode(&cmd->raw_cmd[1], size, 4, 0);
}
static void its_encode_itt(struct its_cmd_block *cmd, u64 itt_addr)
{
its_mask_encode(&cmd->raw_cmd[2], itt_addr >> 8, 51, 8);
}
static void its_encode_valid(struct its_cmd_block *cmd, int valid)
{
its_mask_encode(&cmd->raw_cmd[2], !!valid, 63, 63);
}
static void its_encode_target(struct its_cmd_block *cmd, u64 target_addr)
{
its_mask_encode(&cmd->raw_cmd[2], target_addr >> 16, 51, 16);
}
static void its_encode_collection(struct its_cmd_block *cmd, u16 col)
{
its_mask_encode(&cmd->raw_cmd[2], col, 15, 0);
}
static u64 procnum_to_rdbase(u32 vcpu_id)
{
return vcpu_id << GITS_COLLECTION_TARGET_SHIFT;
}
#define GITS_CMDQ_POLL_ITERATIONS 0
static void its_send_cmd(void *cmdq_base, struct its_cmd_block *cmd)
{
u64 cwriter = its_read_u64(GITS_CWRITER);
struct its_cmd_block *dst = cmdq_base + cwriter;
u64 cbaser = its_read_u64(GITS_CBASER);
size_t cmdq_size;
u64 next;
int i;
cmdq_size = ((cbaser & 0xFF) + 1) * SZ_4K;
its_fixup_cmd(cmd);
WRITE_ONCE(*dst, *cmd);
dsb(ishst);
next = (cwriter + sizeof(*cmd)) % cmdq_size;
its_write_u64(GITS_CWRITER, next);
/*
* Polling isn't necessary considering KVM's ITS emulation at the time
* of writing this, as the CMDQ is processed synchronously after a write
* to CWRITER.
*/
for (i = 0; its_read_u64(GITS_CREADR) != next; i++) {
__GUEST_ASSERT(i < GITS_CMDQ_POLL_ITERATIONS,
"ITS didn't process command at offset %lu after %d iterations\n",
cwriter, i);
cpu_relax();
}
}
void its_send_mapd_cmd(void *cmdq_base, u32 device_id, gpa_t itt_base,
size_t itt_size, bool valid)
{
struct its_cmd_block cmd = {};
its_encode_cmd(&cmd, GITS_CMD_MAPD);
its_encode_devid(&cmd, device_id);
its_encode_size(&cmd, ilog2(itt_size) - 1);
its_encode_itt(&cmd, itt_base);
its_encode_valid(&cmd, valid);
its_send_cmd(cmdq_base, &cmd);
}
void its_send_mapc_cmd(void *cmdq_base, u32 vcpu_id, u32 collection_id, bool valid)
{
struct its_cmd_block cmd = {};
its_encode_cmd(&cmd, GITS_CMD_MAPC);
its_encode_collection(&cmd, collection_id);
its_encode_target(&cmd, procnum_to_rdbase(vcpu_id));
its_encode_valid(&cmd, valid);
its_send_cmd(cmdq_base, &cmd);
}
void its_send_mapti_cmd(void *cmdq_base, u32 device_id, u32 event_id,
u32 collection_id, u32 intid)
{
struct its_cmd_block cmd = {};
its_encode_cmd(&cmd, GITS_CMD_MAPTI);
its_encode_devid(&cmd, device_id);
its_encode_event_id(&cmd, event_id);
its_encode_phys_id(&cmd, intid);
its_encode_collection(&cmd, collection_id);
its_send_cmd(cmdq_base, &cmd);
}
void its_send_invall_cmd(void *cmdq_base, u32 collection_id)
{
struct its_cmd_block cmd = {};
its_encode_cmd(&cmd, GITS_CMD_INVALL);
its_encode_collection(&cmd, collection_id);
its_send_cmd(cmdq_base, &cmd);
}
void its_send_sync_cmd(void *cmdq_base, u32 vcpu_id)
{
struct its_cmd_block cmd = {};
its_encode_cmd(&cmd, GITS_CMD_SYNC);
its_encode_target(&cmd, procnum_to_rdbase(vcpu_id));
its_send_cmd(cmdq_base, &cmd);
}
@@ -0,0 +1,126 @@
/* SPDX-License-Identifier: GPL-2.0 */
.macro save_registers
add sp, sp, #-16 * 17
stp x0, x1, [sp, #16 * 0]
stp x2, x3, [sp, #16 * 1]
stp x4, x5, [sp, #16 * 2]
stp x6, x7, [sp, #16 * 3]
stp x8, x9, [sp, #16 * 4]
stp x10, x11, [sp, #16 * 5]
stp x12, x13, [sp, #16 * 6]
stp x14, x15, [sp, #16 * 7]
stp x16, x17, [sp, #16 * 8]
stp x18, x19, [sp, #16 * 9]
stp x20, x21, [sp, #16 * 10]
stp x22, x23, [sp, #16 * 11]
stp x24, x25, [sp, #16 * 12]
stp x26, x27, [sp, #16 * 13]
stp x28, x29, [sp, #16 * 14]
/*
* This stores sp_el1 into ex_regs.sp so exception handlers can "look"
* at it. It will _not_ be used to restore the sp on return from the
* exception so handlers can not update it.
*/
add x1, sp, #16 * 17
stp x30, x1, [sp, #16 * 15] /* x30, SP */
mrs x1, elr_el1
mrs x2, spsr_el1
stp x1, x2, [sp, #16 * 16] /* PC, PSTATE */
.endm
.macro restore_registers
ldp x1, x2, [sp, #16 * 16] /* PC, PSTATE */
msr elr_el1, x1
msr spsr_el1, x2
/* sp is not restored */
ldp x30, xzr, [sp, #16 * 15] /* x30, SP */
ldp x28, x29, [sp, #16 * 14]
ldp x26, x27, [sp, #16 * 13]
ldp x24, x25, [sp, #16 * 12]
ldp x22, x23, [sp, #16 * 11]
ldp x20, x21, [sp, #16 * 10]
ldp x18, x19, [sp, #16 * 9]
ldp x16, x17, [sp, #16 * 8]
ldp x14, x15, [sp, #16 * 7]
ldp x12, x13, [sp, #16 * 6]
ldp x10, x11, [sp, #16 * 5]
ldp x8, x9, [sp, #16 * 4]
ldp x6, x7, [sp, #16 * 3]
ldp x4, x5, [sp, #16 * 2]
ldp x2, x3, [sp, #16 * 1]
ldp x0, x1, [sp, #16 * 0]
add sp, sp, #16 * 17
eret
.endm
.pushsection ".entry.text", "ax"
.balign 0x800
.global vectors
vectors:
.popsection
.set vector, 0
/*
* Build an exception handler for vector and append a jump to it into
* vectors (while making sure that it's 0x80 aligned).
*/
.macro HANDLER, label
handler_\label:
save_registers
mov x0, sp
mov x1, #vector
bl route_exception
restore_registers
.pushsection ".entry.text", "ax"
.balign 0x80
b handler_\label
.popsection
.set vector, vector + 1
.endm
.macro HANDLER_INVALID
.pushsection ".entry.text", "ax"
.balign 0x80
/* This will abort so no need to save and restore registers. */
mov x0, #vector
mov x1, #0 /* ec */
mov x2, #0 /* valid_ec */
b kvm_exit_unexpected_exception
.popsection
.set vector, vector + 1
.endm
/*
* Caution: be sure to not add anything between the declaration of vectors
* above and these macro calls that will build the vectors table below it.
*/
HANDLER_INVALID // Synchronous EL1t
HANDLER_INVALID // IRQ EL1t
HANDLER_INVALID // FIQ EL1t
HANDLER_INVALID // Error EL1t
HANDLER el1h_sync // Synchronous EL1h
HANDLER el1h_irq // IRQ EL1h
HANDLER el1h_fiq // FIQ EL1h
HANDLER el1h_error // Error EL1h
HANDLER el0_sync_64 // Synchronous 64-bit EL0
HANDLER el0_irq_64 // IRQ 64-bit EL0
HANDLER el0_fiq_64 // FIQ 64-bit EL0
HANDLER el0_error_64 // Error 64-bit EL0
HANDLER el0_sync_32 // Synchronous 32-bit EL0
HANDLER el0_irq_32 // IRQ 32-bit EL0
HANDLER el0_fiq_32 // FIQ 32-bit EL0
HANDLER el0_error_32 // Error 32-bit EL0
@@ -0,0 +1,728 @@
// SPDX-License-Identifier: GPL-2.0
/*
* AArch64 code
*
* Copyright (C) 2018, Red Hat, Inc.
*/
#include <linux/compiler.h>
#include <assert.h>
#include "guest_modes.h"
#include "kvm_util.h"
#include "processor.h"
#include "ucall_common.h"
#include "vgic.h"
#include <linux/bitfield.h>
#include <linux/sizes.h>
#define DEFAULT_ARM64_GUEST_STACK_VADDR_MIN 0xac0000
static gva_t exception_handlers;
static u64 pgd_index(struct kvm_vm *vm, gva_t gva)
{
unsigned int shift = (vm->mmu.pgtable_levels - 1) * (vm->page_shift - 3) + vm->page_shift;
u64 mask = (1UL << (vm->va_bits - shift)) - 1;
return (gva >> shift) & mask;
}
static u64 pud_index(struct kvm_vm *vm, gva_t gva)
{
unsigned int shift = 2 * (vm->page_shift - 3) + vm->page_shift;
u64 mask = (1UL << (vm->page_shift - 3)) - 1;
TEST_ASSERT(vm->mmu.pgtable_levels == 4,
"Mode %d does not have 4 page table levels", vm->mode);
return (gva >> shift) & mask;
}
static u64 pmd_index(struct kvm_vm *vm, gva_t gva)
{
unsigned int shift = (vm->page_shift - 3) + vm->page_shift;
u64 mask = (1UL << (vm->page_shift - 3)) - 1;
TEST_ASSERT(vm->mmu.pgtable_levels >= 3,
"Mode %d does not have >= 3 page table levels", vm->mode);
return (gva >> shift) & mask;
}
static u64 pte_index(struct kvm_vm *vm, gva_t gva)
{
u64 mask = (1UL << (vm->page_shift - 3)) - 1;
return (gva >> vm->page_shift) & mask;
}
static inline bool use_lpa2_pte_format(struct kvm_vm *vm)
{
return (vm->page_size == SZ_4K || vm->page_size == SZ_16K) &&
(vm->pa_bits > 48 || vm->va_bits > 48);
}
static u64 addr_pte(struct kvm_vm *vm, u64 pa, u64 attrs)
{
u64 pte;
if (use_lpa2_pte_format(vm)) {
pte = pa & PTE_ADDR_MASK_LPA2(vm->page_shift);
pte |= FIELD_GET(GENMASK(51, 50), pa) << PTE_ADDR_51_50_LPA2_SHIFT;
attrs &= ~PTE_ADDR_51_50_LPA2;
} else {
pte = pa & PTE_ADDR_MASK(vm->page_shift);
if (vm->page_shift == 16)
pte |= FIELD_GET(GENMASK(51, 48), pa) << PTE_ADDR_51_48_SHIFT;
}
pte |= attrs;
return pte;
}
static u64 pte_addr(struct kvm_vm *vm, u64 pte)
{
u64 pa;
if (use_lpa2_pte_format(vm)) {
pa = pte & PTE_ADDR_MASK_LPA2(vm->page_shift);
pa |= FIELD_GET(PTE_ADDR_51_50_LPA2, pte) << 50;
} else {
pa = pte & PTE_ADDR_MASK(vm->page_shift);
if (vm->page_shift == 16)
pa |= FIELD_GET(PTE_ADDR_51_48, pte) << 48;
}
return pa;
}
static u64 ptrs_per_pgd(struct kvm_vm *vm)
{
unsigned int shift = (vm->mmu.pgtable_levels - 1) * (vm->page_shift - 3) + vm->page_shift;
return 1 << (vm->va_bits - shift);
}
static u64 __maybe_unused ptrs_per_pte(struct kvm_vm *vm)
{
return 1 << (vm->page_shift - 3);
}
void virt_arch_pgd_alloc(struct kvm_vm *vm)
{
size_t nr_pages = vm_page_align(vm, ptrs_per_pgd(vm) * 8) / vm->page_size;
if (vm->mmu.pgd_created)
return;
vm->mmu.pgd = vm_phy_pages_alloc(vm, nr_pages,
KVM_GUEST_PAGE_TABLE_MIN_PADDR,
vm->memslots[MEM_REGION_PT]);
vm->mmu.pgd_created = true;
}
static void _virt_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa,
u64 flags)
{
u8 attr_idx = flags & (PTE_ATTRINDX_MASK >> PTE_ATTRINDX_SHIFT);
u64 pg_attr;
u64 *ptep;
TEST_ASSERT((gva % vm->page_size) == 0,
"Virtual address not on page boundary,\n"
" gva: 0x%lx vm->page_size: 0x%x", gva, vm->page_size);
TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, (gva >> vm->page_shift)),
"Invalid virtual address, gva: 0x%lx", gva);
TEST_ASSERT((gpa % vm->page_size) == 0,
"Physical address not on page boundary,\n"
" gpa: 0x%lx vm->page_size: 0x%x", gpa, vm->page_size);
TEST_ASSERT((gpa >> vm->page_shift) <= vm->max_gfn,
"Physical address beyond beyond maximum supported,\n"
" gpa: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x",
gpa, vm->max_gfn, vm->page_size);
ptep = addr_gpa2hva(vm, vm->mmu.pgd) + pgd_index(vm, gva) * 8;
if (!*ptep)
*ptep = addr_pte(vm, vm_alloc_page_table(vm),
PGD_TYPE_TABLE | PTE_VALID);
switch (vm->mmu.pgtable_levels) {
case 4:
ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pud_index(vm, gva) * 8;
if (!*ptep)
*ptep = addr_pte(vm, vm_alloc_page_table(vm),
PUD_TYPE_TABLE | PTE_VALID);
/* fall through */
case 3:
ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pmd_index(vm, gva) * 8;
if (!*ptep)
*ptep = addr_pte(vm, vm_alloc_page_table(vm),
PMD_TYPE_TABLE | PTE_VALID);
/* fall through */
case 2:
ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pte_index(vm, gva) * 8;
break;
default:
TEST_FAIL("Page table levels must be 2, 3, or 4");
}
pg_attr = PTE_AF | PTE_ATTRINDX(attr_idx) | PTE_TYPE_PAGE | PTE_VALID;
if (!use_lpa2_pte_format(vm))
pg_attr |= PTE_SHARED;
*ptep = addr_pte(vm, gpa, pg_attr);
}
void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa)
{
u64 attr_idx = MT_NORMAL;
_virt_pg_map(vm, gva, gpa, attr_idx);
}
u64 *virt_get_pte_hva_at_level(struct kvm_vm *vm, gva_t gva, int level)
{
u64 *ptep;
if (!vm->mmu.pgd_created)
goto unmapped_gva;
ptep = addr_gpa2hva(vm, vm->mmu.pgd) + pgd_index(vm, gva) * 8;
if (!ptep)
goto unmapped_gva;
if (level == 0)
return ptep;
switch (vm->mmu.pgtable_levels) {
case 4:
ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pud_index(vm, gva) * 8;
if (!ptep)
goto unmapped_gva;
if (level == 1)
break;
/* fall through */
case 3:
ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pmd_index(vm, gva) * 8;
if (!ptep)
goto unmapped_gva;
if (level == 2)
break;
/* fall through */
case 2:
ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pte_index(vm, gva) * 8;
if (!ptep)
goto unmapped_gva;
break;
default:
TEST_FAIL("Page table levels must be 2, 3, or 4");
}
return ptep;
unmapped_gva:
TEST_FAIL("No mapping for vm virtual address, gva: 0x%lx", gva);
exit(EXIT_FAILURE);
}
u64 *virt_get_pte_hva(struct kvm_vm *vm, gva_t gva)
{
return virt_get_pte_hva_at_level(vm, gva, 3);
}
gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva)
{
u64 *ptep = virt_get_pte_hva(vm, gva);
return pte_addr(vm, *ptep) + (gva & (vm->page_size - 1));
}
static void pte_dump(FILE *stream, struct kvm_vm *vm, u8 indent, u64 page, int level)
{
#ifdef DEBUG
static const char * const type[] = { "", "pud", "pmd", "pte" };
u64 pte, *ptep;
if (level == 4)
return;
for (pte = page; pte < page + ptrs_per_pte(vm) * 8; pte += 8) {
ptep = addr_gpa2hva(vm, pte);
if (!*ptep)
continue;
fprintf(stream, "%*s%s: %lx: %lx at %p\n", indent, "", type[level], pte, *ptep, ptep);
pte_dump(stream, vm, indent + 1, pte_addr(vm, *ptep), level + 1);
}
#endif
}
void virt_arch_dump(FILE *stream, struct kvm_vm *vm, u8 indent)
{
int level = 4 - (vm->mmu.pgtable_levels - 1);
u64 pgd, *ptep;
if (!vm->mmu.pgd_created)
return;
for (pgd = vm->mmu.pgd; pgd < vm->mmu.pgd + ptrs_per_pgd(vm) * 8; pgd += 8) {
ptep = addr_gpa2hva(vm, pgd);
if (!*ptep)
continue;
fprintf(stream, "%*spgd: %lx: %lx at %p\n", indent, "", pgd, *ptep, ptep);
pte_dump(stream, vm, indent + 1, pte_addr(vm, *ptep), level);
}
}
bool vm_supports_el2(struct kvm_vm *vm)
{
const char *value = getenv("NV");
if (value && *value == '0')
return false;
return vm_check_cap(vm, KVM_CAP_ARM_EL2) && vm->arch.has_gic;
}
void kvm_get_default_vcpu_target(struct kvm_vm *vm, struct kvm_vcpu_init *init)
{
struct kvm_vcpu_init preferred = {};
vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &preferred);
if (vm_supports_el2(vm))
preferred.features[0] |= BIT(KVM_ARM_VCPU_HAS_EL2);
*init = preferred;
}
void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init)
{
struct kvm_vcpu_init default_init = { .target = -1, };
struct kvm_vm *vm = vcpu->vm;
u64 sctlr_el1, tcr_el1, ttbr0_el1;
if (!init) {
kvm_get_default_vcpu_target(vm, &default_init);
init = &default_init;
}
vcpu_ioctl(vcpu, KVM_ARM_VCPU_INIT, init);
vcpu->init = *init;
/*
* Enable FP/ASIMD to avoid trapping when accessing Q0-Q15
* registers, which the variable argument list macros do.
*/
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_CPACR_EL1), 3 << 20);
sctlr_el1 = vcpu_get_reg(vcpu, ctxt_reg_alias(vcpu, SYS_SCTLR_EL1));
tcr_el1 = vcpu_get_reg(vcpu, ctxt_reg_alias(vcpu, SYS_TCR_EL1));
/* Configure base granule size */
switch (vm->mode) {
case VM_MODE_PXXVYY_4K:
TEST_FAIL("AArch64 does not support 4K sized pages "
"with ANY-bit physical address ranges");
case VM_MODE_P52V48_64K:
case VM_MODE_P48V48_64K:
case VM_MODE_P40V48_64K:
case VM_MODE_P36V48_64K:
tcr_el1 |= TCR_TG0_64K;
break;
case VM_MODE_P52V48_16K:
case VM_MODE_P48V48_16K:
case VM_MODE_P40V48_16K:
case VM_MODE_P36V48_16K:
case VM_MODE_P36V47_16K:
tcr_el1 |= TCR_TG0_16K;
break;
case VM_MODE_P52V48_4K:
case VM_MODE_P48V48_4K:
case VM_MODE_P40V48_4K:
case VM_MODE_P36V48_4K:
tcr_el1 |= TCR_TG0_4K;
break;
default:
TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode);
}
ttbr0_el1 = vm->mmu.pgd & GENMASK(47, vm->page_shift);
/* Configure output size */
switch (vm->mode) {
case VM_MODE_P52V48_4K:
case VM_MODE_P52V48_16K:
case VM_MODE_P52V48_64K:
tcr_el1 |= TCR_IPS_52_BITS;
ttbr0_el1 |= FIELD_GET(GENMASK(51, 48), vm->mmu.pgd) << 2;
break;
case VM_MODE_P48V48_4K:
case VM_MODE_P48V48_16K:
case VM_MODE_P48V48_64K:
tcr_el1 |= TCR_IPS_48_BITS;
break;
case VM_MODE_P40V48_4K:
case VM_MODE_P40V48_16K:
case VM_MODE_P40V48_64K:
tcr_el1 |= TCR_IPS_40_BITS;
break;
case VM_MODE_P36V48_4K:
case VM_MODE_P36V48_16K:
case VM_MODE_P36V48_64K:
case VM_MODE_P36V47_16K:
tcr_el1 |= TCR_IPS_36_BITS;
break;
default:
TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode);
}
sctlr_el1 |= SCTLR_ELx_M | SCTLR_ELx_C | SCTLR_ELx_I;
tcr_el1 |= TCR_IRGN0_WBWA | TCR_ORGN0_WBWA | TCR_SH0_INNER;
tcr_el1 |= TCR_T0SZ(vm->va_bits);
tcr_el1 |= TCR_TBI1;
tcr_el1 |= TCR_EPD1_MASK;
if (use_lpa2_pte_format(vm))
tcr_el1 |= TCR_DS;
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_SCTLR_EL1), sctlr_el1);
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_TCR_EL1), tcr_el1);
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_MAIR_EL1), DEFAULT_MAIR_EL1);
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_TTBR0_EL1), ttbr0_el1);
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TPIDR_EL1), vcpu->id);
if (!vcpu_has_el2(vcpu))
return;
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_HCR_EL2),
HCR_EL2_RW | HCR_EL2_TGE | HCR_EL2_E2H);
}
void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, u8 indent)
{
u64 pstate, pc;
pstate = vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pstate));
pc = vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc));
fprintf(stream, "%*spstate: 0x%.16lx pc: 0x%.16lx\n",
indent, "", pstate, pc);
}
void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)
{
vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.pc), (u64)guest_code);
}
static struct kvm_vcpu *__aarch64_vcpu_add(struct kvm_vm *vm, u32 vcpu_id,
struct kvm_vcpu_init *init)
{
size_t stack_size;
gva_t stack_gva;
struct kvm_vcpu *vcpu = __vm_vcpu_add(vm, vcpu_id);
stack_size = vm->page_size == 4096 ? DEFAULT_STACK_PGS * vm->page_size :
vm->page_size;
stack_gva = __vm_alloc(vm, stack_size,
DEFAULT_ARM64_GUEST_STACK_VADDR_MIN,
MEM_REGION_DATA);
aarch64_vcpu_setup(vcpu, init);
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_SP_EL1), stack_gva + stack_size);
return vcpu;
}
struct kvm_vcpu *aarch64_vcpu_add(struct kvm_vm *vm, u32 vcpu_id,
struct kvm_vcpu_init *init, void *guest_code)
{
struct kvm_vcpu *vcpu = __aarch64_vcpu_add(vm, vcpu_id, init);
vcpu_arch_set_entry_point(vcpu, guest_code);
return vcpu;
}
struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
{
return __aarch64_vcpu_add(vm, vcpu_id, NULL);
}
void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...)
{
va_list ap;
int i;
TEST_ASSERT(num >= 1 && num <= 8, "Unsupported number of args,\n"
" num: %u", num);
va_start(ap, num);
for (i = 0; i < num; i++) {
vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.regs[i]),
va_arg(ap, u64));
}
va_end(ap);
}
void kvm_exit_unexpected_exception(int vector, u64 ec, bool valid_ec)
{
ucall(UCALL_UNHANDLED, 3, vector, ec, valid_ec);
while (1)
;
}
void assert_on_unhandled_exception(struct kvm_vcpu *vcpu)
{
struct ucall uc;
if (get_ucall(vcpu, &uc) != UCALL_UNHANDLED)
return;
if (uc.args[2]) /* valid_ec */ {
assert(VECTOR_IS_SYNC(uc.args[0]));
TEST_FAIL("Unexpected exception (vector:0x%lx, ec:0x%lx)",
uc.args[0], uc.args[1]);
} else {
assert(!VECTOR_IS_SYNC(uc.args[0]));
TEST_FAIL("Unexpected exception (vector:0x%lx)",
uc.args[0]);
}
}
struct handlers {
handler_fn exception_handlers[VECTOR_NUM][ESR_ELx_EC_MAX + 1];
};
void vcpu_init_descriptor_tables(struct kvm_vcpu *vcpu)
{
extern char vectors;
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_VBAR_EL1), (u64)&vectors);
}
void route_exception(struct ex_regs *regs, int vector)
{
struct handlers *handlers = (struct handlers *)exception_handlers;
bool valid_ec;
int ec = 0;
switch (vector) {
case VECTOR_SYNC_CURRENT:
case VECTOR_SYNC_LOWER_64:
ec = ESR_ELx_EC(read_sysreg(esr_el1));
valid_ec = true;
break;
case VECTOR_IRQ_CURRENT:
case VECTOR_IRQ_LOWER_64:
case VECTOR_FIQ_CURRENT:
case VECTOR_FIQ_LOWER_64:
case VECTOR_ERROR_CURRENT:
case VECTOR_ERROR_LOWER_64:
ec = 0;
valid_ec = false;
break;
default:
valid_ec = false;
goto unexpected_exception;
}
if (handlers && handlers->exception_handlers[vector][ec])
return handlers->exception_handlers[vector][ec](regs);
unexpected_exception:
kvm_exit_unexpected_exception(vector, ec, valid_ec);
}
void vm_init_descriptor_tables(struct kvm_vm *vm)
{
vm->handlers = __vm_alloc(vm, sizeof(struct handlers), vm->page_size,
MEM_REGION_DATA);
*(gva_t *)addr_gva2hva(vm, (gva_t)(&exception_handlers)) = vm->handlers;
}
void vm_install_sync_handler(struct kvm_vm *vm, int vector, int ec,
void (*handler)(struct ex_regs *))
{
struct handlers *handlers = addr_gva2hva(vm, vm->handlers);
assert(VECTOR_IS_SYNC(vector));
assert(vector < VECTOR_NUM);
assert(ec <= ESR_ELx_EC_MAX);
handlers->exception_handlers[vector][ec] = handler;
}
void vm_install_exception_handler(struct kvm_vm *vm, int vector,
void (*handler)(struct ex_regs *))
{
struct handlers *handlers = addr_gva2hva(vm, vm->handlers);
assert(!VECTOR_IS_SYNC(vector));
assert(vector < VECTOR_NUM);
handlers->exception_handlers[vector][0] = handler;
}
u32 guest_get_vcpuid(void)
{
return read_sysreg(tpidr_el1);
}
static u32 max_ipa_for_page_size(u32 vm_ipa, u32 gran,
u32 not_sup_val, u32 ipa52_min_val)
{
if (gran == not_sup_val)
return 0;
else if (gran >= ipa52_min_val && vm_ipa >= 52)
return 52;
else
return min(vm_ipa, 48U);
}
void aarch64_get_supported_page_sizes(u32 ipa, u32 *ipa4k,
u32 *ipa16k, u32 *ipa64k)
{
struct kvm_vcpu_init preferred_init;
int kvm_fd, vm_fd, vcpu_fd, err;
u64 val;
u32 gran;
struct kvm_one_reg reg = {
.id = KVM_ARM64_SYS_REG(SYS_ID_AA64MMFR0_EL1),
.addr = (u64)&val,
};
kvm_fd = open_kvm_dev_path_or_exit();
vm_fd = __kvm_ioctl(kvm_fd, KVM_CREATE_VM, (void *)(unsigned long)ipa);
TEST_ASSERT(vm_fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_VM, vm_fd));
vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0);
TEST_ASSERT(vcpu_fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_VCPU, vcpu_fd));
err = ioctl(vm_fd, KVM_ARM_PREFERRED_TARGET, &preferred_init);
TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_ARM_PREFERRED_TARGET, err));
err = ioctl(vcpu_fd, KVM_ARM_VCPU_INIT, &preferred_init);
TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_ARM_VCPU_INIT, err));
err = ioctl(vcpu_fd, KVM_GET_ONE_REG, &reg);
TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_GET_ONE_REG, vcpu_fd));
gran = FIELD_GET(ID_AA64MMFR0_EL1_TGRAN4, val);
*ipa4k = max_ipa_for_page_size(ipa, gran, ID_AA64MMFR0_EL1_TGRAN4_NI,
ID_AA64MMFR0_EL1_TGRAN4_52_BIT);
gran = FIELD_GET(ID_AA64MMFR0_EL1_TGRAN64, val);
*ipa64k = max_ipa_for_page_size(ipa, gran, ID_AA64MMFR0_EL1_TGRAN64_NI,
ID_AA64MMFR0_EL1_TGRAN64_IMP);
gran = FIELD_GET(ID_AA64MMFR0_EL1_TGRAN16, val);
*ipa16k = max_ipa_for_page_size(ipa, gran, ID_AA64MMFR0_EL1_TGRAN16_NI,
ID_AA64MMFR0_EL1_TGRAN16_52_BIT);
close(vcpu_fd);
close(vm_fd);
close(kvm_fd);
}
#define __smccc_call(insn, function_id, arg0, arg1, arg2, arg3, arg4, arg5, \
arg6, res) \
asm volatile("mov w0, %w[function_id]\n" \
"mov x1, %[arg0]\n" \
"mov x2, %[arg1]\n" \
"mov x3, %[arg2]\n" \
"mov x4, %[arg3]\n" \
"mov x5, %[arg4]\n" \
"mov x6, %[arg5]\n" \
"mov x7, %[arg6]\n" \
#insn "#0\n" \
"mov %[res0], x0\n" \
"mov %[res1], x1\n" \
"mov %[res2], x2\n" \
"mov %[res3], x3\n" \
: [res0] "=r"(res->a0), [res1] "=r"(res->a1), \
[res2] "=r"(res->a2), [res3] "=r"(res->a3) \
: [function_id] "r"(function_id), [arg0] "r"(arg0), \
[arg1] "r"(arg1), [arg2] "r"(arg2), [arg3] "r"(arg3), \
[arg4] "r"(arg4), [arg5] "r"(arg5), [arg6] "r"(arg6) \
: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7")
void smccc_hvc(u32 function_id, u64 arg0, u64 arg1,
u64 arg2, u64 arg3, u64 arg4, u64 arg5,
u64 arg6, struct arm_smccc_res *res)
{
__smccc_call(hvc, function_id, arg0, arg1, arg2, arg3, arg4, arg5,
arg6, res);
}
void smccc_smc(u32 function_id, u64 arg0, u64 arg1,
u64 arg2, u64 arg3, u64 arg4, u64 arg5,
u64 arg6, struct arm_smccc_res *res)
{
__smccc_call(smc, function_id, arg0, arg1, arg2, arg3, arg4, arg5,
arg6, res);
}
void kvm_selftest_arch_init(void)
{
/*
* arm64 doesn't have a true default mode, so start by computing the
* available IPA space and page sizes early.
*/
guest_modes_append_default();
}
void vm_populate_gva_bitmap(struct kvm_vm *vm)
{
/*
* arm64 selftests use only TTBR0_EL1, meaning that the valid VA space
* is [0, 2^(64 - TCR_EL1.T0SZ)).
*/
sparsebit_set_num(vm->vpages_valid, 0,
(1ULL << vm->va_bits) >> vm->page_shift);
}
/* Helper to call wfi instruction. */
void wfi(void)
{
asm volatile("wfi");
}
static bool request_mte;
static bool request_vgic = true;
void test_wants_mte(void)
{
request_mte = true;
}
void test_disable_default_vgic(void)
{
request_vgic = false;
}
void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)
{
if (request_mte && vm_check_cap(vm, KVM_CAP_ARM_MTE))
vm_enable_cap(vm, KVM_CAP_ARM_MTE, 0);
if (request_vgic && kvm_supports_vgic_v3()) {
vm->arch.gic_fd = __vgic_v3_setup(vm, nr_vcpus, 64);
vm->arch.has_gic = true;
}
}
void kvm_arch_vm_finalize_vcpus(struct kvm_vm *vm)
{
if (vm->arch.has_gic)
__vgic_v3_init(vm->arch.gic_fd);
}
void kvm_arch_vm_release(struct kvm_vm *vm)
{
if (vm->arch.has_gic)
close(vm->arch.gic_fd);
}
bool kvm_arch_has_default_irqchip(void)
{
return request_vgic && kvm_supports_vgic_v3();
}
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ARM64 Spinlock support
*/
#include <stdint.h>
#include "spinlock.h"
void spin_lock(struct spinlock *lock)
{
int val, res;
asm volatile(
"1: ldaxr %w0, [%2]\n"
" cbnz %w0, 1b\n"
" mov %w0, #1\n"
" stxr %w1, %w0, [%2]\n"
" cbnz %w1, 1b\n"
: "=&r" (val), "=&r" (res)
: "r" (&lock->v)
: "memory");
}
void spin_unlock(struct spinlock *lock)
{
asm volatile("stlr wzr, [%0]\n" : : "r" (&lock->v) : "memory");
}
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ucall support. A ucall is a "hypercall to userspace".
*
* Copyright (C) 2018, Red Hat, Inc.
*/
#include "kvm_util.h"
gva_t *ucall_exit_mmio_addr;
void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa)
{
gva_t mmio_gva = vm_unused_gva_gap(vm, vm->page_size, KVM_UTIL_MIN_VADDR);
virt_map(vm, mmio_gva, mmio_gpa, 1);
vm->ucall_mmio_addr = mmio_gpa;
write_guest_global(vm, ucall_exit_mmio_addr, (gva_t *)mmio_gva);
}
void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
{
struct kvm_run *run = vcpu->run;
if (run->exit_reason == KVM_EXIT_MMIO &&
run->mmio.phys_addr == vcpu->vm->ucall_mmio_addr) {
TEST_ASSERT(run->mmio.is_write && run->mmio.len == sizeof(u64),
"Unexpected ucall exit mmio address access");
return (void *)(*((u64 *)run->mmio.data));
}
return NULL;
}
@@ -0,0 +1,212 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ARM Generic Interrupt Controller (GIC) v3 host support
*/
#include <linux/kernel.h>
#include <linux/kvm.h>
#include <linux/sizes.h>
#include <asm/cputype.h>
#include <asm/kvm_para.h>
#include <asm/kvm.h>
#include "kvm_util.h"
#include "vgic.h"
#include "gic.h"
#include "gic_v3.h"
bool kvm_supports_vgic_v3(void)
{
struct kvm_vm *vm = vm_create_barebones();
int r;
r = __kvm_test_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3);
kvm_vm_free(vm);
return !r;
}
/*
* vGIC-v3 default host setup
*
* Input args:
* vm - KVM VM
* nr_vcpus - Number of vCPUs supported by this VM
*
* Output args: None
*
* Return: GIC file-descriptor or negative error code upon failure
*
* The function creates a vGIC-v3 device and maps the distributor and
* redistributor regions of the guest. Since it depends on the number of
* vCPUs for the VM, it must be called after all the vCPUs have been created.
*/
int __vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, u32 nr_irqs)
{
int gic_fd;
u64 attr;
unsigned int nr_gic_pages;
/* Distributor setup */
gic_fd = __kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3);
if (gic_fd < 0)
return gic_fd;
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, &nr_irqs);
attr = GICD_BASE_GPA;
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V3_ADDR_TYPE_DIST, &attr);
nr_gic_pages = vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_DIST_SIZE);
virt_map(vm, GICD_BASE_GPA, GICD_BASE_GPA, nr_gic_pages);
/* Redistributor setup */
attr = REDIST_REGION_ATTR_ADDR(nr_vcpus, GICR_BASE_GPA, 0, 0);
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &attr);
nr_gic_pages = vm_calc_num_guest_pages(vm->mode,
KVM_VGIC_V3_REDIST_SIZE * nr_vcpus);
virt_map(vm, GICR_BASE_GPA, GICR_BASE_GPA, nr_gic_pages);
return gic_fd;
}
void __vgic_v3_init(int fd)
{
kvm_device_attr_set(fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
}
int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, u32 nr_irqs)
{
unsigned int nr_vcpus_created = 0;
struct list_head *iter;
int fd;
TEST_ASSERT(nr_vcpus, "Number of vCPUs cannot be empty");
/*
* Make sure that the caller is infact calling this
* function after all the vCPUs are added.
*/
list_for_each(iter, &vm->vcpus)
nr_vcpus_created++;
TEST_ASSERT(nr_vcpus == nr_vcpus_created,
"Number of vCPUs requested (%u) doesn't match with the ones created for the VM (%u)",
nr_vcpus, nr_vcpus_created);
fd = __vgic_v3_setup(vm, nr_vcpus, nr_irqs);
if (fd < 0)
return fd;
__vgic_v3_init(fd);
return fd;
}
/* should only work for level sensitive interrupts */
int _kvm_irq_set_level_info(int gic_fd, u32 intid, int level)
{
u64 attr = 32 * (intid / 32);
u64 index = intid % 32;
u64 val;
int ret;
ret = __kvm_device_attr_get(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO,
attr, &val);
if (ret != 0)
return ret;
val |= 1U << index;
ret = __kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO,
attr, &val);
return ret;
}
void kvm_irq_set_level_info(int gic_fd, u32 intid, int level)
{
int ret = _kvm_irq_set_level_info(gic_fd, intid, level);
TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, ret));
}
int _kvm_arm_irq_line(struct kvm_vm *vm, u32 intid, int level)
{
u32 irq = intid & KVM_ARM_IRQ_NUM_MASK;
TEST_ASSERT(!INTID_IS_SGI(intid), "KVM_IRQ_LINE's interface itself "
"doesn't allow injecting SGIs. There's no mask for it.");
if (INTID_IS_PPI(intid))
irq |= KVM_ARM_IRQ_TYPE_PPI << KVM_ARM_IRQ_TYPE_SHIFT;
else
irq |= KVM_ARM_IRQ_TYPE_SPI << KVM_ARM_IRQ_TYPE_SHIFT;
return _kvm_irq_line(vm, irq, level);
}
void kvm_arm_irq_line(struct kvm_vm *vm, u32 intid, int level)
{
int ret = _kvm_arm_irq_line(vm, intid, level);
TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_IRQ_LINE, ret));
}
static void vgic_poke_irq(int gic_fd, u32 intid, struct kvm_vcpu *vcpu,
u64 reg_off)
{
u64 reg = intid / 32;
u64 index = intid % 32;
u64 attr = reg_off + reg * 4;
u64 val;
bool intid_is_private = INTID_IS_SGI(intid) || INTID_IS_PPI(intid);
u32 group = intid_is_private ? KVM_DEV_ARM_VGIC_GRP_REDIST_REGS
: KVM_DEV_ARM_VGIC_GRP_DIST_REGS;
if (intid_is_private) {
/* TODO: only vcpu 0 implemented for now. */
assert(vcpu->id == 0);
attr += SZ_64K;
}
/* Check that the addr part of the attr is within 32 bits. */
assert((attr & ~KVM_DEV_ARM_VGIC_OFFSET_MASK) == 0);
/*
* All calls will succeed, even with invalid intid's, as long as the
* addr part of the attr is within 32 bits (checked above). An invalid
* intid will just make the read/writes point to above the intended
* register space (i.e., ICPENDR after ISPENDR).
*/
kvm_device_attr_get(gic_fd, group, attr, &val);
val |= 1ULL << index;
kvm_device_attr_set(gic_fd, group, attr, &val);
}
void kvm_irq_write_ispendr(int gic_fd, u32 intid, struct kvm_vcpu *vcpu)
{
vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISPENDR);
}
void kvm_irq_write_isactiver(int gic_fd, u32 intid, struct kvm_vcpu *vcpu)
{
vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISACTIVER);
}
int vgic_its_setup(struct kvm_vm *vm)
{
int its_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_ITS);
u64 attr;
attr = GITS_BASE_GPA;
kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_ITS_ADDR_TYPE, &attr);
kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
virt_map(vm, GITS_BASE_GPA, GITS_BASE_GPA,
vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_ITS_SIZE));
return its_fd;
}