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;
}
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* tools/testing/selftests/kvm/lib/assert.c
*
* Copyright (C) 2018, Google LLC.
*/
#include "test_util.h"
#include <sys/syscall.h>
#include "kselftest.h"
#ifdef __GLIBC__
#include <execinfo.h>
/* Dumps the current stack trace to stderr. */
static void __attribute__((noinline)) test_dump_stack(void);
static void test_dump_stack(void)
{
/*
* Build and run this command:
*
* addr2line -s -e /proc/$PPID/exe -fpai {backtrace addresses} | \
* cat -n 1>&2
*
* Note that the spacing is different and there's no newline.
*/
size_t i;
size_t n = 20;
void *stack[n];
const char *addr2line = "addr2line -s -e /proc/$PPID/exe -fpai";
const char *pipeline = "|cat -n 1>&2";
char cmd[strlen(addr2line) + strlen(pipeline) +
/* N bytes per addr * 2 digits per byte + 1 space per addr: */
n * (((sizeof(void *)) * 2) + 1) +
/* Null terminator: */
1];
char *c = cmd;
n = backtrace(stack, n);
/*
* Skip the first 2 frames, which should be test_dump_stack() and
* test_assert(); both of which are declared noinline. Bail if the
* resulting stack trace would be empty. Otherwise, addr2line will block
* waiting for addresses to be passed in via stdin.
*/
if (n <= 2) {
fputs(" (stack trace empty)\n", stderr);
return;
}
c += sprintf(c, "%s", addr2line);
for (i = 2; i < n; i++)
c += sprintf(c, " %lx", ((unsigned long) stack[i]) - 1);
c += sprintf(c, "%s", pipeline);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
system(cmd);
#pragma GCC diagnostic pop
}
#else
static void test_dump_stack(void) {}
#endif
static pid_t _gettid(void)
{
return syscall(SYS_gettid);
}
void __attribute__((noinline))
test_assert(bool exp, const char *exp_str,
const char *file, unsigned int line, const char *fmt, ...)
{
va_list ap;
if (!(exp)) {
va_start(ap, fmt);
fprintf(stderr, "==== Test Assertion Failure ====\n"
" %s:%u: %s\n"
" pid=%d tid=%d errno=%d - %s\n",
file, line, exp_str, getpid(), _gettid(),
errno, strerror(errno));
test_dump_stack();
if (fmt) {
fputs(" ", stderr);
vfprintf(stderr, fmt, ap);
fputs("\n", stderr);
}
va_end(ap);
if (errno == EACCES) {
print_skip("Access denied - Exiting");
exit(KSFT_SKIP);
}
exit(254);
}
return;
}
@@ -0,0 +1,194 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* tools/testing/selftests/kvm/lib/elf.c
*
* Copyright (C) 2018, Google LLC.
*/
#include "test_util.h"
#include <endian.h>
#include <linux/elf.h>
#include "kvm_util.h"
static void elfhdr_get(const char *filename, Elf64_Ehdr *hdrp)
{
off_t offset_rv;
/* Open the ELF file. */
int fd;
fd = open(filename, O_RDONLY);
TEST_ASSERT(fd >= 0, "Failed to open ELF file,\n"
" filename: %s\n"
" rv: %i errno: %i", filename, fd, errno);
/* Read in and validate ELF Identification Record.
* The ELF Identification record is the first 16 (EI_NIDENT) bytes
* of the ELF header, which is at the beginning of the ELF file.
* For now it is only safe to read the first EI_NIDENT bytes. Once
* read and validated, the value of e_ehsize can be used to determine
* the real size of the ELF header.
*/
unsigned char ident[EI_NIDENT];
test_read(fd, ident, sizeof(ident));
TEST_ASSERT((ident[EI_MAG0] == ELFMAG0) && (ident[EI_MAG1] == ELFMAG1)
&& (ident[EI_MAG2] == ELFMAG2) && (ident[EI_MAG3] == ELFMAG3),
"ELF MAGIC Mismatch,\n"
" filename: %s\n"
" ident[EI_MAG0 - EI_MAG3]: %02x %02x %02x %02x\n"
" Expected: %02x %02x %02x %02x",
filename,
ident[EI_MAG0], ident[EI_MAG1], ident[EI_MAG2], ident[EI_MAG3],
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3);
TEST_ASSERT(ident[EI_CLASS] == ELFCLASS64,
"Current implementation only able to handle ELFCLASS64,\n"
" filename: %s\n"
" ident[EI_CLASS]: %02x\n"
" expected: %02x",
filename,
ident[EI_CLASS], ELFCLASS64);
TEST_ASSERT(((BYTE_ORDER == LITTLE_ENDIAN)
&& (ident[EI_DATA] == ELFDATA2LSB))
|| ((BYTE_ORDER == BIG_ENDIAN)
&& (ident[EI_DATA] == ELFDATA2MSB)), "Current "
"implementation only able to handle\n"
"cases where the host and ELF file endianness\n"
"is the same:\n"
" host BYTE_ORDER: %u\n"
" host LITTLE_ENDIAN: %u\n"
" host BIG_ENDIAN: %u\n"
" ident[EI_DATA]: %u\n"
" ELFDATA2LSB: %u\n"
" ELFDATA2MSB: %u",
BYTE_ORDER, LITTLE_ENDIAN, BIG_ENDIAN,
ident[EI_DATA], ELFDATA2LSB, ELFDATA2MSB);
TEST_ASSERT(ident[EI_VERSION] == EV_CURRENT,
"Current implementation only able to handle current "
"ELF version,\n"
" filename: %s\n"
" ident[EI_VERSION]: %02x\n"
" expected: %02x",
filename, ident[EI_VERSION], EV_CURRENT);
/* Read in the ELF header.
* With the ELF Identification portion of the ELF header
* validated, especially that the value at EI_VERSION is
* as expected, it is now safe to read the entire ELF header.
*/
offset_rv = lseek(fd, 0, SEEK_SET);
TEST_ASSERT(offset_rv == 0, "Seek to ELF header failed,\n"
" rv: %zi expected: %i", offset_rv, 0);
test_read(fd, hdrp, sizeof(*hdrp));
TEST_ASSERT(hdrp->e_phentsize == sizeof(Elf64_Phdr),
"Unexpected physical header size,\n"
" hdrp->e_phentsize: %x\n"
" expected: %zx",
hdrp->e_phentsize, sizeof(Elf64_Phdr));
TEST_ASSERT(hdrp->e_shentsize == sizeof(Elf64_Shdr),
"Unexpected section header size,\n"
" hdrp->e_shentsize: %x\n"
" expected: %zx",
hdrp->e_shentsize, sizeof(Elf64_Shdr));
close(fd);
}
/* VM ELF Load
*
* Input Args:
* filename - Path to ELF file
*
* Output Args: None
*
* Input/Output Args:
* vm - Pointer to opaque type that describes the VM.
*
* Return: None, TEST_ASSERT failures for all error conditions
*
* Loads the program image of the ELF file specified by filename,
* into the virtual address space of the VM pointed to by vm. On entry
* the VM needs to not be using any of the virtual address space used
* by the image and it needs to have sufficient available physical pages, to
* back the virtual pages used to load the image.
*/
void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename)
{
off_t offset, offset_rv;
Elf64_Ehdr hdr;
/* Open the ELF file. */
int fd;
fd = open(filename, O_RDONLY);
TEST_ASSERT(fd >= 0, "Failed to open ELF file,\n"
" filename: %s\n"
" rv: %i errno: %i", filename, fd, errno);
/* Read in the ELF header. */
elfhdr_get(filename, &hdr);
/* For each program header.
* The following ELF header members specify the location
* and size of the program headers:
*
* e_phoff - File offset to start of program headers
* e_phentsize - Size of each program header
* e_phnum - Number of program header entries
*/
for (unsigned int n1 = 0; n1 < hdr.e_phnum; n1++) {
/* Seek to the beginning of the program header. */
offset = hdr.e_phoff + (n1 * hdr.e_phentsize);
offset_rv = lseek(fd, offset, SEEK_SET);
TEST_ASSERT(offset_rv == offset,
"Failed to seek to beginning of program header %u,\n"
" filename: %s\n"
" rv: %jd errno: %i",
n1, filename, (intmax_t) offset_rv, errno);
/* Read in the program header. */
Elf64_Phdr phdr;
test_read(fd, &phdr, sizeof(phdr));
/* Skip if this header doesn't describe a loadable segment. */
if (phdr.p_type != PT_LOAD)
continue;
/* Allocate memory for this segment within the VM. */
TEST_ASSERT(phdr.p_memsz > 0, "Unexpected loadable segment "
"memsize of 0,\n"
" phdr index: %u p_memsz: 0x%" PRIx64,
n1, (u64)phdr.p_memsz);
gva_t seg_vstart = align_down(phdr.p_vaddr, vm->page_size);
gva_t seg_vend = phdr.p_vaddr + phdr.p_memsz - 1;
seg_vend |= vm->page_size - 1;
size_t seg_size = seg_vend - seg_vstart + 1;
gva_t gva = __vm_alloc(vm, seg_size, seg_vstart, MEM_REGION_CODE);
TEST_ASSERT(gva == seg_vstart, "Unable to allocate "
"virtual memory for segment at requested min addr,\n"
" segment idx: %u\n"
" seg_vstart: 0x%lx\n"
" gva: 0x%lx",
n1, seg_vstart, gva);
memset(addr_gva2hva(vm, gva), 0, seg_size);
/* TODO(lhuemill): Set permissions of each memory segment
* based on the least-significant 3 bits of phdr.p_flags.
*/
/* Load portion of initial state that is contained within
* the ELF file.
*/
if (phdr.p_filesz) {
offset_rv = lseek(fd, phdr.p_offset, SEEK_SET);
TEST_ASSERT(offset_rv == phdr.p_offset,
"Seek to program segment offset failed,\n"
" program header idx: %u errno: %i\n"
" offset_rv: 0x%jx\n"
" expected: 0x%jx",
n1, errno, (intmax_t) offset_rv,
(intmax_t) phdr.p_offset);
test_read(fd, addr_gva2hva(vm, phdr.p_vaddr),
phdr.p_filesz);
}
}
close(fd);
}
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020, Red Hat, Inc.
*/
#include "guest_modes.h"
#if defined(__aarch64__) || defined(__riscv)
#include "processor.h"
enum vm_guest_mode vm_mode_default;
#endif
struct guest_mode guest_modes[NUM_VM_MODES];
void guest_modes_append_default(void)
{
#if !defined(__aarch64__) && !defined(__riscv)
guest_mode_append(VM_MODE_DEFAULT, true);
#endif
#ifdef __aarch64__
{
unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
u32 ipa4k, ipa16k, ipa64k;
int i;
aarch64_get_supported_page_sizes(limit, &ipa4k, &ipa16k, &ipa64k);
guest_mode_append(VM_MODE_P52V48_4K, ipa4k >= 52);
guest_mode_append(VM_MODE_P52V48_16K, ipa16k >= 52);
guest_mode_append(VM_MODE_P52V48_64K, ipa64k >= 52);
guest_mode_append(VM_MODE_P48V48_4K, ipa4k >= 48);
guest_mode_append(VM_MODE_P48V48_16K, ipa16k >= 48);
guest_mode_append(VM_MODE_P48V48_64K, ipa64k >= 48);
guest_mode_append(VM_MODE_P40V48_4K, ipa4k >= 40);
guest_mode_append(VM_MODE_P40V48_16K, ipa16k >= 40);
guest_mode_append(VM_MODE_P40V48_64K, ipa64k >= 40);
guest_mode_append(VM_MODE_P36V48_4K, ipa4k >= 36);
guest_mode_append(VM_MODE_P36V48_16K, ipa16k >= 36);
guest_mode_append(VM_MODE_P36V48_64K, ipa64k >= 36);
guest_mode_append(VM_MODE_P36V47_16K, ipa16k >= 36);
vm_mode_default = ipa4k >= 40 ? VM_MODE_P40V48_4K : NUM_VM_MODES;
/*
* Pick the first supported IPA size if the default
* isn't available.
*/
for (i = 0; vm_mode_default == NUM_VM_MODES && i < NUM_VM_MODES; i++) {
if (guest_modes[i].supported && guest_modes[i].enabled)
vm_mode_default = i;
}
TEST_ASSERT(vm_mode_default != NUM_VM_MODES,
"No supported mode!");
}
#endif
#ifdef __s390x__
{
int kvm_fd, vm_fd;
struct kvm_s390_vm_cpu_processor info;
kvm_fd = open_kvm_dev_path_or_exit();
vm_fd = __kvm_ioctl(kvm_fd, KVM_CREATE_VM, NULL);
kvm_device_attr_get(vm_fd, KVM_S390_VM_CPU_MODEL,
KVM_S390_VM_CPU_PROCESSOR, &info);
close(vm_fd);
close(kvm_fd);
/* Starting with z13 we have 47bits of physical address */
if (info.ibc >= 0x30)
guest_mode_append(VM_MODE_P47V64_4K, true);
}
#endif
#ifdef __riscv
{
unsigned int sz = kvm_check_cap(KVM_CAP_VM_GPA_BITS);
unsigned long satp_mode = riscv64_get_satp_mode() << SATP_MODE_SHIFT;
int i;
switch (sz) {
case 59:
guest_mode_append(VM_MODE_P56V57_4K, satp_mode >= SATP_MODE_57);
guest_mode_append(VM_MODE_P56V48_4K, satp_mode >= SATP_MODE_48);
guest_mode_append(VM_MODE_P56V39_4K, satp_mode >= SATP_MODE_39);
break;
case 50:
guest_mode_append(VM_MODE_P50V57_4K, satp_mode >= SATP_MODE_57);
guest_mode_append(VM_MODE_P50V48_4K, satp_mode >= SATP_MODE_48);
guest_mode_append(VM_MODE_P50V39_4K, satp_mode >= SATP_MODE_39);
break;
case 41:
guest_mode_append(VM_MODE_P41V57_4K, satp_mode >= SATP_MODE_57);
guest_mode_append(VM_MODE_P41V48_4K, satp_mode >= SATP_MODE_48);
guest_mode_append(VM_MODE_P41V39_4K, satp_mode >= SATP_MODE_39);
break;
default:
break;
}
/* set the first supported mode as default */
vm_mode_default = NUM_VM_MODES;
for (i = 0; vm_mode_default == NUM_VM_MODES && i < NUM_VM_MODES; i++) {
if (guest_modes[i].supported && guest_modes[i].enabled)
vm_mode_default = i;
}
TEST_ASSERT(vm_mode_default != NUM_VM_MODES, "No supported mode!");
}
#endif
}
void for_each_guest_mode(void (*func)(enum vm_guest_mode, void *), void *arg)
{
int i;
for (i = 0; i < NUM_VM_MODES; ++i) {
if (!guest_modes[i].enabled)
continue;
TEST_ASSERT(guest_modes[i].supported,
"Guest mode ID %d (%s) not supported.",
i, vm_guest_mode_string(i));
func(i, arg);
}
}
void guest_modes_help(void)
{
int i;
printf(" -m: specify the guest mode ID to test\n"
" (default: test all supported modes)\n"
" This option may be used multiple times.\n"
" Guest mode IDs:\n");
for (i = 0; i < NUM_VM_MODES; ++i) {
printf(" %d: %s%s\n", i, vm_guest_mode_string(i),
guest_modes[i].supported ? " (supported)" : "");
}
}
void guest_modes_cmdline(const char *arg)
{
static bool mode_selected;
unsigned int mode;
int i;
if (!mode_selected) {
for (i = 0; i < NUM_VM_MODES; ++i)
guest_modes[i].enabled = false;
mode_selected = true;
}
mode = atoi_non_negative("Guest mode ID", arg);
TEST_ASSERT(mode < NUM_VM_MODES, "Guest mode ID %d too big", mode);
guest_modes[mode].enabled = true;
}
@@ -0,0 +1,314 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "test_util.h"
#include "kvm_util.h"
#include "ucall_common.h"
#define APPEND_BUFFER_SAFE(str, end, v) \
do { \
GUEST_ASSERT(str < end); \
*str++ = (v); \
} while (0)
static int isdigit(int ch)
{
return (ch >= '0') && (ch <= '9');
}
static int skip_atoi(const char **s)
{
int i = 0;
while (isdigit(**s))
i = i * 10 + *((*s)++) - '0';
return i;
}
#define ZEROPAD 1 /* pad with zero */
#define SIGN 2 /* unsigned/signed long */
#define PLUS 4 /* show plus */
#define SPACE 8 /* space if plus */
#define LEFT 16 /* left justified */
#define SMALL 32 /* Must be 32 == 0x20 */
#define SPECIAL 64 /* 0x */
#define __do_div(n, base) \
({ \
int __res; \
\
__res = ((u64)n) % (u32)base; \
n = ((u64)n) / (u32)base; \
__res; \
})
static char *number(char *str, const char *end, long num, int base, int size,
int precision, int type)
{
/* we are called with base 8, 10 or 16, only, thus don't need "G..." */
static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
char tmp[66];
char c, sign, locase;
int i;
/*
* locase = 0 or 0x20. ORing digits or letters with 'locase'
* produces same digits or (maybe lowercased) letters
*/
locase = (type & SMALL);
if (type & LEFT)
type &= ~ZEROPAD;
if (base < 2 || base > 16)
return NULL;
c = (type & ZEROPAD) ? '0' : ' ';
sign = 0;
if (type & SIGN) {
if (num < 0) {
sign = '-';
num = -num;
size--;
} else if (type & PLUS) {
sign = '+';
size--;
} else if (type & SPACE) {
sign = ' ';
size--;
}
}
if (type & SPECIAL) {
if (base == 16)
size -= 2;
else if (base == 8)
size--;
}
i = 0;
if (num == 0)
tmp[i++] = '0';
else
while (num != 0)
tmp[i++] = (digits[__do_div(num, base)] | locase);
if (i > precision)
precision = i;
size -= precision;
if (!(type & (ZEROPAD + LEFT)))
while (size-- > 0)
APPEND_BUFFER_SAFE(str, end, ' ');
if (sign)
APPEND_BUFFER_SAFE(str, end, sign);
if (type & SPECIAL) {
if (base == 8)
APPEND_BUFFER_SAFE(str, end, '0');
else if (base == 16) {
APPEND_BUFFER_SAFE(str, end, '0');
APPEND_BUFFER_SAFE(str, end, 'x');
}
}
if (!(type & LEFT))
while (size-- > 0)
APPEND_BUFFER_SAFE(str, end, c);
while (i < precision--)
APPEND_BUFFER_SAFE(str, end, '0');
while (i-- > 0)
APPEND_BUFFER_SAFE(str, end, tmp[i]);
while (size-- > 0)
APPEND_BUFFER_SAFE(str, end, ' ');
return str;
}
int guest_vsnprintf(char *buf, int n, const char *fmt, va_list args)
{
char *str, *end;
const char *s;
u64 num;
int i, base;
int len;
int flags; /* flags to number() */
int field_width; /* width of output field */
int precision; /*
* min. # of digits for integers; max
* number of chars for from string
*/
int qualifier; /* 'h', 'l', or 'L' for integer fields */
end = buf + n;
GUEST_ASSERT(buf < end);
GUEST_ASSERT(n > 0);
for (str = buf; *fmt; ++fmt) {
if (*fmt != '%') {
APPEND_BUFFER_SAFE(str, end, *fmt);
continue;
}
/* process flags */
flags = 0;
repeat:
++fmt; /* this also skips first '%' */
switch (*fmt) {
case '-':
flags |= LEFT;
goto repeat;
case '+':
flags |= PLUS;
goto repeat;
case ' ':
flags |= SPACE;
goto repeat;
case '#':
flags |= SPECIAL;
goto repeat;
case '0':
flags |= ZEROPAD;
goto repeat;
}
/* get field width */
field_width = -1;
if (isdigit(*fmt))
field_width = skip_atoi(&fmt);
else if (*fmt == '*') {
++fmt;
/* it's the next argument */
field_width = va_arg(args, int);
if (field_width < 0) {
field_width = -field_width;
flags |= LEFT;
}
}
/* get the precision */
precision = -1;
if (*fmt == '.') {
++fmt;
if (isdigit(*fmt))
precision = skip_atoi(&fmt);
else if (*fmt == '*') {
++fmt;
/* it's the next argument */
precision = va_arg(args, int);
}
if (precision < 0)
precision = 0;
}
/* get the conversion qualifier */
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
qualifier = *fmt;
++fmt;
}
/*
* Play nice with %llu, %llx, etc. KVM selftests only support
* 64-bit builds, so just treat %ll* the same as %l*.
*/
if (qualifier == 'l' && *fmt == 'l')
++fmt;
/* default base */
base = 10;
switch (*fmt) {
case 'c':
if (!(flags & LEFT))
while (--field_width > 0)
APPEND_BUFFER_SAFE(str, end, ' ');
APPEND_BUFFER_SAFE(str, end,
(u8)va_arg(args, int));
while (--field_width > 0)
APPEND_BUFFER_SAFE(str, end, ' ');
continue;
case 's':
s = va_arg(args, char *);
len = strnlen(s, precision);
if (!(flags & LEFT))
while (len < field_width--)
APPEND_BUFFER_SAFE(str, end, ' ');
for (i = 0; i < len; ++i)
APPEND_BUFFER_SAFE(str, end, *s++);
while (len < field_width--)
APPEND_BUFFER_SAFE(str, end, ' ');
continue;
case 'p':
if (field_width == -1) {
field_width = 2 * sizeof(void *);
flags |= SPECIAL | SMALL | ZEROPAD;
}
str = number(str, end,
(u64)va_arg(args, void *), 16,
field_width, precision, flags);
continue;
case 'n':
if (qualifier == 'l') {
long *ip = va_arg(args, long *);
*ip = (str - buf);
} else {
int *ip = va_arg(args, int *);
*ip = (str - buf);
}
continue;
case '%':
APPEND_BUFFER_SAFE(str, end, '%');
continue;
/* integer number formats - set up the flags and "break" */
case 'o':
base = 8;
break;
case 'x':
flags |= SMALL;
case 'X':
base = 16;
break;
case 'd':
case 'i':
flags |= SIGN;
case 'u':
break;
default:
APPEND_BUFFER_SAFE(str, end, '%');
if (*fmt)
APPEND_BUFFER_SAFE(str, end, *fmt);
else
--fmt;
continue;
}
if (qualifier == 'l')
num = va_arg(args, u64);
else if (qualifier == 'h') {
num = (u16)va_arg(args, int);
if (flags & SIGN)
num = (s16)num;
} else if (flags & SIGN)
num = va_arg(args, int);
else
num = va_arg(args, u32);
str = number(str, end, num, base, field_width, precision, flags);
}
GUEST_ASSERT(str < end);
*str = '\0';
return str - buf;
}
int guest_snprintf(char *buf, int n, const char *fmt, ...)
{
va_list va;
int len;
va_start(va, fmt);
len = guest_vsnprintf(buf, n, fmt, va);
va_end(va);
return len;
}
@@ -0,0 +1,157 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* tools/testing/selftests/kvm/lib/io.c
*
* Copyright (C) 2018, Google LLC.
*/
#include "test_util.h"
/* Test Write
*
* A wrapper for write(2), that automatically handles the following
* special conditions:
*
* + Interrupted system call (EINTR)
* + Write of less than requested amount
* + Non-block return (EAGAIN)
*
* For each of the above, an additional write is performed to automatically
* continue writing the requested data.
* There are also many cases where write(2) can return an unexpected
* error (e.g. EIO). Such errors cause a TEST_ASSERT failure.
*
* Note, for function signature compatibility with write(2), this function
* returns the number of bytes written, but that value will always be equal
* to the number of requested bytes. All other conditions in this and
* future enhancements to this function either automatically issue another
* write(2) or cause a TEST_ASSERT failure.
*
* Args:
* fd - Opened file descriptor to file to be written.
* count - Number of bytes to write.
*
* Output:
* buf - Starting address of data to be written.
*
* Return:
* On success, number of bytes written.
* On failure, a TEST_ASSERT failure is caused.
*/
ssize_t test_write(int fd, const void *buf, size_t count)
{
ssize_t rc;
ssize_t num_written = 0;
size_t num_left = count;
const char *ptr = buf;
/* Note: Count of zero is allowed (see "RETURN VALUE" portion of
* write(2) manpage for details.
*/
TEST_ASSERT(count >= 0, "Unexpected count, count: %li", count);
do {
rc = write(fd, ptr, num_left);
switch (rc) {
case -1:
TEST_ASSERT(errno == EAGAIN || errno == EINTR,
"Unexpected write failure,\n"
" rc: %zi errno: %i", rc, errno);
continue;
case 0:
TEST_FAIL("Unexpected EOF,\n"
" rc: %zi num_written: %zi num_left: %zu",
rc, num_written, num_left);
break;
default:
TEST_ASSERT(rc >= 0, "Unexpected ret from write,\n"
" rc: %zi errno: %i", rc, errno);
num_written += rc;
num_left -= rc;
ptr += rc;
break;
}
} while (num_written < count);
return num_written;
}
/* Test Read
*
* A wrapper for read(2), that automatically handles the following
* special conditions:
*
* + Interrupted system call (EINTR)
* + Read of less than requested amount
* + Non-block return (EAGAIN)
*
* For each of the above, an additional read is performed to automatically
* continue reading the requested data.
* There are also many cases where read(2) can return an unexpected
* error (e.g. EIO). Such errors cause a TEST_ASSERT failure. Note,
* it is expected that the file opened by fd at the current file position
* contains at least the number of requested bytes to be read. A TEST_ASSERT
* failure is produced if an End-Of-File condition occurs, before all the
* data is read. It is the callers responsibility to assure that sufficient
* data exists.
*
* Note, for function signature compatibility with read(2), this function
* returns the number of bytes read, but that value will always be equal
* to the number of requested bytes. All other conditions in this and
* future enhancements to this function either automatically issue another
* read(2) or cause a TEST_ASSERT failure.
*
* Args:
* fd - Opened file descriptor to file to be read.
* count - Number of bytes to read.
*
* Output:
* buf - Starting address of where to write the bytes read.
*
* Return:
* On success, number of bytes read.
* On failure, a TEST_ASSERT failure is caused.
*/
ssize_t test_read(int fd, void *buf, size_t count)
{
ssize_t rc;
ssize_t num_read = 0;
size_t num_left = count;
char *ptr = buf;
/* Note: Count of zero is allowed (see "If count is zero" portion of
* read(2) manpage for details.
*/
TEST_ASSERT(count >= 0, "Unexpected count, count: %li", count);
do {
rc = read(fd, ptr, num_left);
switch (rc) {
case -1:
TEST_ASSERT(errno == EAGAIN || errno == EINTR,
"Unexpected read failure,\n"
" rc: %zi errno: %i", rc, errno);
break;
case 0:
TEST_FAIL("Unexpected EOF,\n"
" rc: %zi num_read: %zi num_left: %zu",
rc, num_read, num_left);
break;
default:
TEST_ASSERT(rc > 0, "Unexpected ret from read,\n"
" rc: %zi errno: %i", rc, errno);
num_read += rc;
num_left -= rc;
ptr += rc;
break;
}
} while (num_read < count);
return num_read;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include "processor.h"
/* address of refill exception should be 4K aligned */
.balign 4096
.global handle_tlb_refill
handle_tlb_refill:
csrwr t0, LOONGARCH_CSR_TLBRSAVE
csrrd t0, LOONGARCH_CSR_PGD
lddir t0, t0, 3
lddir t0, t0, 1
ldpte t0, 0
ldpte t0, 1
tlbfill
csrrd t0, LOONGARCH_CSR_TLBRSAVE
ertn
/*
* save and restore all gprs except base register,
* and default value of base register is sp ($r3).
*/
.macro save_gprs base
.irp n,1,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
st.d $r\n, \base, 8 * \n
.endr
.endm
.macro restore_gprs base
.irp n,1,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
ld.d $r\n, \base, 8 * \n
.endr
.endm
/* address of general exception should be 4K aligned */
.balign 4096
.global handle_exception
handle_exception:
csrwr sp, LOONGARCH_CSR_KS0
csrrd sp, LOONGARCH_CSR_KS1
addi.d sp, sp, -EXREGS_SIZE
save_gprs sp
/* save sp register to stack */
csrrd t0, LOONGARCH_CSR_KS0
st.d t0, sp, 3 * 8
csrrd t0, LOONGARCH_CSR_ERA
st.d t0, sp, PC_OFFSET_EXREGS
csrrd t0, LOONGARCH_CSR_ESTAT
st.d t0, sp, ESTAT_OFFSET_EXREGS
csrrd t0, LOONGARCH_CSR_BADV
st.d t0, sp, BADV_OFFSET_EXREGS
csrrd t0, LOONGARCH_CSR_PRMD
st.d t0, sp, PRMD_OFFSET_EXREGS
or a0, sp, zero
bl route_exception
ld.d t0, sp, PC_OFFSET_EXREGS
csrwr t0, LOONGARCH_CSR_ERA
ld.d t0, sp, PRMD_OFFSET_EXREGS
csrwr t0, LOONGARCH_CSR_PRMD
restore_gprs sp
csrrd sp, LOONGARCH_CSR_KS0
ertn
@@ -0,0 +1,402 @@
// SPDX-License-Identifier: GPL-2.0
#include <assert.h>
#include <linux/compiler.h>
#include <asm/kvm.h>
#include "kvm_util.h"
#include "pmu.h"
#include "processor.h"
#include "ucall_common.h"
#define LOONGARCH_PAGE_TABLE_PHYS_MIN 0x200000
#define LOONGARCH_GUEST_STACK_VADDR_MIN 0x200000
static gpa_t invalid_pgtable[4];
static gva_t exception_handlers;
static u64 virt_pte_index(struct kvm_vm *vm, gva_t gva, int level)
{
unsigned int shift;
u64 mask;
shift = level * (vm->page_shift - 3) + vm->page_shift;
mask = (1UL << (vm->page_shift - 3)) - 1;
return (gva >> shift) & mask;
}
static u64 pte_addr(struct kvm_vm *vm, u64 entry)
{
return entry & ~((0x1UL << vm->page_shift) - 1);
}
static u64 ptrs_per_pte(struct kvm_vm *vm)
{
return 1 << (vm->page_shift - 3);
}
static void virt_set_pgtable(struct kvm_vm *vm, gpa_t table, gpa_t child)
{
u64 *ptep;
int i, ptrs_per_pte;
ptep = addr_gpa2hva(vm, table);
ptrs_per_pte = 1 << (vm->page_shift - 3);
for (i = 0; i < ptrs_per_pte; i++)
WRITE_ONCE(*(ptep + i), child);
}
void virt_arch_pgd_alloc(struct kvm_vm *vm)
{
int i;
gpa_t child, table;
if (vm->mmu.pgd_created)
return;
child = table = 0;
for (i = 0; i < vm->mmu.pgtable_levels; i++) {
invalid_pgtable[i] = child;
table = vm_phy_page_alloc(vm, LOONGARCH_PAGE_TABLE_PHYS_MIN,
vm->memslots[MEM_REGION_PT]);
TEST_ASSERT(table, "Fail to allocate page tale at level %d\n", i);
virt_set_pgtable(vm, table, child);
child = table;
}
vm->mmu.pgd = table;
vm->mmu.pgd_created = true;
}
static int virt_pte_none(u64 *ptep, int level)
{
return *ptep == invalid_pgtable[level];
}
static u64 *virt_populate_pte(struct kvm_vm *vm, gva_t gva, int alloc)
{
int level;
u64 *ptep;
gpa_t child;
if (!vm->mmu.pgd_created)
goto unmapped_gva;
child = vm->mmu.pgd;
level = vm->mmu.pgtable_levels - 1;
while (level > 0) {
ptep = addr_gpa2hva(vm, child) + virt_pte_index(vm, gva, level) * 8;
if (virt_pte_none(ptep, level)) {
if (alloc) {
child = vm_alloc_page_table(vm);
virt_set_pgtable(vm, child, invalid_pgtable[level - 1]);
WRITE_ONCE(*ptep, child);
} else
goto unmapped_gva;
} else
child = pte_addr(vm, *ptep);
level--;
}
ptep = addr_gpa2hva(vm, child) + virt_pte_index(vm, gva, level) * 8;
return ptep;
unmapped_gva:
TEST_FAIL("No mapping for vm virtual address, gva: 0x%lx", gva);
exit(EXIT_FAILURE);
}
gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva)
{
u64 *ptep;
ptep = virt_populate_pte(vm, gva, 0);
TEST_ASSERT(*ptep != 0, "Virtual address gva: 0x%lx not mapped\n", gva);
return pte_addr(vm, *ptep) + (gva & (vm->page_size - 1));
}
void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa)
{
u32 prot_bits;
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 maximum supported,\n"
"gpa: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x",
gpa, vm->max_gfn, vm->page_size);
ptep = virt_populate_pte(vm, gva, 1);
prot_bits = _PAGE_PRESENT | __READABLE | __WRITEABLE | _CACHE_CC | _PAGE_USER;
WRITE_ONCE(*ptep, gpa | prot_bits);
}
static void pte_dump(FILE *stream, struct kvm_vm *vm, u8 indent, u64 page, int level)
{
u64 pte, *ptep;
static const char * const type[] = { "pte", "pmd", "pud", "pgd"};
if (level < 0)
return;
for (pte = page; pte < page + ptrs_per_pte(vm) * 8; pte += 8) {
ptep = addr_gpa2hva(vm, pte);
if (virt_pte_none(ptep, level))
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--);
}
}
void virt_arch_dump(FILE *stream, struct kvm_vm *vm, u8 indent)
{
int level;
if (!vm->mmu.pgd_created)
return;
level = vm->mmu.pgtable_levels - 1;
pte_dump(stream, vm, indent, vm->mmu.pgd, level);
}
void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, u8 indent)
{
}
void assert_on_unhandled_exception(struct kvm_vcpu *vcpu)
{
struct ucall uc;
if (get_ucall(vcpu, &uc) != UCALL_UNHANDLED)
return;
TEST_FAIL("Unexpected exception (pc:0x%lx, estat:0x%lx, badv:0x%lx)",
uc.args[0], uc.args[1], uc.args[2]);
}
void route_exception(struct ex_regs *regs)
{
int vector;
unsigned long pc, estat, badv;
struct handlers *handlers;
handlers = (struct handlers *)exception_handlers;
vector = (regs->estat & CSR_ESTAT_EXC) >> CSR_ESTAT_EXC_SHIFT;
if (handlers && handlers->exception_handlers[vector])
return handlers->exception_handlers[vector](regs);
pc = regs->pc;
badv = regs->badv;
estat = regs->estat;
ucall(UCALL_UNHANDLED, 3, pc, estat, badv);
while (1) ;
}
void vm_init_descriptor_tables(struct kvm_vm *vm)
{
void *addr;
vm->handlers = __vm_alloc(vm, sizeof(struct handlers),
LOONGARCH_GUEST_STACK_VADDR_MIN,
MEM_REGION_DATA);
addr = addr_gva2hva(vm, vm->handlers);
memset(addr, 0, vm->page_size);
exception_handlers = vm->handlers;
sync_global_to_guest(vm, exception_handlers);
}
void vm_install_exception_handler(struct kvm_vm *vm, int vector, handler_fn handler)
{
struct handlers *handlers = addr_gva2hva(vm, vm->handlers);
assert(vector < VECTOR_NUM);
handlers->exception_handlers[vector] = handler;
}
u32 guest_get_vcpuid(void)
{
return csr_read(LOONGARCH_CSR_CPUID);
}
void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...)
{
int i;
va_list ap;
struct kvm_regs regs;
TEST_ASSERT(num >= 1 && num <= 8, "Unsupported number of args,\n"
"num: %u\n", num);
vcpu_regs_get(vcpu, &regs);
va_start(ap, num);
for (i = 0; i < num; i++)
regs.gpr[i + 4] = va_arg(ap, u64);
va_end(ap);
vcpu_regs_set(vcpu, &regs);
}
static void loongarch_set_reg(struct kvm_vcpu *vcpu, u64 id, u64 val)
{
__vcpu_set_reg(vcpu, id, val);
}
static void loongarch_set_cpucfg(struct kvm_vcpu *vcpu, u64 id, u64 val)
{
u64 cfgid;
cfgid = KVM_REG_LOONGARCH_CPUCFG | KVM_REG_SIZE_U64 | 8 * id;
__vcpu_set_reg(vcpu, cfgid, val);
}
static void loongarch_get_csr(struct kvm_vcpu *vcpu, u64 id, void *addr)
{
u64 csrid;
csrid = KVM_REG_LOONGARCH_CSR | KVM_REG_SIZE_U64 | 8 * id;
__vcpu_get_reg(vcpu, csrid, addr);
}
static void loongarch_set_csr(struct kvm_vcpu *vcpu, u64 id, u64 val)
{
u64 csrid;
csrid = KVM_REG_LOONGARCH_CSR | KVM_REG_SIZE_U64 | 8 * id;
__vcpu_set_reg(vcpu, csrid, val);
}
void loongarch_vcpu_setup(struct kvm_vcpu *vcpu)
{
int width;
unsigned int cfg;
unsigned long val;
struct kvm_vm *vm = vcpu->vm;
switch (vm->mode) {
case VM_MODE_P36V47_16K:
case VM_MODE_P47V47_16K:
break;
default:
TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode);
}
cfg = read_cpucfg(LOONGARCH_CPUCFG6);
loongarch_set_cpucfg(vcpu, LOONGARCH_CPUCFG6, cfg);
/* kernel mode and page enable mode */
val = PLV_KERN | CSR_CRMD_PG;
loongarch_set_csr(vcpu, LOONGARCH_CSR_CRMD, val);
loongarch_set_csr(vcpu, LOONGARCH_CSR_PRMD, val);
loongarch_set_csr(vcpu, LOONGARCH_CSR_EUEN, 1);
loongarch_set_csr(vcpu, LOONGARCH_CSR_ECFG, 0);
loongarch_set_csr(vcpu, LOONGARCH_CSR_TCFG, 0);
loongarch_set_csr(vcpu, LOONGARCH_CSR_ASID, 1);
/* time count start from 0 */
val = 0;
loongarch_set_reg(vcpu, KVM_REG_LOONGARCH_COUNTER, val);
width = vm->page_shift - 3;
switch (vm->mmu.pgtable_levels) {
case 4:
/* pud page shift and width */
val = (vm->page_shift + width * 2) << 20 | (width << 25);
/* fall throuth */
case 3:
/* pmd page shift and width */
val |= (vm->page_shift + width) << 10 | (width << 15);
/* pte page shift and width */
val |= vm->page_shift | width << 5;
break;
default:
TEST_FAIL("Got %u page table levels, expected 3 or 4", vm->mmu.pgtable_levels);
}
loongarch_set_csr(vcpu, LOONGARCH_CSR_PWCTL0, val);
/* PGD page shift and width */
val = (vm->page_shift + width * (vm->mmu.pgtable_levels - 1)) | width << 6;
loongarch_set_csr(vcpu, LOONGARCH_CSR_PWCTL1, val);
loongarch_set_csr(vcpu, LOONGARCH_CSR_PGDL, vm->mmu.pgd);
/*
* Refill exception runs on real mode
* Entry address should be physical address
*/
val = addr_gva2gpa(vm, (unsigned long)handle_tlb_refill);
loongarch_set_csr(vcpu, LOONGARCH_CSR_TLBRENTRY, val);
/*
* General exception runs on page-enabled mode
* Entry address should be virtual address
*/
val = (unsigned long)handle_exception;
loongarch_set_csr(vcpu, LOONGARCH_CSR_EENTRY, val);
loongarch_get_csr(vcpu, LOONGARCH_CSR_TLBIDX, &val);
val &= ~CSR_TLBIDX_SIZEM;
val |= PS_DEFAULT_SIZE << CSR_TLBIDX_SIZE;
loongarch_set_csr(vcpu, LOONGARCH_CSR_TLBIDX, val);
loongarch_set_csr(vcpu, LOONGARCH_CSR_STLBPGSIZE, PS_DEFAULT_SIZE);
/* LOONGARCH_CSR_KS1 is used for exception stack */
val = __vm_alloc(vm, vm->page_size, LOONGARCH_GUEST_STACK_VADDR_MIN,
MEM_REGION_DATA);
TEST_ASSERT(val != 0, "No memory for exception stack");
val = val + vm->page_size;
loongarch_set_csr(vcpu, LOONGARCH_CSR_KS1, val);
loongarch_get_csr(vcpu, LOONGARCH_CSR_TLBREHI, &val);
val &= ~CSR_TLBREHI_PS;
val |= PS_DEFAULT_SIZE << CSR_TLBREHI_PS_SHIFT;
loongarch_set_csr(vcpu, LOONGARCH_CSR_TLBREHI, val);
loongarch_set_csr(vcpu, LOONGARCH_CSR_CPUID, vcpu->id);
loongarch_set_csr(vcpu, LOONGARCH_CSR_TMID, vcpu->id);
}
struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
{
size_t stack_size;
u64 stack_gva;
struct kvm_regs regs;
struct kvm_vcpu *vcpu;
vcpu = __vm_vcpu_add(vm, vcpu_id);
stack_size = vm->page_size;
stack_gva = __vm_alloc(vm, stack_size,
LOONGARCH_GUEST_STACK_VADDR_MIN, MEM_REGION_DATA);
TEST_ASSERT(stack_gva != 0, "No memory for vm stack");
loongarch_vcpu_setup(vcpu);
/* Setup guest general purpose registers */
vcpu_regs_get(vcpu, &regs);
regs.gpr[3] = stack_gva + stack_size;
vcpu_regs_set(vcpu, &regs);
return vcpu;
}
void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)
{
struct kvm_regs regs;
/* Setup guest PC register */
vcpu_regs_get(vcpu, &regs);
regs.pc = (u64)guest_code;
vcpu_regs_set(vcpu, &regs);
}
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ucall support. A ucall is a "hypercall to userspace".
*
*/
#include "kvm_util.h"
/*
* ucall_exit_mmio_addr holds per-VM values (global data is duplicated by each
* VM), it must not be accessed from host code.
*/
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,387 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2025, Google LLC.
*/
#include <time.h>
#include "lru_gen_util.h"
/*
* Tracks state while we parse memcg lru_gen stats. The file we're parsing is
* structured like this (some extra whitespace elided):
*
* memcg (id) (path)
* node (id)
* (gen_nr) (age_in_ms) (nr_anon_pages) (nr_file_pages)
*/
struct memcg_stats_parse_context {
bool consumed; /* Whether or not this line was consumed */
/* Next parse handler to invoke */
void (*next_handler)(struct memcg_stats *stats,
struct memcg_stats_parse_context *ctx,
char *line);
int current_node_idx; /* Current index in nodes array */
const char *name; /* The name of the memcg we're looking for */
};
static void memcg_stats_handle_searching(struct memcg_stats *stats,
struct memcg_stats_parse_context *ctx,
char *line);
static void memcg_stats_handle_in_memcg(struct memcg_stats *stats,
struct memcg_stats_parse_context *ctx,
char *line);
static void memcg_stats_handle_in_node(struct memcg_stats *stats,
struct memcg_stats_parse_context *ctx,
char *line);
struct split_iterator {
char *str;
char *save;
};
static char *split_next(struct split_iterator *it)
{
char *ret = strtok_r(it->str, " \t\n\r", &it->save);
it->str = NULL;
return ret;
}
static void memcg_stats_handle_searching(struct memcg_stats *stats,
struct memcg_stats_parse_context *ctx,
char *line)
{
struct split_iterator it = { .str = line };
char *prefix = split_next(&it);
char *memcg_id = split_next(&it);
char *memcg_name = split_next(&it);
char *end;
ctx->consumed = true;
if (!prefix || strcmp("memcg", prefix))
return; /* Not a memcg line (maybe empty), skip */
TEST_ASSERT(memcg_id && memcg_name,
"malformed memcg line; no memcg id or memcg_name");
if (strcmp(memcg_name + 1, ctx->name))
return; /* Wrong memcg, skip */
/* Found it! */
stats->memcg_id = strtoul(memcg_id, &end, 10);
TEST_ASSERT(*end == '\0', "malformed memcg id '%s'", memcg_id);
if (!stats->memcg_id)
return; /* Removed memcg? */
ctx->next_handler = memcg_stats_handle_in_memcg;
}
static void memcg_stats_handle_in_memcg(struct memcg_stats *stats,
struct memcg_stats_parse_context *ctx,
char *line)
{
struct split_iterator it = { .str = line };
char *prefix = split_next(&it);
char *id = split_next(&it);
long found_node_id;
char *end;
ctx->consumed = true;
ctx->current_node_idx = -1;
if (!prefix)
return; /* Skip empty lines */
if (!strcmp("memcg", prefix)) {
/* Memcg done, found next one; stop. */
ctx->next_handler = NULL;
return;
} else if (strcmp("node", prefix))
TEST_ASSERT(false, "found malformed line after 'memcg ...',"
"token: '%s'", prefix);
/* At this point we know we have a node line. Parse the ID. */
TEST_ASSERT(id, "malformed node line; no node id");
found_node_id = strtol(id, &end, 10);
TEST_ASSERT(*end == '\0', "malformed node id '%s'", id);
ctx->current_node_idx = stats->nr_nodes++;
TEST_ASSERT(ctx->current_node_idx < MAX_NR_NODES,
"memcg has stats for too many nodes, max is %d",
MAX_NR_NODES);
stats->nodes[ctx->current_node_idx].node = found_node_id;
ctx->next_handler = memcg_stats_handle_in_node;
}
static void memcg_stats_handle_in_node(struct memcg_stats *stats,
struct memcg_stats_parse_context *ctx,
char *line)
{
char *my_line = strdup(line);
struct split_iterator it = { .str = my_line };
char *gen, *age, *nr_anon, *nr_file;
struct node_stats *node_stats;
struct generation_stats *gen_stats;
char *end;
TEST_ASSERT(it.str, "failed to copy input line");
gen = split_next(&it);
if (!gen)
goto out_consume; /* Skip empty lines */
if (!strcmp("memcg", gen) || !strcmp("node", gen)) {
/*
* Reached next memcg or node section. Don't consume, let the
* other handler deal with this.
*/
ctx->next_handler = memcg_stats_handle_in_memcg;
goto out;
}
node_stats = &stats->nodes[ctx->current_node_idx];
TEST_ASSERT(node_stats->nr_gens < MAX_NR_GENS,
"found too many generation lines; max is %d",
MAX_NR_GENS);
gen_stats = &node_stats->gens[node_stats->nr_gens++];
age = split_next(&it);
nr_anon = split_next(&it);
nr_file = split_next(&it);
TEST_ASSERT(age && nr_anon && nr_file,
"malformed generation line; not enough tokens");
gen_stats->gen = (int)strtol(gen, &end, 10);
TEST_ASSERT(*end == '\0', "malformed generation number '%s'", gen);
gen_stats->age_ms = strtol(age, &end, 10);
TEST_ASSERT(*end == '\0', "malformed generation age '%s'", age);
gen_stats->nr_anon = strtol(nr_anon, &end, 10);
TEST_ASSERT(*end == '\0', "malformed anonymous page count '%s'",
nr_anon);
gen_stats->nr_file = strtol(nr_file, &end, 10);
TEST_ASSERT(*end == '\0', "malformed file page count '%s'", nr_file);
out_consume:
ctx->consumed = true;
out:
free(my_line);
}
static void print_memcg_stats(const struct memcg_stats *stats, const char *name)
{
int node, gen;
pr_debug("stats for memcg %s (id %lu):\n", name, stats->memcg_id);
for (node = 0; node < stats->nr_nodes; ++node) {
pr_debug("\tnode %d\n", stats->nodes[node].node);
for (gen = 0; gen < stats->nodes[node].nr_gens; ++gen) {
const struct generation_stats *gstats =
&stats->nodes[node].gens[gen];
pr_debug("\t\tgen %d\tage_ms %ld"
"\tnr_anon %ld\tnr_file %ld\n",
gstats->gen, gstats->age_ms, gstats->nr_anon,
gstats->nr_file);
}
}
}
/* Re-read lru_gen debugfs information for @memcg into @stats. */
void lru_gen_read_memcg_stats(struct memcg_stats *stats, const char *memcg)
{
FILE *f;
ssize_t read = 0;
char *line = NULL;
size_t bufsz;
struct memcg_stats_parse_context ctx = {
.next_handler = memcg_stats_handle_searching,
.name = memcg,
};
memset(stats, 0, sizeof(struct memcg_stats));
f = fopen(LRU_GEN_DEBUGFS, "r");
TEST_ASSERT(f, "fopen(%s) failed", LRU_GEN_DEBUGFS);
while (ctx.next_handler && (read = getline(&line, &bufsz, f)) > 0) {
ctx.consumed = false;
do {
ctx.next_handler(stats, &ctx, line);
if (!ctx.next_handler)
break;
} while (!ctx.consumed);
}
if (read < 0 && !feof(f))
TEST_ASSERT(false, "getline(%s) failed", LRU_GEN_DEBUGFS);
TEST_ASSERT(stats->memcg_id > 0, "Couldn't find memcg: %s\n"
"Did the memcg get created in the proper mount?",
memcg);
if (line)
free(line);
TEST_ASSERT(!fclose(f), "fclose(%s) failed", LRU_GEN_DEBUGFS);
print_memcg_stats(stats, memcg);
}
/*
* Find all pages tracked by lru_gen for this memcg in generation @target_gen.
*
* If @target_gen is negative, look for all generations.
*/
long lru_gen_sum_memcg_stats_for_gen(int target_gen,
const struct memcg_stats *stats)
{
int node, gen;
long total_nr = 0;
for (node = 0; node < stats->nr_nodes; ++node) {
const struct node_stats *node_stats = &stats->nodes[node];
for (gen = 0; gen < node_stats->nr_gens; ++gen) {
const struct generation_stats *gen_stats =
&node_stats->gens[gen];
if (target_gen >= 0 && gen_stats->gen != target_gen)
continue;
total_nr += gen_stats->nr_anon + gen_stats->nr_file;
}
}
return total_nr;
}
/* Find all pages tracked by lru_gen for this memcg. */
long lru_gen_sum_memcg_stats(const struct memcg_stats *stats)
{
return lru_gen_sum_memcg_stats_for_gen(-1, stats);
}
/*
* If lru_gen aging should force page table scanning.
*
* If you want to set this to false, you will need to do eviction
* before doing extra aging passes.
*/
static const bool force_scan = true;
static void run_aging_impl(unsigned long memcg_id, int node_id, int max_gen)
{
FILE *f = fopen(LRU_GEN_DEBUGFS, "w");
char *command;
size_t sz;
TEST_ASSERT(f, "fopen(%s) failed", LRU_GEN_DEBUGFS);
sz = asprintf(&command, "+ %lu %d %d 1 %d\n",
memcg_id, node_id, max_gen, force_scan);
TEST_ASSERT(sz > 0, "creating aging command failed");
pr_debug("Running aging command: %s", command);
if (fwrite(command, sizeof(char), sz, f) < sz) {
TEST_ASSERT(false, "writing aging command %s to %s failed",
command, LRU_GEN_DEBUGFS);
}
TEST_ASSERT(!fclose(f), "fclose(%s) failed", LRU_GEN_DEBUGFS);
}
void lru_gen_do_aging(struct memcg_stats *stats, const char *memcg)
{
int node, gen;
pr_debug("lru_gen: invoking aging...\n");
/* Must read memcg stats to construct the proper aging command. */
lru_gen_read_memcg_stats(stats, memcg);
for (node = 0; node < stats->nr_nodes; ++node) {
int max_gen = 0;
for (gen = 0; gen < stats->nodes[node].nr_gens; ++gen) {
int this_gen = stats->nodes[node].gens[gen].gen;
max_gen = max_gen > this_gen ? max_gen : this_gen;
}
run_aging_impl(stats->memcg_id, stats->nodes[node].node,
max_gen);
}
/* Re-read so callers get updated information */
lru_gen_read_memcg_stats(stats, memcg);
}
/*
* Find which generation contains at least @pages pages, assuming that
* such a generation exists.
*/
int lru_gen_find_generation(const struct memcg_stats *stats,
unsigned long pages)
{
int node, gen, gen_idx, min_gen = INT_MAX, max_gen = -1;
for (node = 0; node < stats->nr_nodes; ++node)
for (gen_idx = 0; gen_idx < stats->nodes[node].nr_gens;
++gen_idx) {
gen = stats->nodes[node].gens[gen_idx].gen;
max_gen = gen > max_gen ? gen : max_gen;
min_gen = gen < min_gen ? gen : min_gen;
}
for (gen = min_gen; gen <= max_gen; ++gen)
/* See if this generation has enough pages. */
if (lru_gen_sum_memcg_stats_for_gen(gen, stats) > pages)
return gen;
return -1;
}
bool lru_gen_usable(void)
{
long required_features = LRU_GEN_ENABLED | LRU_GEN_MM_WALK;
int lru_gen_fd, lru_gen_debug_fd;
char mglru_feature_str[8] = {};
long mglru_features;
lru_gen_fd = open(LRU_GEN_ENABLED_PATH, O_RDONLY);
if (lru_gen_fd < 0) {
puts("lru_gen: Could not open " LRU_GEN_ENABLED_PATH);
return false;
}
if (read(lru_gen_fd, &mglru_feature_str, 7) < 7) {
puts("lru_gen: Could not read from " LRU_GEN_ENABLED_PATH);
close(lru_gen_fd);
return false;
}
close(lru_gen_fd);
mglru_features = strtol(mglru_feature_str, NULL, 16);
if ((mglru_features & required_features) != required_features) {
printf("lru_gen: missing features, got: 0x%lx, expected: 0x%lx\n",
mglru_features, required_features);
printf("lru_gen: Try 'echo 0x%lx > /sys/kernel/mm/lru_gen/enabled'\n",
required_features);
return false;
}
lru_gen_debug_fd = open(LRU_GEN_DEBUGFS, O_RDWR);
__TEST_REQUIRE(lru_gen_debug_fd >= 0,
"lru_gen: Could not open " LRU_GEN_DEBUGFS ", "
"but lru_gen is enabled, so cannot use page_idle.");
close(lru_gen_debug_fd);
return true;
}
@@ -0,0 +1,387 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020, Google LLC.
*/
#include <inttypes.h>
#include <linux/bitmap.h>
#include "kvm_util.h"
#include "memstress.h"
#include "processor.h"
#include "ucall_common.h"
struct memstress_args memstress_args;
/*
* Guest virtual memory offset of the testing memory slot.
* Must not conflict with identity mapped test code.
*/
static u64 guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM;
struct vcpu_thread {
/* The index of the vCPU. */
int vcpu_idx;
/* The pthread backing the vCPU. */
pthread_t thread;
/* Set to true once the vCPU thread is up and running. */
bool running;
};
/* The vCPU threads involved in this test. */
static struct vcpu_thread vcpu_threads[KVM_MAX_VCPUS];
/* The function run by each vCPU thread, as provided by the test. */
static void (*vcpu_thread_fn)(struct memstress_vcpu_args *);
/* Set to true once all vCPU threads are up and running. */
static bool all_vcpu_threads_running;
static struct kvm_vcpu *vcpus[KVM_MAX_VCPUS];
/*
* Continuously write to the first 8 bytes of each page in the
* specified region.
*/
void memstress_guest_code(u32 vcpu_idx)
{
struct memstress_args *args = &memstress_args;
struct memstress_vcpu_args *vcpu_args = &args->vcpu_args[vcpu_idx];
struct guest_random_state rand_state;
gva_t gva;
u64 pages;
u64 addr;
u64 page;
int i;
rand_state = new_guest_random_state(guest_random_seed + vcpu_idx);
gva = vcpu_args->gva;
pages = vcpu_args->pages;
/* Make sure vCPU args data structure is not corrupt. */
GUEST_ASSERT(vcpu_args->vcpu_idx == vcpu_idx);
while (true) {
for (i = 0; i < sizeof(memstress_args); i += args->guest_page_size)
(void) *((volatile char *)args + i);
for (i = 0; i < pages; i++) {
if (args->random_access)
page = guest_random_u32(&rand_state) % pages;
else
page = i;
addr = gva + (page * args->guest_page_size);
if (__guest_random_bool(&rand_state, args->write_percent))
*(u64 *)addr = 0x0123456789ABCDEF;
else
READ_ONCE(*(u64 *)addr);
}
GUEST_SYNC(1);
}
}
void memstress_setup_vcpus(struct kvm_vm *vm, int nr_vcpus,
struct kvm_vcpu *vcpus[],
u64 vcpu_memory_bytes,
bool partition_vcpu_memory_access)
{
struct memstress_args *args = &memstress_args;
struct memstress_vcpu_args *vcpu_args;
int i;
for (i = 0; i < nr_vcpus; i++) {
vcpu_args = &args->vcpu_args[i];
vcpu_args->vcpu = vcpus[i];
vcpu_args->vcpu_idx = i;
if (partition_vcpu_memory_access) {
vcpu_args->gva = guest_test_virt_mem +
(i * vcpu_memory_bytes);
vcpu_args->pages = vcpu_memory_bytes /
args->guest_page_size;
vcpu_args->gpa = args->gpa + (i * vcpu_memory_bytes);
} else {
vcpu_args->gva = guest_test_virt_mem;
vcpu_args->pages = (nr_vcpus * vcpu_memory_bytes) /
args->guest_page_size;
vcpu_args->gpa = args->gpa;
}
vcpu_args_set(vcpus[i], 1, i);
pr_debug("Added VCPU %d with test mem gpa [%lx, %lx)\n",
i, vcpu_args->gpa, vcpu_args->gpa +
(vcpu_args->pages * args->guest_page_size));
}
}
struct kvm_vm *memstress_create_vm(enum vm_guest_mode mode, int nr_vcpus,
u64 vcpu_memory_bytes, int slots,
enum vm_mem_backing_src_type backing_src,
bool partition_vcpu_memory_access)
{
struct memstress_args *args = &memstress_args;
struct kvm_vm *vm;
u64 guest_num_pages, slot0_pages = 0;
u64 backing_src_pagesz = get_backing_src_pagesz(backing_src);
u64 region_end_gfn;
int i;
pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode));
/* By default vCPUs will write to memory. */
args->write_percent = 100;
/*
* Snapshot the non-huge page size. This is used by the guest code to
* access/dirty pages at the logging granularity.
*/
args->guest_page_size = vm_guest_mode_params[mode].page_size;
guest_num_pages = vm_adjust_num_guest_pages(mode,
(nr_vcpus * vcpu_memory_bytes) / args->guest_page_size);
TEST_ASSERT(vcpu_memory_bytes % getpagesize() == 0,
"Guest memory size is not host page size aligned.");
TEST_ASSERT(vcpu_memory_bytes % args->guest_page_size == 0,
"Guest memory size is not guest page size aligned.");
TEST_ASSERT(guest_num_pages % slots == 0,
"Guest memory cannot be evenly divided into %d slots.",
slots);
/*
* If using nested, allocate extra pages for the nested page tables and
* in-memory data structures.
*/
if (args->nested)
slot0_pages += memstress_nested_pages(nr_vcpus);
/*
* Pass guest_num_pages to populate the page tables for test memory.
* The memory is also added to memslot 0, but that's a benign side
* effect as KVM allows aliasing HVAs in meslots.
*/
vm = __vm_create_with_vcpus(VM_SHAPE(mode), nr_vcpus,
slot0_pages + guest_num_pages,
memstress_guest_code, vcpus);
args->vm = vm;
/* Put the test region at the top guest physical memory. */
region_end_gfn = vm->max_gfn + 1;
#ifdef __x86_64__
/*
* When running vCPUs in L2, restrict the test region to 48 bits to
* avoid needing 5-level page tables to identity map L2.
*/
if (args->nested)
region_end_gfn = min(region_end_gfn, (1UL << 48) / args->guest_page_size);
#endif
/*
* If there should be more memory in the guest test region than there
* can be pages in the guest, it will definitely cause problems.
*/
TEST_ASSERT(guest_num_pages < region_end_gfn,
"Requested more guest memory than address space allows.\n"
" guest pages: %" PRIx64 " max gfn: %" PRIx64
" nr_vcpus: %d wss: %" PRIx64 "]",
guest_num_pages, region_end_gfn - 1, nr_vcpus, vcpu_memory_bytes);
args->gpa = (region_end_gfn - guest_num_pages - 1) * args->guest_page_size;
args->gpa = align_down(args->gpa, backing_src_pagesz);
args->size = guest_num_pages * args->guest_page_size;
pr_info("guest physical test memory: [0x%lx, 0x%lx)\n",
args->gpa, args->gpa + args->size);
/* Add extra memory slots for testing */
for (i = 0; i < slots; i++) {
u64 region_pages = guest_num_pages / slots;
gpa_t region_start = args->gpa + region_pages * args->guest_page_size * i;
vm_userspace_mem_region_add(vm, backing_src, region_start,
MEMSTRESS_MEM_SLOT_INDEX + i,
region_pages, 0);
}
/* Do mapping for the demand paging memory slot */
virt_map(vm, guest_test_virt_mem, args->gpa, guest_num_pages);
memstress_setup_vcpus(vm, nr_vcpus, vcpus, vcpu_memory_bytes,
partition_vcpu_memory_access);
if (args->nested) {
pr_info("Configuring vCPUs to run in L2 (nested).\n");
memstress_setup_nested(vm, nr_vcpus, vcpus);
}
/* Export the shared variables to the guest. */
sync_global_to_guest(vm, memstress_args);
return vm;
}
void memstress_destroy_vm(struct kvm_vm *vm)
{
kvm_vm_free(vm);
}
void memstress_set_write_percent(struct kvm_vm *vm, u32 write_percent)
{
memstress_args.write_percent = write_percent;
sync_global_to_guest(vm, memstress_args.write_percent);
}
void memstress_set_random_access(struct kvm_vm *vm, bool random_access)
{
memstress_args.random_access = random_access;
sync_global_to_guest(vm, memstress_args.random_access);
}
u64 __weak memstress_nested_pages(int nr_vcpus)
{
return 0;
}
void __weak memstress_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu **vcpus)
{
pr_info("%s() not support on this architecture, skipping.\n", __func__);
exit(KSFT_SKIP);
}
static void *vcpu_thread_main(void *data)
{
struct vcpu_thread *vcpu = data;
int vcpu_idx = vcpu->vcpu_idx;
if (memstress_args.pin_vcpus)
pin_self_to_cpu(memstress_args.vcpu_to_pcpu[vcpu_idx]);
WRITE_ONCE(vcpu->running, true);
/*
* Wait for all vCPU threads to be up and running before calling the test-
* provided vCPU thread function. This prevents thread creation (which
* requires taking the mmap_sem in write mode) from interfering with the
* guest faulting in its memory.
*/
while (!READ_ONCE(all_vcpu_threads_running))
;
vcpu_thread_fn(&memstress_args.vcpu_args[vcpu_idx]);
return NULL;
}
void memstress_start_vcpu_threads(int nr_vcpus,
void (*vcpu_fn)(struct memstress_vcpu_args *))
{
int i;
vcpu_thread_fn = vcpu_fn;
WRITE_ONCE(all_vcpu_threads_running, false);
WRITE_ONCE(memstress_args.stop_vcpus, false);
for (i = 0; i < nr_vcpus; i++) {
struct vcpu_thread *vcpu = &vcpu_threads[i];
vcpu->vcpu_idx = i;
WRITE_ONCE(vcpu->running, false);
pthread_create(&vcpu->thread, NULL, vcpu_thread_main, vcpu);
}
for (i = 0; i < nr_vcpus; i++) {
while (!READ_ONCE(vcpu_threads[i].running))
;
}
WRITE_ONCE(all_vcpu_threads_running, true);
}
void memstress_join_vcpu_threads(int nr_vcpus)
{
int i;
WRITE_ONCE(memstress_args.stop_vcpus, true);
for (i = 0; i < nr_vcpus; i++)
pthread_join(vcpu_threads[i].thread, NULL);
}
static void toggle_dirty_logging(struct kvm_vm *vm, int slots, bool enable)
{
int i;
for (i = 0; i < slots; i++) {
int slot = MEMSTRESS_MEM_SLOT_INDEX + i;
int flags = enable ? KVM_MEM_LOG_DIRTY_PAGES : 0;
vm_mem_region_set_flags(vm, slot, flags);
}
}
void memstress_enable_dirty_logging(struct kvm_vm *vm, int slots)
{
toggle_dirty_logging(vm, slots, true);
}
void memstress_disable_dirty_logging(struct kvm_vm *vm, int slots)
{
toggle_dirty_logging(vm, slots, false);
}
void memstress_get_dirty_log(struct kvm_vm *vm, unsigned long *bitmaps[], int slots)
{
int i;
for (i = 0; i < slots; i++) {
int slot = MEMSTRESS_MEM_SLOT_INDEX + i;
kvm_vm_get_dirty_log(vm, slot, bitmaps[i]);
}
}
void memstress_clear_dirty_log(struct kvm_vm *vm, unsigned long *bitmaps[],
int slots, u64 pages_per_slot)
{
int i;
for (i = 0; i < slots; i++) {
int slot = MEMSTRESS_MEM_SLOT_INDEX + i;
kvm_vm_clear_dirty_log(vm, slot, bitmaps[i], 0, pages_per_slot);
}
}
unsigned long **memstress_alloc_bitmaps(int slots, u64 pages_per_slot)
{
unsigned long **bitmaps;
int i;
bitmaps = malloc(slots * sizeof(bitmaps[0]));
TEST_ASSERT(bitmaps, "Failed to allocate bitmaps array.");
for (i = 0; i < slots; i++) {
bitmaps[i] = bitmap_zalloc(pages_per_slot);
TEST_ASSERT(bitmaps[i], "Failed to allocate slot bitmap.");
}
return bitmaps;
}
void memstress_free_bitmaps(unsigned long *bitmaps[], int slots)
{
int i;
for (i = 0; i < slots; i++)
free(bitmaps[i]);
free(bitmaps);
}
@@ -0,0 +1 @@
#include "../../../../lib/rbtree.c"
@@ -0,0 +1,104 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2023 Intel Corporation
*/
#ifndef __ASSEMBLY__
#define __ASSEMBLY__
#endif
#include <asm/csr.h>
.macro save_context
addi sp, sp, (-8*36)
sd x1, 8(sp)
sd x2, 16(sp)
sd x3, 24(sp)
sd x4, 32(sp)
sd x5, 40(sp)
sd x6, 48(sp)
sd x7, 56(sp)
sd x8, 64(sp)
sd x9, 72(sp)
sd x10, 80(sp)
sd x11, 88(sp)
sd x12, 96(sp)
sd x13, 104(sp)
sd x14, 112(sp)
sd x15, 120(sp)
sd x16, 128(sp)
sd x17, 136(sp)
sd x18, 144(sp)
sd x19, 152(sp)
sd x20, 160(sp)
sd x21, 168(sp)
sd x22, 176(sp)
sd x23, 184(sp)
sd x24, 192(sp)
sd x25, 200(sp)
sd x26, 208(sp)
sd x27, 216(sp)
sd x28, 224(sp)
sd x29, 232(sp)
sd x30, 240(sp)
sd x31, 248(sp)
csrr s0, CSR_SEPC
csrr s1, CSR_SSTATUS
csrr s2, CSR_STVAL
csrr s3, CSR_SCAUSE
sd s0, 0(sp)
sd s1, 256(sp)
sd s2, 264(sp)
sd s3, 272(sp)
.endm
.macro restore_context
ld s3, 272(sp)
ld s2, 264(sp)
ld s1, 256(sp)
ld s0, 0(sp)
csrw CSR_SCAUSE, s3
csrw CSR_SSTATUS, s1
csrw CSR_SEPC, s0
ld x31, 248(sp)
ld x30, 240(sp)
ld x29, 232(sp)
ld x28, 224(sp)
ld x27, 216(sp)
ld x26, 208(sp)
ld x25, 200(sp)
ld x24, 192(sp)
ld x23, 184(sp)
ld x22, 176(sp)
ld x21, 168(sp)
ld x20, 160(sp)
ld x19, 152(sp)
ld x18, 144(sp)
ld x17, 136(sp)
ld x16, 128(sp)
ld x15, 120(sp)
ld x14, 112(sp)
ld x13, 104(sp)
ld x12, 96(sp)
ld x11, 88(sp)
ld x10, 80(sp)
ld x9, 72(sp)
ld x8, 64(sp)
ld x7, 56(sp)
ld x6, 48(sp)
ld x5, 40(sp)
ld x4, 32(sp)
ld x3, 24(sp)
ld x2, 16(sp)
ld x1, 8(sp)
addi sp, sp, (8*36)
.endm
.balign 4
.global exception_vectors
exception_vectors:
save_context
move a0, sp
call route_exception
restore_context
sret
@@ -0,0 +1,572 @@
// SPDX-License-Identifier: GPL-2.0
/*
* RISC-V code
*
* Copyright (C) 2021 Western Digital Corporation or its affiliates.
*/
#include <linux/compiler.h>
#include <assert.h>
#include "guest_modes.h"
#include "kvm_util.h"
#include "processor.h"
#include "ucall_common.h"
#define DEFAULT_RISCV_GUEST_STACK_VADDR_MIN 0xac0000
static gva_t exception_handlers;
bool __vcpu_has_ext(struct kvm_vcpu *vcpu, u64 ext)
{
unsigned long value = 0;
int ret;
ret = __vcpu_get_reg(vcpu, ext, &value);
return !ret && !!value;
}
static u64 pte_addr(struct kvm_vm *vm, u64 entry)
{
return ((entry & PGTBL_PTE_ADDR_MASK) >> PGTBL_PTE_ADDR_SHIFT) <<
PGTBL_PAGE_SIZE_SHIFT;
}
static u64 ptrs_per_pte(struct kvm_vm *vm)
{
return PGTBL_PAGE_SIZE / sizeof(u64);
}
static u64 pte_index_mask[] = {
PGTBL_L0_INDEX_MASK,
PGTBL_L1_INDEX_MASK,
PGTBL_L2_INDEX_MASK,
PGTBL_L3_INDEX_MASK,
};
static u32 pte_index_shift[] = {
PGTBL_L0_INDEX_SHIFT,
PGTBL_L1_INDEX_SHIFT,
PGTBL_L2_INDEX_SHIFT,
PGTBL_L3_INDEX_SHIFT,
};
static u64 pte_index(struct kvm_vm *vm, gva_t gva, int level)
{
TEST_ASSERT(level > -1,
"Negative page table level (%d) not possible", level);
TEST_ASSERT(level < vm->mmu.pgtable_levels,
"Invalid page table level (%d)", level);
return (gva & pte_index_mask[level]) >> pte_index_shift[level];
}
void virt_arch_pgd_alloc(struct kvm_vm *vm)
{
size_t nr_pages = vm_page_align(vm, ptrs_per_pte(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;
}
void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa)
{
u64 *ptep, next_ppn;
int level = vm->mmu.pgtable_levels - 1;
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 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) + pte_index(vm, gva, level) * 8;
if (!*ptep) {
next_ppn = vm_alloc_page_table(vm) >> PGTBL_PAGE_SIZE_SHIFT;
*ptep = (next_ppn << PGTBL_PTE_ADDR_SHIFT) |
PGTBL_PTE_VALID_MASK;
}
level--;
while (level > -1) {
ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) +
pte_index(vm, gva, level) * 8;
if (!*ptep && level > 0) {
next_ppn = vm_alloc_page_table(vm) >>
PGTBL_PAGE_SIZE_SHIFT;
*ptep = (next_ppn << PGTBL_PTE_ADDR_SHIFT) |
PGTBL_PTE_VALID_MASK;
}
level--;
}
gpa = gpa >> PGTBL_PAGE_SIZE_SHIFT;
*ptep = (gpa << PGTBL_PTE_ADDR_SHIFT) |
PGTBL_PTE_PERM_MASK | PGTBL_PTE_VALID_MASK;
}
gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva)
{
u64 *ptep;
int level = vm->mmu.pgtable_levels - 1;
if (!vm->mmu.pgd_created)
goto unmapped_gva;
ptep = addr_gpa2hva(vm, vm->mmu.pgd) + pte_index(vm, gva, level) * 8;
if (!ptep)
goto unmapped_gva;
level--;
while (level > -1) {
ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) +
pte_index(vm, gva, level) * 8;
if (!ptep)
goto unmapped_gva;
level--;
}
return pte_addr(vm, *ptep) + (gva & (vm->page_size - 1));
unmapped_gva:
TEST_FAIL("No mapping for vm virtual address gva: 0x%lx level: %d",
gva, level);
exit(1);
}
static void pte_dump(FILE *stream, struct kvm_vm *vm, u8 indent,
u64 page, int level)
{
#ifdef DEBUG
static const char *const type[] = { "pte", "pmd", "pud", "p4d"};
u64 pte, *ptep;
if (level < 0)
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)
{
struct kvm_mmu *mmu = &vm->mmu;
int level = mmu->pgtable_levels - 1;
u64 pgd, *ptep;
if (!mmu->pgd_created)
return;
for (pgd = mmu->pgd; pgd < mmu->pgd + ptrs_per_pte(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 - 1);
}
}
void riscv_vcpu_mmu_setup(struct kvm_vcpu *vcpu)
{
struct kvm_vm *vm = vcpu->vm;
unsigned long satp;
unsigned long satp_mode;
unsigned long max_satp_mode;
/*
* The RISC-V Sv48 MMU mode supports 56-bit physical address
* for 48-bit virtual address with 4KB last level page size.
*/
switch (vm->mode) {
case VM_MODE_P56V57_4K:
case VM_MODE_P50V57_4K:
case VM_MODE_P41V57_4K:
satp_mode = SATP_MODE_57;
break;
case VM_MODE_P56V48_4K:
case VM_MODE_P50V48_4K:
case VM_MODE_P41V48_4K:
satp_mode = SATP_MODE_48;
break;
case VM_MODE_P56V39_4K:
case VM_MODE_P50V39_4K:
case VM_MODE_P41V39_4K:
satp_mode = SATP_MODE_39;
break;
default:
TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode);
}
max_satp_mode = vcpu_get_reg(vcpu, RISCV_CONFIG_REG(satp_mode));
if ((satp_mode >> SATP_MODE_SHIFT) > max_satp_mode)
TEST_FAIL("Unable to set satp mode 0x%lx, max mode 0x%lx\n",
satp_mode >> SATP_MODE_SHIFT, max_satp_mode);
satp = (vm->mmu.pgd >> PGTBL_PAGE_SIZE_SHIFT) & SATP_PPN;
satp |= satp_mode;
vcpu_set_reg(vcpu, RISCV_GENERAL_CSR_REG(satp), satp);
}
void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, u8 indent)
{
struct kvm_riscv_core core;
core.mode = vcpu_get_reg(vcpu, RISCV_CORE_REG(mode));
core.regs.pc = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.pc));
core.regs.ra = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.ra));
core.regs.sp = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.sp));
core.regs.gp = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.gp));
core.regs.tp = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.tp));
core.regs.t0 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t0));
core.regs.t1 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t1));
core.regs.t2 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t2));
core.regs.s0 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s0));
core.regs.s1 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s1));
core.regs.a0 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a0));
core.regs.a1 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a1));
core.regs.a2 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a2));
core.regs.a3 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a3));
core.regs.a4 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a4));
core.regs.a5 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a5));
core.regs.a6 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a6));
core.regs.a7 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a7));
core.regs.s2 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s2));
core.regs.s3 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s3));
core.regs.s4 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s4));
core.regs.s5 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s5));
core.regs.s6 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s6));
core.regs.s7 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s7));
core.regs.s8 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s8));
core.regs.s9 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s9));
core.regs.s10 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s10));
core.regs.s11 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s11));
core.regs.t3 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t3));
core.regs.t4 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t4));
core.regs.t5 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t5));
core.regs.t6 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t6));
fprintf(stream,
" MODE: 0x%lx\n", core.mode);
fprintf(stream,
" PC: 0x%016lx RA: 0x%016lx SP: 0x%016lx GP: 0x%016lx\n",
core.regs.pc, core.regs.ra, core.regs.sp, core.regs.gp);
fprintf(stream,
" TP: 0x%016lx T0: 0x%016lx T1: 0x%016lx T2: 0x%016lx\n",
core.regs.tp, core.regs.t0, core.regs.t1, core.regs.t2);
fprintf(stream,
" S0: 0x%016lx S1: 0x%016lx A0: 0x%016lx A1: 0x%016lx\n",
core.regs.s0, core.regs.s1, core.regs.a0, core.regs.a1);
fprintf(stream,
" A2: 0x%016lx A3: 0x%016lx A4: 0x%016lx A5: 0x%016lx\n",
core.regs.a2, core.regs.a3, core.regs.a4, core.regs.a5);
fprintf(stream,
" A6: 0x%016lx A7: 0x%016lx S2: 0x%016lx S3: 0x%016lx\n",
core.regs.a6, core.regs.a7, core.regs.s2, core.regs.s3);
fprintf(stream,
" S4: 0x%016lx S5: 0x%016lx S6: 0x%016lx S7: 0x%016lx\n",
core.regs.s4, core.regs.s5, core.regs.s6, core.regs.s7);
fprintf(stream,
" S8: 0x%016lx S9: 0x%016lx S10: 0x%016lx S11: 0x%016lx\n",
core.regs.s8, core.regs.s9, core.regs.s10, core.regs.s11);
fprintf(stream,
" T3: 0x%016lx T4: 0x%016lx T5: 0x%016lx T6: 0x%016lx\n",
core.regs.t3, core.regs.t4, core.regs.t5, core.regs.t6);
}
static void __aligned(16) guest_unexp_trap(void)
{
sbi_ecall(KVM_RISCV_SELFTESTS_SBI_EXT,
KVM_RISCV_SELFTESTS_SBI_UNEXP,
0, 0, 0, 0, 0, 0);
}
void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)
{
vcpu_set_reg(vcpu, RISCV_CORE_REG(regs.pc), (unsigned long)guest_code);
}
struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
{
int r;
size_t stack_size;
unsigned long stack_gva;
unsigned long current_gp = 0;
struct kvm_mp_state mps;
struct kvm_vcpu *vcpu;
stack_size = vm->page_size == 4096 ? DEFAULT_STACK_PGS * vm->page_size :
vm->page_size;
stack_gva = __vm_alloc(vm, stack_size,
DEFAULT_RISCV_GUEST_STACK_VADDR_MIN,
MEM_REGION_DATA);
vcpu = __vm_vcpu_add(vm, vcpu_id);
riscv_vcpu_mmu_setup(vcpu);
/*
* With SBI HSM support in KVM RISC-V, all secondary VCPUs are
* powered-off by default so we ensure that all secondary VCPUs
* are powered-on using KVM_SET_MP_STATE ioctl().
*/
mps.mp_state = KVM_MP_STATE_RUNNABLE;
r = __vcpu_ioctl(vcpu, KVM_SET_MP_STATE, &mps);
TEST_ASSERT(!r, "IOCTL KVM_SET_MP_STATE failed (error %d)", r);
/* Setup global pointer of guest to be same as the host */
asm volatile (
"add %0, gp, zero" : "=r" (current_gp) : : "memory");
vcpu_set_reg(vcpu, RISCV_CORE_REG(regs.gp), current_gp);
/* Setup stack pointer and program counter of guest */
vcpu_set_reg(vcpu, RISCV_CORE_REG(regs.sp), stack_gva + stack_size);
/* Setup sscratch for guest_get_vcpuid() */
vcpu_set_reg(vcpu, RISCV_GENERAL_CSR_REG(sscratch), vcpu_id);
/* Setup default exception vector of guest */
vcpu_set_reg(vcpu, RISCV_GENERAL_CSR_REG(stvec), (unsigned long)guest_unexp_trap);
return vcpu;
}
void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...)
{
va_list ap;
u64 id = RISCV_CORE_REG(regs.a0);
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++) {
switch (i) {
case 0:
id = RISCV_CORE_REG(regs.a0);
break;
case 1:
id = RISCV_CORE_REG(regs.a1);
break;
case 2:
id = RISCV_CORE_REG(regs.a2);
break;
case 3:
id = RISCV_CORE_REG(regs.a3);
break;
case 4:
id = RISCV_CORE_REG(regs.a4);
break;
case 5:
id = RISCV_CORE_REG(regs.a5);
break;
case 6:
id = RISCV_CORE_REG(regs.a6);
break;
case 7:
id = RISCV_CORE_REG(regs.a7);
break;
}
vcpu_set_reg(vcpu, id, va_arg(ap, u64));
}
va_end(ap);
}
void kvm_exit_unexpected_exception(int vector, int ec)
{
ucall(UCALL_UNHANDLED, 2, vector, ec);
}
void assert_on_unhandled_exception(struct kvm_vcpu *vcpu)
{
struct ucall uc;
if (get_ucall(vcpu, &uc) == UCALL_UNHANDLED) {
TEST_FAIL("Unexpected exception (vector:0x%lx, ec:0x%lx)",
uc.args[0], uc.args[1]);
}
}
struct handlers {
exception_handler_fn exception_handlers[NR_VECTORS][NR_EXCEPTIONS];
};
void route_exception(struct pt_regs *regs)
{
struct handlers *handlers = (struct handlers *)exception_handlers;
int vector = 0, ec;
ec = regs->cause & ~CAUSE_IRQ_FLAG;
if (ec >= NR_EXCEPTIONS)
goto unexpected_exception;
/* Use the same handler for all the interrupts */
if (regs->cause & CAUSE_IRQ_FLAG) {
vector = 1;
ec = 0;
}
if (handlers && handlers->exception_handlers[vector][ec])
return handlers->exception_handlers[vector][ec](regs);
unexpected_exception:
return kvm_exit_unexpected_exception(vector, ec);
}
void vcpu_init_vector_tables(struct kvm_vcpu *vcpu)
{
extern char exception_vectors;
vcpu_set_reg(vcpu, RISCV_GENERAL_CSR_REG(stvec), (unsigned long)&exception_vectors);
}
void vm_init_vector_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_exception_handler(struct kvm_vm *vm, int vector, exception_handler_fn handler)
{
struct handlers *handlers = addr_gva2hva(vm, vm->handlers);
assert(vector < NR_EXCEPTIONS);
handlers->exception_handlers[0][vector] = handler;
}
void vm_install_interrupt_handler(struct kvm_vm *vm, exception_handler_fn handler)
{
struct handlers *handlers = addr_gva2hva(vm, vm->handlers);
handlers->exception_handlers[1][0] = handler;
}
u32 guest_get_vcpuid(void)
{
return csr_read(CSR_SSCRATCH);
}
struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
unsigned long arg1, unsigned long arg2,
unsigned long arg3, unsigned long arg4,
unsigned long arg5)
{
register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0);
register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1);
register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2);
register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3);
register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4);
register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5);
register uintptr_t a6 asm ("a6") = (uintptr_t)(fid);
register uintptr_t a7 asm ("a7") = (uintptr_t)(ext);
struct sbiret ret;
asm volatile (
"ecall"
: "+r" (a0), "+r" (a1)
: "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7)
: "memory");
ret.error = a0;
ret.value = a1;
return ret;
}
bool guest_sbi_probe_extension(int extid, long *out_val)
{
struct sbiret ret;
ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_PROBE_EXT, extid,
0, 0, 0, 0, 0);
__GUEST_ASSERT(!ret.error || ret.error == SBI_ERR_NOT_SUPPORTED,
"ret.error=%ld, ret.value=%ld\n", ret.error, ret.value);
if (ret.error == SBI_ERR_NOT_SUPPORTED)
return false;
if (out_val)
*out_val = ret.value;
return true;
}
unsigned long get_host_sbi_spec_version(void)
{
struct sbiret ret;
ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_SPEC_VERSION, 0,
0, 0, 0, 0, 0);
GUEST_ASSERT(!ret.error);
return ret.value;
}
void kvm_selftest_arch_init(void)
{
/*
* riscv64 doesn't have a true default mode, so start by detecting the
* supported vm mode.
*/
guest_modes_append_default();
}
unsigned long riscv64_get_satp_mode(void)
{
int kvm_fd, vm_fd, vcpu_fd, err;
u64 val;
struct kvm_one_reg reg = {
.id = RISCV_CONFIG_REG(satp_mode),
.addr = (u64)&val,
};
kvm_fd = open_kvm_dev_path_or_exit();
vm_fd = __kvm_ioctl(kvm_fd, KVM_CREATE_VM, NULL);
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(vcpu_fd, KVM_GET_ONE_REG, &reg);
TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_GET_ONE_REG, vcpu_fd));
close(vcpu_fd);
close(vm_fd);
close(kvm_fd);
return val;
}
bool kvm_arch_has_default_irqchip(void)
{
return kvm_check_cap(KVM_CAP_IRQCHIP);
}
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ucall support. A ucall is a "hypercall to userspace".
*
* Copyright (C) 2021 Western Digital Corporation or its affiliates.
*/
#include <linux/kvm.h>
#include "kvm_util.h"
#include "processor.h"
#include "sbi.h"
void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
{
struct kvm_run *run = vcpu->run;
if (run->exit_reason == KVM_EXIT_RISCV_SBI &&
run->riscv_sbi.extension_id == KVM_RISCV_SELFTESTS_SBI_EXT) {
switch (run->riscv_sbi.function_id) {
case KVM_RISCV_SELFTESTS_SBI_UCALL:
return (void *)run->riscv_sbi.args[0];
case KVM_RISCV_SELFTESTS_SBI_UNEXP:
vcpu_dump(stderr, vcpu, 2);
TEST_ASSERT(0, "Unexpected trap taken by guest");
break;
default:
break;
}
}
return NULL;
}
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Test handler for the s390x DIAGNOSE 0x0318 instruction.
*
* Copyright (C) 2020, IBM
*/
#include "test_util.h"
#include "kvm_util.h"
#define ICPT_INSTRUCTION 0x04
#define IPA0_DIAG 0x8300
static void guest_code(void)
{
u64 diag318_info = 0x12345678;
asm volatile ("diag %0,0,0x318\n" : : "d" (diag318_info));
}
/*
* The DIAGNOSE 0x0318 instruction call must be handled via userspace. As such,
* we create an ad-hoc VM here to handle the instruction then extract the
* necessary data. It is up to the caller to decide what to do with that data.
*/
static u64 diag318_handler(void)
{
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
struct kvm_run *run;
u64 reg;
u64 diag318_info;
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
vcpu_run(vcpu);
run = vcpu->run;
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
TEST_ASSERT(run->s390_sieic.icptcode == ICPT_INSTRUCTION,
"Unexpected intercept code: 0x%x", run->s390_sieic.icptcode);
TEST_ASSERT((run->s390_sieic.ipa & 0xff00) == IPA0_DIAG,
"Unexpected IPA0 code: 0x%x", (run->s390_sieic.ipa & 0xff00));
reg = (run->s390_sieic.ipa & 0x00f0) >> 4;
diag318_info = run->s.regs.gprs[reg];
TEST_ASSERT(diag318_info != 0, "DIAGNOSE 0x0318 info not set");
kvm_vm_free(vm);
return diag318_info;
}
u64 get_diag318_info(void)
{
static u64 diag318_info;
static bool printed_skip;
/*
* If KVM does not support diag318, then return 0 to
* ensure tests do not break.
*/
if (!kvm_has_cap(KVM_CAP_S390_DIAG318)) {
if (!printed_skip) {
fprintf(stdout, "KVM_CAP_S390_DIAG318 not supported. "
"Skipping diag318 test.\n");
printed_skip = true;
}
return 0;
}
/*
* If a test has previously requested the diag318 info,
* then don't bother spinning up a temporary VM again.
*/
if (!diag318_info)
diag318_info = diag318_handler();
return diag318_info;
}
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright IBM Corp. 2024
*
* Authors:
* Hariharan Mari <hari55@linux.ibm.com>
*
* Contains the definition for the global variables to have the test facitlity feature.
*/
#include "facility.h"
u64 stfl_doublewords[NB_STFL_DOUBLEWORDS];
bool stfle_flag;
@@ -0,0 +1,225 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KVM selftest s390x library code - CPU-related functions (page tables...)
*
* Copyright (C) 2019, Red Hat, Inc.
*/
#include "processor.h"
#include "kvm_util.h"
#define PAGES_PER_REGION 4
void virt_arch_pgd_alloc(struct kvm_vm *vm)
{
gpa_t gpa;
TEST_ASSERT(vm->page_size == PAGE_SIZE, "Unsupported page size: 0x%x",
vm->page_size);
if (vm->mmu.pgd_created)
return;
gpa = vm_phy_pages_alloc(vm, PAGES_PER_REGION,
KVM_GUEST_PAGE_TABLE_MIN_PADDR,
vm->memslots[MEM_REGION_PT]);
memset(addr_gpa2hva(vm, gpa), 0xff, PAGES_PER_REGION * vm->page_size);
vm->mmu.pgd = gpa;
vm->mmu.pgd_created = true;
}
/*
* Allocate 4 pages for a region/segment table (ri < 4), or one page for
* a page table (ri == 4). Returns a suitable region/segment table entry
* which points to the freshly allocated pages.
*/
static u64 virt_alloc_region(struct kvm_vm *vm, int ri)
{
u64 taddr;
taddr = vm_phy_pages_alloc(vm, ri < 4 ? PAGES_PER_REGION : 1,
KVM_GUEST_PAGE_TABLE_MIN_PADDR, 0);
memset(addr_gpa2hva(vm, taddr), 0xff, PAGES_PER_REGION * vm->page_size);
return (taddr & REGION_ENTRY_ORIGIN)
| (((4 - ri) << 2) & REGION_ENTRY_TYPE)
| ((ri < 4 ? (PAGES_PER_REGION - 1) : 0) & REGION_ENTRY_LENGTH);
}
void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa)
{
int ri, idx;
u64 *entry;
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",
gva, 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",
gva, vm->max_gfn, vm->page_size);
/* Walk through region and segment tables */
entry = addr_gpa2hva(vm, vm->mmu.pgd);
for (ri = 1; ri <= 4; ri++) {
idx = (gva >> (64 - 11 * ri)) & 0x7ffu;
if (entry[idx] & REGION_ENTRY_INVALID)
entry[idx] = virt_alloc_region(vm, ri);
entry = addr_gpa2hva(vm, entry[idx] & REGION_ENTRY_ORIGIN);
}
/* Fill in page table entry */
idx = (gva >> PAGE_SHIFT) & 0x0ffu; /* page index */
if (!(entry[idx] & PAGE_INVALID))
fprintf(stderr,
"WARNING: PTE for gpa=0x%"PRIx64" already set!\n", gpa);
entry[idx] = gpa;
}
gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva)
{
int ri, idx;
u64 *entry;
TEST_ASSERT(vm->page_size == PAGE_SIZE, "Unsupported page size: 0x%x",
vm->page_size);
entry = addr_gpa2hva(vm, vm->mmu.pgd);
for (ri = 1; ri <= 4; ri++) {
idx = (gva >> (64 - 11 * ri)) & 0x7ffu;
TEST_ASSERT(!(entry[idx] & REGION_ENTRY_INVALID),
"No region mapping for vm virtual address 0x%lx",
gva);
entry = addr_gpa2hva(vm, entry[idx] & REGION_ENTRY_ORIGIN);
}
idx = (gva >> PAGE_SHIFT) & 0x0ffu; /* page index */
TEST_ASSERT(!(entry[idx] & PAGE_INVALID),
"No page mapping for vm virtual address 0x%lx", gva);
return (entry[idx] & ~0xffful) + (gva & 0xffful);
}
static void virt_dump_ptes(FILE *stream, struct kvm_vm *vm, u8 indent,
u64 ptea_start)
{
u64 *pte, ptea;
for (ptea = ptea_start; ptea < ptea_start + 0x100 * 8; ptea += 8) {
pte = addr_gpa2hva(vm, ptea);
if (*pte & PAGE_INVALID)
continue;
fprintf(stream, "%*spte @ 0x%lx: 0x%016lx\n",
indent, "", ptea, *pte);
}
}
static void virt_dump_region(FILE *stream, struct kvm_vm *vm, u8 indent,
u64 reg_tab_addr)
{
u64 addr, *entry;
for (addr = reg_tab_addr; addr < reg_tab_addr + 0x400 * 8; addr += 8) {
entry = addr_gpa2hva(vm, addr);
if (*entry & REGION_ENTRY_INVALID)
continue;
fprintf(stream, "%*srt%lde @ 0x%lx: 0x%016lx\n",
indent, "", 4 - ((*entry & REGION_ENTRY_TYPE) >> 2),
addr, *entry);
if (*entry & REGION_ENTRY_TYPE) {
virt_dump_region(stream, vm, indent + 2,
*entry & REGION_ENTRY_ORIGIN);
} else {
virt_dump_ptes(stream, vm, indent + 2,
*entry & REGION_ENTRY_ORIGIN);
}
}
}
void virt_arch_dump(FILE *stream, struct kvm_vm *vm, u8 indent)
{
if (!vm->mmu.pgd_created)
return;
virt_dump_region(stream, vm, indent, vm->mmu.pgd);
}
void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)
{
vcpu->run->psw_addr = (uintptr_t)guest_code;
}
struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
{
size_t stack_size = DEFAULT_STACK_PGS * getpagesize();
u64 stack_gva;
struct kvm_regs regs;
struct kvm_sregs sregs;
struct kvm_vcpu *vcpu;
TEST_ASSERT(vm->page_size == PAGE_SIZE, "Unsupported page size: 0x%x",
vm->page_size);
stack_gva = __vm_alloc(vm, stack_size, DEFAULT_GUEST_STACK_VADDR_MIN,
MEM_REGION_DATA);
vcpu = __vm_vcpu_add(vm, vcpu_id);
/* Setup guest registers */
vcpu_regs_get(vcpu, &regs);
regs.gprs[15] = stack_gva + (DEFAULT_STACK_PGS * getpagesize()) - 160;
vcpu_regs_set(vcpu, &regs);
vcpu_sregs_get(vcpu, &sregs);
sregs.crs[0] |= 0x00040000; /* Enable floating point regs */
sregs.crs[1] = vm->mmu.pgd | 0xf; /* Primary region table */
vcpu_sregs_set(vcpu, &sregs);
vcpu->run->psw_mask = 0x0400000180000000ULL; /* DAT enabled + 64 bit mode */
return vcpu;
}
void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...)
{
va_list ap;
struct kvm_regs regs;
int i;
TEST_ASSERT(num >= 1 && num <= 5, "Unsupported number of args,\n"
" num: %u",
num);
va_start(ap, num);
vcpu_regs_get(vcpu, &regs);
for (i = 0; i < num; i++)
regs.gprs[i + 2] = va_arg(ap, u64);
vcpu_regs_set(vcpu, &regs);
va_end(ap);
}
void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, u8 indent)
{
fprintf(stream, "%*spstate: psw: 0x%.16llx:0x%.16llx\n",
indent, "", vcpu->run->psw_mask, vcpu->run->psw_addr);
}
void assert_on_unhandled_exception(struct kvm_vcpu *vcpu)
{
}
bool kvm_arch_has_default_irqchip(void)
{
return true;
}
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ucall support. A ucall is a "hypercall to userspace".
*
* Copyright (C) 2019 Red Hat, Inc.
*/
#include "kvm_util.h"
void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
{
struct kvm_run *run = vcpu->run;
if (run->exit_reason == KVM_EXIT_S390_SIEIC &&
run->s390_sieic.icptcode == 4 &&
(run->s390_sieic.ipa >> 8) == 0x83 && /* 0x83 means DIAGNOSE */
(run->s390_sieic.ipb >> 16) == 0x501) {
int reg = run->s390_sieic.ipa & 0xf;
return (void *)run->s.regs.gprs[reg];
}
return NULL;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <stddef.h>
/*
* Override the "basic" built-in string helpers so that they can be used in
* guest code. KVM selftests don't support dynamic loading in guest code and
* will jump into the weeds if the compiler decides to insert an out-of-line
* call via the PLT.
*/
int memcmp(const void *cs, const void *ct, size_t count)
{
const unsigned char *su1, *su2;
int res = 0;
for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) {
if ((res = *su1 - *su2) != 0)
break;
}
return res;
}
void *memcpy(void *dest, const void *src, size_t count)
{
char *tmp = dest;
const char *s = src;
while (count--)
*tmp++ = *s++;
return dest;
}
void *memset(void *s, int c, size_t count)
{
char *xs = s;
while (count--)
*xs++ = c;
return s;
}
size_t strnlen(const char *s, size_t count)
{
const char *sc;
for (sc = s; count-- && *sc != '\0'; ++sc)
/* nothing */;
return sc - s;
}
@@ -0,0 +1,444 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* tools/testing/selftests/kvm/lib/test_util.c
*
* Copyright (C) 2020, Google LLC.
*/
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <linux/mman.h>
#include "linux/kernel.h"
#include "test_util.h"
sigjmp_buf expect_sigbus_jmpbuf;
void __attribute__((used)) expect_sigbus_handler(int signum)
{
siglongjmp(expect_sigbus_jmpbuf, 1);
}
/*
* Random number generator that is usable from guest code. This is the
* Park-Miller LCG using standard constants.
*/
struct guest_random_state new_guest_random_state(u32 seed)
{
struct guest_random_state s = {.seed = seed};
return s;
}
u32 guest_random_u32(struct guest_random_state *state)
{
state->seed = (u64)state->seed * 48271 % ((u32)(1 << 31) - 1);
return state->seed;
}
/*
* Parses "[0-9]+[kmgt]?".
*/
size_t parse_size(const char *size)
{
size_t base;
char *scale;
int shift = 0;
TEST_ASSERT(size && isdigit(size[0]), "Need at least one digit in '%s'", size);
base = strtoull(size, &scale, 0);
TEST_ASSERT(base != ULLONG_MAX, "Overflow parsing size!");
switch (tolower(*scale)) {
case 't':
shift = 40;
break;
case 'g':
shift = 30;
break;
case 'm':
shift = 20;
break;
case 'k':
shift = 10;
break;
case 'b':
case '\0':
shift = 0;
break;
default:
TEST_ASSERT(false, "Unknown size letter %c", *scale);
}
TEST_ASSERT((base << shift) >> shift == base, "Overflow scaling size!");
return base << shift;
}
s64 timespec_to_ns(struct timespec ts)
{
return (s64)ts.tv_nsec + 1000000000LL * (s64)ts.tv_sec;
}
struct timespec timespec_add_ns(struct timespec ts, s64 ns)
{
struct timespec res;
res.tv_nsec = ts.tv_nsec + ns;
res.tv_sec = ts.tv_sec + res.tv_nsec / 1000000000LL;
res.tv_nsec %= 1000000000LL;
return res;
}
struct timespec timespec_add(struct timespec ts1, struct timespec ts2)
{
s64 ns1 = timespec_to_ns(ts1);
s64 ns2 = timespec_to_ns(ts2);
return timespec_add_ns((struct timespec){0}, ns1 + ns2);
}
struct timespec timespec_sub(struct timespec ts1, struct timespec ts2)
{
s64 ns1 = timespec_to_ns(ts1);
s64 ns2 = timespec_to_ns(ts2);
return timespec_add_ns((struct timespec){0}, ns1 - ns2);
}
struct timespec timespec_elapsed(struct timespec start)
{
struct timespec end;
clock_gettime(CLOCK_MONOTONIC, &end);
return timespec_sub(end, start);
}
struct timespec timespec_div(struct timespec ts, int divisor)
{
s64 ns = timespec_to_ns(ts) / divisor;
return timespec_add_ns((struct timespec){0}, ns);
}
void print_skip(const char *fmt, ...)
{
va_list ap;
assert(fmt);
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
puts(", skipping test");
}
static bool test_sysfs_path(const char *path)
{
struct stat statbuf;
int ret;
ret = stat(path, &statbuf);
TEST_ASSERT(ret == 0 || (ret == -1 && errno == ENOENT),
"Error in stat()ing '%s'", path);
return ret == 0;
}
bool thp_configured(void)
{
return test_sysfs_path("/sys/kernel/mm/transparent_hugepage");
}
static size_t get_sysfs_val(const char *path)
{
size_t size;
FILE *f;
int ret;
f = fopen(path, "r");
TEST_ASSERT(f, "Error opening '%s'", path);
ret = fscanf(f, "%ld", &size);
TEST_ASSERT(ret > 0, "Error reading '%s'", path);
/* Re-scan the input stream to verify the entire file was read. */
ret = fscanf(f, "%ld", &size);
TEST_ASSERT(ret < 1, "Error reading '%s'", path);
fclose(f);
return size;
}
size_t get_trans_hugepagesz(void)
{
TEST_ASSERT(thp_configured(), "THP is not configured in host kernel");
return get_sysfs_val("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size");
}
bool is_numa_balancing_enabled(void)
{
if (!test_sysfs_path("/proc/sys/kernel/numa_balancing"))
return false;
return get_sysfs_val("/proc/sys/kernel/numa_balancing") == 1;
}
size_t get_def_hugetlb_pagesz(void)
{
char buf[64];
const char *hugepagesize = "Hugepagesize:";
const char *hugepages_total = "HugePages_Total:";
FILE *f;
f = fopen("/proc/meminfo", "r");
TEST_ASSERT(f != NULL, "Error in opening /proc/meminfo");
while (fgets(buf, sizeof(buf), f) != NULL) {
if (strstr(buf, hugepages_total) == buf) {
unsigned long long total = strtoull(buf + strlen(hugepages_total), NULL, 10);
if (!total) {
fprintf(stderr, "HUGETLB is not enabled in /proc/sys/vm/nr_hugepages\n");
exit(KSFT_SKIP);
}
}
if (strstr(buf, hugepagesize) == buf) {
fclose(f);
return strtoull(buf + strlen(hugepagesize), NULL, 10) << 10;
}
}
if (feof(f)) {
fprintf(stderr, "HUGETLB is not configured in host kernel");
exit(KSFT_SKIP);
}
TEST_FAIL("Error in reading /proc/meminfo");
}
#define ANON_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS)
#define ANON_HUGE_FLAGS (ANON_FLAGS | MAP_HUGETLB)
const struct vm_mem_backing_src_alias *vm_mem_backing_src_alias(u32 i)
{
static const struct vm_mem_backing_src_alias aliases[] = {
[VM_MEM_SRC_ANONYMOUS] = {
.name = "anonymous",
.flag = ANON_FLAGS,
},
[VM_MEM_SRC_ANONYMOUS_THP] = {
.name = "anonymous_thp",
.flag = ANON_FLAGS,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB] = {
.name = "anonymous_hugetlb",
.flag = ANON_HUGE_FLAGS,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB_16KB] = {
.name = "anonymous_hugetlb_16kb",
.flag = ANON_HUGE_FLAGS | MAP_HUGE_16KB,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB_64KB] = {
.name = "anonymous_hugetlb_64kb",
.flag = ANON_HUGE_FLAGS | MAP_HUGE_64KB,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB_512KB] = {
.name = "anonymous_hugetlb_512kb",
.flag = ANON_HUGE_FLAGS | MAP_HUGE_512KB,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB_1MB] = {
.name = "anonymous_hugetlb_1mb",
.flag = ANON_HUGE_FLAGS | MAP_HUGE_1MB,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB_2MB] = {
.name = "anonymous_hugetlb_2mb",
.flag = ANON_HUGE_FLAGS | MAP_HUGE_2MB,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB_8MB] = {
.name = "anonymous_hugetlb_8mb",
.flag = ANON_HUGE_FLAGS | MAP_HUGE_8MB,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB_16MB] = {
.name = "anonymous_hugetlb_16mb",
.flag = ANON_HUGE_FLAGS | MAP_HUGE_16MB,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB_32MB] = {
.name = "anonymous_hugetlb_32mb",
.flag = ANON_HUGE_FLAGS | MAP_HUGE_32MB,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB_256MB] = {
.name = "anonymous_hugetlb_256mb",
.flag = ANON_HUGE_FLAGS | MAP_HUGE_256MB,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB_512MB] = {
.name = "anonymous_hugetlb_512mb",
.flag = ANON_HUGE_FLAGS | MAP_HUGE_512MB,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB_1GB] = {
.name = "anonymous_hugetlb_1gb",
.flag = ANON_HUGE_FLAGS | MAP_HUGE_1GB,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB_2GB] = {
.name = "anonymous_hugetlb_2gb",
.flag = ANON_HUGE_FLAGS | MAP_HUGE_2GB,
},
[VM_MEM_SRC_ANONYMOUS_HUGETLB_16GB] = {
.name = "anonymous_hugetlb_16gb",
.flag = ANON_HUGE_FLAGS | MAP_HUGE_16GB,
},
[VM_MEM_SRC_SHMEM] = {
.name = "shmem",
.flag = MAP_SHARED,
},
[VM_MEM_SRC_SHARED_HUGETLB] = {
.name = "shared_hugetlb",
/*
* No MAP_HUGETLB, we use MFD_HUGETLB instead. Since
* we're using "file backed" memory, we need to specify
* this when the FD is created, not when the area is
* mapped.
*/
.flag = MAP_SHARED,
},
};
_Static_assert(ARRAY_SIZE(aliases) == NUM_SRC_TYPES,
"Missing new backing src types?");
TEST_ASSERT(i < NUM_SRC_TYPES, "Backing src type ID %d too big", i);
return &aliases[i];
}
#define MAP_HUGE_PAGE_SIZE(x) (1ULL << ((x >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK))
size_t get_backing_src_pagesz(u32 i)
{
u32 flag = vm_mem_backing_src_alias(i)->flag;
switch (i) {
case VM_MEM_SRC_ANONYMOUS:
case VM_MEM_SRC_SHMEM:
return getpagesize();
case VM_MEM_SRC_ANONYMOUS_THP:
return get_trans_hugepagesz();
case VM_MEM_SRC_ANONYMOUS_HUGETLB:
case VM_MEM_SRC_SHARED_HUGETLB:
return get_def_hugetlb_pagesz();
default:
return MAP_HUGE_PAGE_SIZE(flag);
}
}
bool is_backing_src_hugetlb(u32 i)
{
return !!(vm_mem_backing_src_alias(i)->flag & MAP_HUGETLB);
}
static void print_available_backing_src_types(const char *prefix)
{
int i;
printf("%sAvailable backing src types:\n", prefix);
for (i = 0; i < NUM_SRC_TYPES; i++)
printf("%s %s\n", prefix, vm_mem_backing_src_alias(i)->name);
}
void backing_src_help(const char *flag)
{
printf(" %s: specify the type of memory that should be used to\n"
" back the guest data region. (default: %s)\n",
flag, vm_mem_backing_src_alias(DEFAULT_VM_MEM_SRC)->name);
print_available_backing_src_types(" ");
}
enum vm_mem_backing_src_type parse_backing_src_type(const char *type_name)
{
int i;
for (i = 0; i < NUM_SRC_TYPES; i++)
if (!strcmp(type_name, vm_mem_backing_src_alias(i)->name))
return i;
print_available_backing_src_types("");
TEST_FAIL("Unknown backing src type: %s", type_name);
return -1;
}
long get_run_delay(void)
{
char path[64];
long val[2];
FILE *fp;
sprintf(path, "/proc/%ld/schedstat", syscall(SYS_gettid));
fp = fopen(path, "r");
/* Return MIN_RUN_DELAY_NS upon failure just to be safe */
if (fscanf(fp, "%ld %ld ", &val[0], &val[1]) < 2)
val[1] = MIN_RUN_DELAY_NS;
fclose(fp);
return val[1];
}
int atoi_paranoid(const char *num_str)
{
char *end_ptr;
long num;
errno = 0;
num = strtol(num_str, &end_ptr, 0);
TEST_ASSERT(!errno, "strtol(\"%s\") failed", num_str);
TEST_ASSERT(num_str != end_ptr,
"strtol(\"%s\") didn't find a valid integer.", num_str);
TEST_ASSERT(*end_ptr == '\0',
"strtol(\"%s\") failed to parse trailing characters \"%s\".",
num_str, end_ptr);
TEST_ASSERT(num >= INT_MIN && num <= INT_MAX,
"%ld not in range of [%d, %d]", num, INT_MIN, INT_MAX);
return num;
}
char *strdup_printf(const char *fmt, ...)
{
va_list ap;
char *str;
va_start(ap, fmt);
TEST_ASSERT(vasprintf(&str, fmt, ap) >= 0, "vasprintf() failed");
va_end(ap);
return str;
}
#define CLOCKSOURCE_PATH "/sys/devices/system/clocksource/clocksource0/current_clocksource"
char *sys_get_cur_clocksource(void)
{
char *clk_name;
struct stat st;
FILE *fp;
fp = fopen(CLOCKSOURCE_PATH, "r");
TEST_ASSERT(fp, "failed to open clocksource file, errno: %d", errno);
TEST_ASSERT(!fstat(fileno(fp), &st), "failed to stat clocksource file, errno: %d",
errno);
clk_name = malloc(st.st_size);
TEST_ASSERT(clk_name, "failed to allocate buffer to read file");
TEST_ASSERT(fgets(clk_name, st.st_size, fp), "failed to read clocksource file: %d",
ferror(fp));
fclose(fp);
return clk_name;
}
@@ -0,0 +1,163 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "linux/types.h"
#include "linux/bitmap.h"
#include "linux/atomic.h"
#include "kvm_util.h"
#include "ucall_common.h"
#define GUEST_UCALL_FAILED -1
struct ucall_header {
DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);
struct ucall ucalls[KVM_MAX_VCPUS];
};
int ucall_nr_pages_required(u64 page_size)
{
return align_up(sizeof(struct ucall_header), page_size) / page_size;
}
/*
* ucall_pool holds per-VM values (global data is duplicated by each VM), it
* must not be accessed from host code.
*/
static struct ucall_header *ucall_pool;
void ucall_init(struct kvm_vm *vm, gpa_t mmio_gpa)
{
struct ucall_header *hdr;
struct ucall *uc;
gva_t gva;
int i;
gva = vm_alloc_shared(vm, sizeof(*hdr), KVM_UTIL_MIN_VADDR,
MEM_REGION_DATA);
hdr = (struct ucall_header *)addr_gva2hva(vm, gva);
memset(hdr, 0, sizeof(*hdr));
for (i = 0; i < KVM_MAX_VCPUS; ++i) {
uc = &hdr->ucalls[i];
uc->hva = uc;
}
write_guest_global(vm, ucall_pool, (struct ucall_header *)gva);
ucall_arch_init(vm, mmio_gpa);
}
static struct ucall *ucall_alloc(void)
{
struct ucall *uc;
int i;
if (!ucall_pool)
goto ucall_failed;
for (i = 0; i < KVM_MAX_VCPUS; ++i) {
if (!test_and_set_bit(i, ucall_pool->in_use)) {
uc = &ucall_pool->ucalls[i];
memset(uc->args, 0, sizeof(uc->args));
return uc;
}
}
ucall_failed:
/*
* If the vCPU cannot grab a ucall structure, make a bare ucall with a
* magic value to signal to get_ucall() that things went sideways.
* GUEST_ASSERT() depends on ucall_alloc() and so cannot be used here.
*/
ucall_arch_do_ucall(GUEST_UCALL_FAILED);
return NULL;
}
static void ucall_free(struct ucall *uc)
{
/* Beware, here be pointer arithmetic. */
clear_bit(uc - ucall_pool->ucalls, ucall_pool->in_use);
}
void ucall_assert(u64 cmd, const char *exp, const char *file,
unsigned int line, const char *fmt, ...)
{
struct ucall *uc;
va_list va;
uc = ucall_alloc();
uc->cmd = cmd;
WRITE_ONCE(uc->args[GUEST_ERROR_STRING], (u64)(exp));
WRITE_ONCE(uc->args[GUEST_FILE], (u64)(file));
WRITE_ONCE(uc->args[GUEST_LINE], line);
va_start(va, fmt);
guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va);
va_end(va);
ucall_arch_do_ucall((gva_t)uc->hva);
ucall_free(uc);
}
void ucall_fmt(u64 cmd, const char *fmt, ...)
{
struct ucall *uc;
va_list va;
uc = ucall_alloc();
uc->cmd = cmd;
va_start(va, fmt);
guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va);
va_end(va);
ucall_arch_do_ucall((gva_t)uc->hva);
ucall_free(uc);
}
void ucall(u64 cmd, int nargs, ...)
{
struct ucall *uc;
va_list va;
int i;
uc = ucall_alloc();
WRITE_ONCE(uc->cmd, cmd);
nargs = min(nargs, UCALL_MAX_ARGS);
va_start(va, nargs);
for (i = 0; i < nargs; ++i)
WRITE_ONCE(uc->args[i], va_arg(va, u64));
va_end(va);
ucall_arch_do_ucall((gva_t)uc->hva);
ucall_free(uc);
}
u64 get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
{
struct ucall ucall;
void *addr;
if (!uc)
uc = &ucall;
addr = ucall_arch_get_ucall(vcpu);
if (addr) {
TEST_ASSERT(addr != (void *)GUEST_UCALL_FAILED,
"Guest failed to allocate ucall struct");
memcpy(uc, addr, sizeof(*uc));
vcpu_run_complete_io(vcpu);
} else {
memset(uc, 0, sizeof(*uc));
}
return uc->cmd;
}
@@ -0,0 +1,206 @@
// SPDX-License-Identifier: GPL-2.0
/*
* KVM userfaultfd util
* Adapted from demand_paging_test.c
*
* Copyright (C) 2018, Red Hat, Inc.
* Copyright (C) 2019-2022 Google LLC
*/
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <poll.h>
#include <pthread.h>
#include <linux/userfaultfd.h>
#include <sys/epoll.h>
#include <sys/syscall.h>
#include "kvm_util.h"
#include "test_util.h"
#include "memstress.h"
#include "userfaultfd_util.h"
#ifdef __NR_userfaultfd
static void *uffd_handler_thread_fn(void *arg)
{
struct uffd_reader_args *reader_args = (struct uffd_reader_args *)arg;
int uffd = reader_args->uffd;
s64 pages = 0;
struct timespec start;
struct timespec ts_diff;
struct epoll_event evt;
int epollfd;
epollfd = epoll_create(1);
TEST_ASSERT(epollfd >= 0, "Failed to create epollfd.");
evt.events = EPOLLIN | EPOLLEXCLUSIVE;
evt.data.u32 = 0;
TEST_ASSERT(!epoll_ctl(epollfd, EPOLL_CTL_ADD, uffd, &evt),
"Failed to add uffd to epollfd");
evt.events = EPOLLIN;
evt.data.u32 = 1;
TEST_ASSERT(!epoll_ctl(epollfd, EPOLL_CTL_ADD, reader_args->pipe, &evt),
"Failed to add pipe to epollfd");
clock_gettime(CLOCK_MONOTONIC, &start);
while (1) {
struct uffd_msg msg;
int r;
r = epoll_wait(epollfd, &evt, 1, -1);
TEST_ASSERT(r == 1,
"Unexpected number of events (%d) from epoll, errno = %d",
r, errno);
if (evt.data.u32 == 1) {
char tmp_chr;
TEST_ASSERT(!(evt.events & (EPOLLERR | EPOLLHUP)),
"Reader thread received EPOLLERR or EPOLLHUP on pipe.");
r = read(reader_args->pipe, &tmp_chr, 1);
TEST_ASSERT(r == 1,
"Error reading pipefd in uffd reader thread");
break;
}
TEST_ASSERT(!(evt.events & (EPOLLERR | EPOLLHUP)),
"Reader thread received EPOLLERR or EPOLLHUP on uffd.");
r = read(uffd, &msg, sizeof(msg));
if (r == -1) {
TEST_ASSERT(errno == EAGAIN,
"Error reading from UFFD: errno = %d", errno);
continue;
}
TEST_ASSERT(r == sizeof(msg),
"Read on uffd returned unexpected number of bytes (%d)", r);
if (!(msg.event & UFFD_EVENT_PAGEFAULT))
continue;
if (reader_args->delay)
usleep(reader_args->delay);
r = reader_args->handler(reader_args->uffd_mode, uffd, &msg);
TEST_ASSERT(r >= 0,
"Reader thread handler fn returned negative value %d", r);
pages++;
}
ts_diff = timespec_elapsed(start);
PER_VCPU_DEBUG("userfaulted %ld pages over %ld.%.9lds. (%f/sec)\n",
pages, ts_diff.tv_sec, ts_diff.tv_nsec,
pages / ((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / NSEC_PER_SEC));
return NULL;
}
struct uffd_desc *uffd_setup_demand_paging(int uffd_mode, useconds_t delay,
void *hva, u64 len,
u64 num_readers,
uffd_handler_t handler)
{
struct uffd_desc *uffd_desc;
bool is_minor = (uffd_mode == UFFDIO_REGISTER_MODE_MINOR);
int uffd;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
u64 expected_ioctls = ((u64)1) << _UFFDIO_COPY;
int ret, i;
PER_PAGE_DEBUG("Userfaultfd %s mode, faults resolved with %s\n",
is_minor ? "MINOR" : "MISSING",
is_minor ? "UFFDIO_CONTINUE" : "UFFDIO_COPY");
uffd_desc = malloc(sizeof(struct uffd_desc));
TEST_ASSERT(uffd_desc, "Failed to malloc uffd descriptor");
uffd_desc->pipefds = calloc(sizeof(int), num_readers);
TEST_ASSERT(uffd_desc->pipefds, "Failed to alloc pipes");
uffd_desc->readers = calloc(sizeof(pthread_t), num_readers);
TEST_ASSERT(uffd_desc->readers, "Failed to alloc reader threads");
uffd_desc->reader_args = calloc(sizeof(struct uffd_reader_args), num_readers);
TEST_ASSERT(uffd_desc->reader_args, "Failed to alloc reader_args");
uffd_desc->num_readers = num_readers;
/* In order to get minor faults, prefault via the alias. */
if (is_minor)
expected_ioctls = ((u64)1) << _UFFDIO_CONTINUE;
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
TEST_ASSERT(uffd >= 0, "uffd creation failed, errno: %d", errno);
uffdio_api.api = UFFD_API;
uffdio_api.features = 0;
TEST_ASSERT(ioctl(uffd, UFFDIO_API, &uffdio_api) != -1,
"ioctl UFFDIO_API failed: %" PRIu64,
(u64)uffdio_api.api);
uffdio_register.range.start = (u64)hva;
uffdio_register.range.len = len;
uffdio_register.mode = uffd_mode;
TEST_ASSERT(ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) != -1,
"ioctl UFFDIO_REGISTER failed");
TEST_ASSERT((uffdio_register.ioctls & expected_ioctls) ==
expected_ioctls, "missing userfaultfd ioctls");
uffd_desc->uffd = uffd;
for (i = 0; i < uffd_desc->num_readers; ++i) {
int pipes[2];
ret = pipe2((int *) &pipes, O_CLOEXEC | O_NONBLOCK);
TEST_ASSERT(!ret, "Failed to set up pipefd %i for uffd_desc %p",
i, uffd_desc);
uffd_desc->pipefds[i] = pipes[1];
uffd_desc->reader_args[i].uffd_mode = uffd_mode;
uffd_desc->reader_args[i].uffd = uffd;
uffd_desc->reader_args[i].delay = delay;
uffd_desc->reader_args[i].handler = handler;
uffd_desc->reader_args[i].pipe = pipes[0];
pthread_create(&uffd_desc->readers[i], NULL, uffd_handler_thread_fn,
&uffd_desc->reader_args[i]);
PER_VCPU_DEBUG("Created uffd thread %i for HVA range [%p, %p)\n",
i, hva, hva + len);
}
return uffd_desc;
}
void uffd_stop_demand_paging(struct uffd_desc *uffd)
{
char c = 0;
int i;
for (i = 0; i < uffd->num_readers; ++i)
TEST_ASSERT(write(uffd->pipefds[i], &c, 1) == 1,
"Unable to write to pipefd %i for uffd_desc %p", i, uffd);
for (i = 0; i < uffd->num_readers; ++i)
TEST_ASSERT(!pthread_join(uffd->readers[i], NULL),
"Pthread_join failed on reader %i for uffd_desc %p", i, uffd);
close(uffd->uffd);
for (i = 0; i < uffd->num_readers; ++i) {
close(uffd->pipefds[i]);
close(uffd->reader_args[i].pipe);
}
free(uffd->pipefds);
free(uffd->readers);
free(uffd->reader_args);
free(uffd);
}
#endif /* __NR_userfaultfd */
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021, Google LLC.
*/
#include "apic.h"
void apic_disable(void)
{
wrmsr(MSR_IA32_APICBASE,
rdmsr(MSR_IA32_APICBASE) &
~(MSR_IA32_APICBASE_ENABLE | MSR_IA32_APICBASE_EXTD));
}
void xapic_enable(void)
{
u64 val = rdmsr(MSR_IA32_APICBASE);
/* Per SDM: to enable xAPIC when in x2APIC must first disable APIC */
if (val & MSR_IA32_APICBASE_EXTD) {
apic_disable();
wrmsr(MSR_IA32_APICBASE,
rdmsr(MSR_IA32_APICBASE) | MSR_IA32_APICBASE_ENABLE);
} else if (!(val & MSR_IA32_APICBASE_ENABLE)) {
wrmsr(MSR_IA32_APICBASE, val | MSR_IA32_APICBASE_ENABLE);
}
/*
* Per SDM: reset value of spurious interrupt vector register has the
* APIC software enabled bit=0. It must be enabled in addition to the
* enable bit in the MSR.
*/
val = xapic_read_reg(APIC_SPIV) | APIC_SPIV_APIC_ENABLED;
xapic_write_reg(APIC_SPIV, val);
}
void x2apic_enable(void)
{
wrmsr(MSR_IA32_APICBASE, rdmsr(MSR_IA32_APICBASE) |
MSR_IA32_APICBASE_ENABLE | MSR_IA32_APICBASE_EXTD);
x2apic_write_reg(APIC_SPIV,
x2apic_read_reg(APIC_SPIV) | APIC_SPIV_APIC_ENABLED);
}
@@ -0,0 +1,81 @@
handle_exception:
push %r15
push %r14
push %r13
push %r12
push %r11
push %r10
push %r9
push %r8
push %rdi
push %rsi
push %rbp
push %rbx
push %rdx
push %rcx
push %rax
mov %rsp, %rdi
call route_exception
pop %rax
pop %rcx
pop %rdx
pop %rbx
pop %rbp
pop %rsi
pop %rdi
pop %r8
pop %r9
pop %r10
pop %r11
pop %r12
pop %r13
pop %r14
pop %r15
/* Discard vector and error code. */
add $16, %rsp
iretq
/*
* Build the handle_exception wrappers which push the vector/error code on the
* stack and an array of pointers to those wrappers.
*/
.pushsection .rodata
.globl idt_handlers
idt_handlers:
.popsection
.macro HANDLERS has_error from to
vector = \from
.rept \to - \from + 1
.align 8
/* Fetch current address and append it to idt_handlers. */
666 :
.pushsection .rodata
.quad 666b
.popsection
.if ! \has_error
pushq $0
.endif
pushq $vector
jmp handle_exception
vector = vector + 1
.endr
.endm
.global idt_handler_code
idt_handler_code:
HANDLERS has_error=0 from=0 to=7
HANDLERS has_error=1 from=8 to=8
HANDLERS has_error=0 from=9 to=9
HANDLERS has_error=1 from=10 to=14
HANDLERS has_error=0 from=15 to=16
HANDLERS has_error=1 from=17 to=17
HANDLERS has_error=0 from=18 to=255
.section .note.GNU-stack, "", %progbits
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Hyper-V specific functions.
*
* Copyright (C) 2021, Red Hat Inc.
*/
#include <stdint.h>
#include "processor.h"
#include "hyperv.h"
const struct kvm_cpuid2 *kvm_get_supported_hv_cpuid(void)
{
static struct kvm_cpuid2 *cpuid;
int kvm_fd;
if (cpuid)
return cpuid;
cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES);
kvm_fd = open_kvm_dev_path_or_exit();
kvm_ioctl(kvm_fd, KVM_GET_SUPPORTED_HV_CPUID, cpuid);
close(kvm_fd);
return cpuid;
}
void vcpu_set_hv_cpuid(struct kvm_vcpu *vcpu)
{
static struct kvm_cpuid2 *cpuid_full;
const struct kvm_cpuid2 *cpuid_sys, *cpuid_hv;
int i, nent = 0;
if (!cpuid_full) {
cpuid_sys = kvm_get_supported_cpuid();
cpuid_hv = kvm_get_supported_hv_cpuid();
cpuid_full = allocate_kvm_cpuid2(cpuid_sys->nent + cpuid_hv->nent);
if (!cpuid_full) {
perror("malloc");
abort();
}
/* Need to skip KVM CPUID leaves 0x400000xx */
for (i = 0; i < cpuid_sys->nent; i++) {
if (cpuid_sys->entries[i].function >= 0x40000000 &&
cpuid_sys->entries[i].function < 0x40000100)
continue;
cpuid_full->entries[nent] = cpuid_sys->entries[i];
nent++;
}
memcpy(&cpuid_full->entries[nent], cpuid_hv->entries,
cpuid_hv->nent * sizeof(struct kvm_cpuid_entry2));
cpuid_full->nent = nent + cpuid_hv->nent;
}
vcpu_init_cpuid(vcpu, cpuid_full);
}
const struct kvm_cpuid2 *vcpu_get_supported_hv_cpuid(struct kvm_vcpu *vcpu)
{
struct kvm_cpuid2 *cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES);
vcpu_ioctl(vcpu, KVM_GET_SUPPORTED_HV_CPUID, cpuid);
return cpuid;
}
bool kvm_hv_cpu_has(struct kvm_x86_cpu_feature feature)
{
if (!kvm_has_cap(KVM_CAP_SYS_HYPERV_CPUID))
return false;
return kvm_cpuid_has(kvm_get_supported_hv_cpuid(), feature);
}
struct hyperv_test_pages *vcpu_alloc_hyperv_test_pages(struct kvm_vm *vm,
gva_t *p_hv_pages_gva)
{
gva_t hv_pages_gva = vm_alloc_page(vm);
struct hyperv_test_pages *hv = addr_gva2hva(vm, hv_pages_gva);
/* Setup of a region of guest memory for the VP Assist page. */
hv->vp_assist = (void *)vm_alloc_page(vm);
hv->vp_assist_hva = addr_gva2hva(vm, (uintptr_t)hv->vp_assist);
hv->vp_assist_gpa = addr_gva2gpa(vm, (uintptr_t)hv->vp_assist);
/* Setup of a region of guest memory for the partition assist page. */
hv->partition_assist = (void *)vm_alloc_page(vm);
hv->partition_assist_hva = addr_gva2hva(vm, (uintptr_t)hv->partition_assist);
hv->partition_assist_gpa = addr_gva2gpa(vm, (uintptr_t)hv->partition_assist);
/* Setup of a region of guest memory for the enlightened VMCS. */
hv->enlightened_vmcs = (void *)vm_alloc_page(vm);
hv->enlightened_vmcs_hva = addr_gva2hva(vm, (uintptr_t)hv->enlightened_vmcs);
hv->enlightened_vmcs_gpa = addr_gva2gpa(vm, (uintptr_t)hv->enlightened_vmcs);
*p_hv_pages_gva = hv_pages_gva;
return hv;
}
int enable_vp_assist(u64 vp_assist_pa, void *vp_assist)
{
u64 val = (vp_assist_pa & HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_MASK) |
HV_X64_MSR_VP_ASSIST_PAGE_ENABLE;
wrmsr(HV_X64_MSR_VP_ASSIST_PAGE, val);
current_vp_assist = vp_assist;
return 0;
}
@@ -0,0 +1,129 @@
// SPDX-License-Identifier: GPL-2.0
/*
* x86-specific extensions to memstress.c.
*
* Copyright (C) 2022, Google, Inc.
*/
#include <stdio.h>
#include <stdlib.h>
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include "test_util.h"
#include "kvm_util.h"
#include "memstress.h"
#include "processor.h"
#include "svm_util.h"
#include "vmx.h"
void memstress_l2_guest_code(u64 vcpu_id)
{
memstress_guest_code(vcpu_id);
vmcall();
}
extern char memstress_l2_guest_entry[];
__asm__(
"memstress_l2_guest_entry:"
" mov (%rsp), %rdi;"
" call memstress_l2_guest_code;"
" ud2;"
);
#define L2_GUEST_STACK_SIZE 64
static void l1_vmx_code(struct vmx_pages *vmx, u64 vcpu_id)
{
unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
unsigned long *rsp;
GUEST_ASSERT(vmx->vmcs_gpa);
GUEST_ASSERT(prepare_for_vmx_operation(vmx));
GUEST_ASSERT(load_vmcs(vmx));
GUEST_ASSERT(ept_1g_pages_supported());
rsp = &l2_guest_stack[L2_GUEST_STACK_SIZE - 1];
*rsp = vcpu_id;
prepare_vmcs(vmx, memstress_l2_guest_entry, rsp);
GUEST_ASSERT(!vmlaunch());
GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_VMCALL);
GUEST_DONE();
}
static void l1_svm_code(struct svm_test_data *svm, u64 vcpu_id)
{
unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
unsigned long *rsp;
rsp = &l2_guest_stack[L2_GUEST_STACK_SIZE - 1];
*rsp = vcpu_id;
generic_svm_setup(svm, memstress_l2_guest_entry, rsp);
run_guest(svm->vmcb, svm->vmcb_gpa);
GUEST_ASSERT_EQ(svm->vmcb->control.exit_code, SVM_EXIT_VMMCALL);
GUEST_DONE();
}
static void memstress_l1_guest_code(void *data, u64 vcpu_id)
{
if (this_cpu_has(X86_FEATURE_VMX))
l1_vmx_code(data, vcpu_id);
else
l1_svm_code(data, vcpu_id);
}
u64 memstress_nested_pages(int nr_vcpus)
{
/*
* 513 page tables is enough to identity-map 256 TiB of L2 with 1G
* pages and 4-level paging, plus a few pages per-vCPU for data
* structures such as the VMCS.
*/
return 513 + 10 * nr_vcpus;
}
static void memstress_setup_ept_mappings(struct kvm_vm *vm)
{
u64 start, end;
/*
* Identity map the first 4G and the test region with 1G pages so that
* KVM can shadow the EPT12 with the maximum huge page size supported
* by the backing source.
*/
tdp_identity_map_1g(vm, 0, 0x100000000ULL);
start = align_down(memstress_args.gpa, PG_SIZE_1G);
end = align_up(memstress_args.gpa + memstress_args.size, PG_SIZE_1G);
tdp_identity_map_1g(vm, start, end - start);
}
void memstress_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu *vcpus[])
{
struct kvm_regs regs;
gva_t nested_gva;
int vcpu_id;
TEST_REQUIRE(kvm_cpu_has_tdp());
vm_enable_tdp(vm);
memstress_setup_ept_mappings(vm);
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) {
if (kvm_cpu_has(X86_FEATURE_VMX))
vcpu_alloc_vmx(vm, &nested_gva);
else
vcpu_alloc_svm(vm, &nested_gva);
/*
* Override the vCPU to run memstress_l1_guest_code() which will
* bounce it into L2 before calling memstress_guest_code().
*/
vcpu_regs_get(vcpus[vcpu_id], &regs);
regs.rip = (unsigned long) memstress_l1_guest_code;
vcpu_regs_set(vcpus[vcpu_id], &regs);
vcpu_args_set(vcpus[vcpu_id], 2, nested_gva, vcpu_id);
}
}
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2023, Tencent, Inc.
*/
#include <stdint.h>
#include <linux/kernel.h>
#include "kvm_util.h"
#include "processor.h"
#include "pmu.h"
const u64 intel_pmu_arch_events[] = {
INTEL_ARCH_CPU_CYCLES,
INTEL_ARCH_INSTRUCTIONS_RETIRED,
INTEL_ARCH_REFERENCE_CYCLES,
INTEL_ARCH_LLC_REFERENCES,
INTEL_ARCH_LLC_MISSES,
INTEL_ARCH_BRANCHES_RETIRED,
INTEL_ARCH_BRANCHES_MISPREDICTED,
INTEL_ARCH_TOPDOWN_SLOTS,
INTEL_ARCH_TOPDOWN_BE_BOUND,
INTEL_ARCH_TOPDOWN_BAD_SPEC,
INTEL_ARCH_TOPDOWN_FE_BOUND,
INTEL_ARCH_TOPDOWN_RETIRING,
INTEL_ARCH_LBR_INSERTS,
};
kvm_static_assert(ARRAY_SIZE(intel_pmu_arch_events) == NR_INTEL_ARCH_EVENTS);
const u64 amd_pmu_zen_events[] = {
AMD_ZEN_CORE_CYCLES,
AMD_ZEN_INSTRUCTIONS_RETIRED,
AMD_ZEN_BRANCHES_RETIRED,
AMD_ZEN_BRANCHES_MISPREDICTED,
};
kvm_static_assert(ARRAY_SIZE(amd_pmu_zen_events) == NR_AMD_ZEN_EVENTS);
/*
* For Intel Atom CPUs, the PMU events "Instruction Retired" or
* "Branch Instruction Retired" may be overcounted for some certain
* instructions, like FAR CALL/JMP, RETF, IRET, VMENTRY/VMEXIT/VMPTRLD
* and complex SGX/SMX/CSTATE instructions/flows.
*
* The detailed information can be found in the errata (section SRF7):
* https://edc.intel.com/content/www/us/en/design/products-and-solutions/processors-and-chipsets/sierra-forest/xeon-6700-series-processor-with-e-cores-specification-update/errata-details/
*
* For the Atom platforms before Sierra Forest (including Sierra Forest),
* Both 2 events "Instruction Retired" and "Branch Instruction Retired" would
* be overcounted on these certain instructions, but for Clearwater Forest
* only "Instruction Retired" event is overcounted on these instructions.
*/
static u64 get_pmu_errata(void)
{
if (!this_cpu_is_intel())
return 0;
if (this_cpu_family() != 0x6)
return 0;
switch (this_cpu_model()) {
case 0xDD: /* Clearwater Forest */
return BIT_ULL(INSTRUCTIONS_RETIRED_OVERCOUNT);
case 0xAF: /* Sierra Forest */
case 0x4D: /* Avaton, Rangely */
case 0x5F: /* Denverton */
case 0x86: /* Jacobsville */
return BIT_ULL(INSTRUCTIONS_RETIRED_OVERCOUNT) |
BIT_ULL(BRANCHES_RETIRED_OVERCOUNT);
default:
return 0;
}
}
u64 pmu_errata_mask;
void kvm_init_pmu_errata(void)
{
pmu_errata_mask = get_pmu_errata();
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,199 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <stdint.h>
#include <stdbool.h>
#include "sev.h"
/*
* sparsebit_next_clear() can return 0 if [x, 2**64-1] are all set, and the
* -1 would then cause an underflow back to 2**64 - 1. This is expected and
* correct.
*
* If the last range in the sparsebit is [x, y] and we try to iterate,
* sparsebit_next_set() will return 0, and sparsebit_next_clear() will try
* and find the first range, but that's correct because the condition
* expression would cause us to quit the loop.
*/
static void encrypt_region(struct kvm_vm *vm, struct userspace_mem_region *region,
u8 page_type, bool private)
{
const struct sparsebit *protected_phy_pages = region->protected_phy_pages;
const gpa_t gpa_base = region->region.guest_phys_addr;
const sparsebit_idx_t lowest_page_in_region = gpa_base >> vm->page_shift;
sparsebit_idx_t i, j;
if (!sparsebit_any_set(protected_phy_pages))
return;
if (!is_sev_snp_vm(vm))
sev_register_encrypted_memory(vm, region);
sparsebit_for_each_set_range(protected_phy_pages, i, j) {
const u64 size = (j - i + 1) * vm->page_size;
const u64 offset = (i - lowest_page_in_region) * vm->page_size;
if (private)
vm_mem_set_private(vm, gpa_base + offset, size);
if (is_sev_snp_vm(vm))
snp_launch_update_data(vm, gpa_base + offset,
(u64)addr_gpa2hva(vm, gpa_base + offset),
size, page_type);
else
sev_launch_update_data(vm, gpa_base + offset, size);
}
}
void sev_vm_init(struct kvm_vm *vm)
{
if (vm->type == KVM_X86_DEFAULT_VM) {
TEST_ASSERT_EQ(vm->arch.sev_fd, -1);
vm->arch.sev_fd = open_sev_dev_path_or_exit();
vm_sev_ioctl(vm, KVM_SEV_INIT, NULL);
} else {
struct kvm_sev_init init = { 0 };
TEST_ASSERT_EQ(vm->type, KVM_X86_SEV_VM);
vm_sev_ioctl(vm, KVM_SEV_INIT2, &init);
}
}
void sev_es_vm_init(struct kvm_vm *vm)
{
if (vm->type == KVM_X86_DEFAULT_VM) {
TEST_ASSERT_EQ(vm->arch.sev_fd, -1);
vm->arch.sev_fd = open_sev_dev_path_or_exit();
vm_sev_ioctl(vm, KVM_SEV_ES_INIT, NULL);
} else {
struct kvm_sev_init init = { 0 };
TEST_ASSERT_EQ(vm->type, KVM_X86_SEV_ES_VM);
vm_sev_ioctl(vm, KVM_SEV_INIT2, &init);
}
}
void snp_vm_init(struct kvm_vm *vm)
{
struct kvm_sev_init init = { 0 };
TEST_ASSERT_EQ(vm->type, KVM_X86_SNP_VM);
vm_sev_ioctl(vm, KVM_SEV_INIT2, &init);
}
void sev_vm_launch(struct kvm_vm *vm, u32 policy)
{
struct kvm_sev_launch_start launch_start = {
.policy = policy,
};
struct userspace_mem_region *region;
struct kvm_sev_guest_status status;
int ctr;
vm_sev_ioctl(vm, KVM_SEV_LAUNCH_START, &launch_start);
vm_sev_ioctl(vm, KVM_SEV_GUEST_STATUS, &status);
TEST_ASSERT_EQ(status.policy, policy);
TEST_ASSERT_EQ(status.state, SEV_GUEST_STATE_LAUNCH_UPDATE);
hash_for_each(vm->regions.slot_hash, ctr, region, slot_node)
encrypt_region(vm, region, KVM_SEV_PAGE_TYPE_INVALID, false);
if (policy & SEV_POLICY_ES)
vm_sev_ioctl(vm, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL);
vm->arch.is_pt_protected = true;
}
void sev_vm_launch_measure(struct kvm_vm *vm, u8 *measurement)
{
struct kvm_sev_launch_measure launch_measure;
struct kvm_sev_guest_status guest_status;
launch_measure.len = 256;
launch_measure.uaddr = (__u64)measurement;
vm_sev_ioctl(vm, KVM_SEV_LAUNCH_MEASURE, &launch_measure);
vm_sev_ioctl(vm, KVM_SEV_GUEST_STATUS, &guest_status);
TEST_ASSERT_EQ(guest_status.state, SEV_GUEST_STATE_LAUNCH_SECRET);
}
void sev_vm_launch_finish(struct kvm_vm *vm)
{
struct kvm_sev_guest_status status;
vm_sev_ioctl(vm, KVM_SEV_GUEST_STATUS, &status);
TEST_ASSERT(status.state == SEV_GUEST_STATE_LAUNCH_UPDATE ||
status.state == SEV_GUEST_STATE_LAUNCH_SECRET,
"Unexpected guest state: %d", status.state);
vm_sev_ioctl(vm, KVM_SEV_LAUNCH_FINISH, NULL);
vm_sev_ioctl(vm, KVM_SEV_GUEST_STATUS, &status);
TEST_ASSERT_EQ(status.state, SEV_GUEST_STATE_RUNNING);
}
void snp_vm_launch_start(struct kvm_vm *vm, u64 policy)
{
struct kvm_sev_snp_launch_start launch_start = {
.policy = policy,
};
vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_START, &launch_start);
}
void snp_vm_launch_update(struct kvm_vm *vm)
{
struct userspace_mem_region *region;
int ctr;
hash_for_each(vm->regions.slot_hash, ctr, region, slot_node)
encrypt_region(vm, region, KVM_SEV_SNP_PAGE_TYPE_NORMAL, true);
vm->arch.is_pt_protected = true;
}
void snp_vm_launch_finish(struct kvm_vm *vm)
{
struct kvm_sev_snp_launch_finish launch_finish = { 0 };
vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_FINISH, &launch_finish);
}
struct kvm_vm *vm_sev_create_with_one_vcpu(u32 type, void *guest_code,
struct kvm_vcpu **cpu)
{
struct vm_shape shape = {
.mode = VM_MODE_DEFAULT,
.type = type,
};
struct kvm_vm *vm;
struct kvm_vcpu *cpus[1];
vm = __vm_create_with_vcpus(shape, 1, 0, guest_code, cpus);
*cpu = cpus[0];
return vm;
}
void vm_sev_launch(struct kvm_vm *vm, u64 policy, u8 *measurement)
{
if (is_sev_snp_vm(vm)) {
vm_enable_cap(vm, KVM_CAP_EXIT_HYPERCALL, BIT(KVM_HC_MAP_GPA_RANGE));
snp_vm_launch_start(vm, policy);
snp_vm_launch_update(vm);
snp_vm_launch_finish(vm);
return;
}
sev_vm_launch(vm, policy);
if (!measurement)
measurement = alloca(256);
sev_vm_launch_measure(vm, measurement);
sev_vm_launch_finish(vm);
}
@@ -0,0 +1,190 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Helpers used for nested SVM testing
* Largely inspired from KVM unit test svm.c
*
* Copyright (C) 2020, Red Hat, Inc.
*/
#include "test_util.h"
#include "kvm_util.h"
#include "processor.h"
#include "svm_util.h"
#define SEV_DEV_PATH "/dev/sev"
struct gpr64_regs guest_regs;
u64 rflags;
/* Allocate memory regions for nested SVM tests.
*
* Input Args:
* vm - The VM to allocate guest-virtual addresses in.
*
* Output Args:
* p_svm_gva - The guest virtual address for the struct svm_test_data.
*
* Return:
* Pointer to structure with the addresses of the SVM areas.
*/
struct svm_test_data *
vcpu_alloc_svm(struct kvm_vm *vm, gva_t *p_svm_gva)
{
gva_t svm_gva = vm_alloc_page(vm);
struct svm_test_data *svm = addr_gva2hva(vm, svm_gva);
svm->vmcb = (void *)vm_alloc_page(vm);
svm->vmcb_hva = addr_gva2hva(vm, (uintptr_t)svm->vmcb);
svm->vmcb_gpa = addr_gva2gpa(vm, (uintptr_t)svm->vmcb);
svm->save_area = (void *)vm_alloc_page(vm);
svm->save_area_hva = addr_gva2hva(vm, (uintptr_t)svm->save_area);
svm->save_area_gpa = addr_gva2gpa(vm, (uintptr_t)svm->save_area);
svm->msr = (void *)vm_alloc_page(vm);
svm->msr_hva = addr_gva2hva(vm, (uintptr_t)svm->msr);
svm->msr_gpa = addr_gva2gpa(vm, (uintptr_t)svm->msr);
memset(svm->msr_hva, 0, getpagesize());
if (vm->stage2_mmu.pgd_created)
svm->ncr3_gpa = vm->stage2_mmu.pgd;
*p_svm_gva = svm_gva;
return svm;
}
static void vmcb_set_seg(struct vmcb_seg *seg, u16 selector,
u64 base, u32 limit, u32 attr)
{
seg->selector = selector;
seg->attrib = attr;
seg->limit = limit;
seg->base = base;
}
void vm_enable_npt(struct kvm_vm *vm)
{
struct pte_masks pte_masks;
TEST_ASSERT(kvm_cpu_has_npt(), "KVM doesn't supported nested NPT");
/*
* NPTs use the same PTE format, but deliberately drop the C-bit as the
* per-VM shared vs. private information is only meant for stage-1.
*/
pte_masks = vm->mmu.arch.pte_masks;
pte_masks.c = 0;
/* NPT walks are treated as user accesses, so set the 'user' bit. */
pte_masks.always_set = pte_masks.user;
tdp_mmu_init(vm, vm->mmu.pgtable_levels, &pte_masks);
}
void generic_svm_setup(struct svm_test_data *svm, void *guest_rip, void *guest_rsp)
{
struct vmcb *vmcb = svm->vmcb;
u64 vmcb_gpa = svm->vmcb_gpa;
struct vmcb_save_area *save = &vmcb->save;
struct vmcb_control_area *ctrl = &vmcb->control;
u32 data_seg_attr = 3 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
| SVM_SELECTOR_DB_MASK | SVM_SELECTOR_G_MASK;
u32 code_seg_attr = 9 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
| SVM_SELECTOR_L_MASK | SVM_SELECTOR_G_MASK;
u64 efer;
efer = rdmsr(MSR_EFER);
wrmsr(MSR_EFER, efer | EFER_SVME);
wrmsr(MSR_VM_HSAVE_PA, svm->save_area_gpa);
memset(vmcb, 0, sizeof(*vmcb));
asm volatile ("vmsave %0\n\t" : : "a" (vmcb_gpa) : "memory");
vmcb_set_seg(&save->es, get_es(), 0, -1U, data_seg_attr);
vmcb_set_seg(&save->cs, get_cs(), 0, -1U, code_seg_attr);
vmcb_set_seg(&save->ss, get_ss(), 0, -1U, data_seg_attr);
vmcb_set_seg(&save->ds, get_ds(), 0, -1U, data_seg_attr);
vmcb_set_seg(&save->gdtr, 0, get_gdt().address, get_gdt().size, 0);
vmcb_set_seg(&save->idtr, 0, get_idt().address, get_idt().size, 0);
ctrl->asid = 1;
save->cpl = 0;
save->efer = rdmsr(MSR_EFER);
asm volatile ("mov %%cr4, %0" : "=r"(save->cr4) : : "memory");
asm volatile ("mov %%cr3, %0" : "=r"(save->cr3) : : "memory");
asm volatile ("mov %%cr0, %0" : "=r"(save->cr0) : : "memory");
asm volatile ("mov %%dr7, %0" : "=r"(save->dr7) : : "memory");
asm volatile ("mov %%dr6, %0" : "=r"(save->dr6) : : "memory");
asm volatile ("mov %%cr2, %0" : "=r"(save->cr2) : : "memory");
save->g_pat = rdmsr(MSR_IA32_CR_PAT);
save->dbgctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
ctrl->intercept = (1ULL << INTERCEPT_VMRUN) |
(1ULL << INTERCEPT_VMMCALL);
ctrl->msrpm_base_pa = svm->msr_gpa;
vmcb->save.rip = (u64)guest_rip;
vmcb->save.rsp = (u64)guest_rsp;
guest_regs.rdi = (u64)svm;
if (svm->ncr3_gpa) {
ctrl->misc_ctl |= SVM_MISC_ENABLE_NP;
ctrl->nested_cr3 = svm->ncr3_gpa;
}
}
/*
* save/restore 64-bit general registers except rax, rip, rsp
* which are directly handed through the VMCB guest processor state
*/
#define SAVE_GPR_C \
"xchg %%rbx, guest_regs+0x20\n\t" \
"xchg %%rcx, guest_regs+0x10\n\t" \
"xchg %%rdx, guest_regs+0x18\n\t" \
"xchg %%rbp, guest_regs+0x30\n\t" \
"xchg %%rsi, guest_regs+0x38\n\t" \
"xchg %%rdi, guest_regs+0x40\n\t" \
"xchg %%r8, guest_regs+0x48\n\t" \
"xchg %%r9, guest_regs+0x50\n\t" \
"xchg %%r10, guest_regs+0x58\n\t" \
"xchg %%r11, guest_regs+0x60\n\t" \
"xchg %%r12, guest_regs+0x68\n\t" \
"xchg %%r13, guest_regs+0x70\n\t" \
"xchg %%r14, guest_regs+0x78\n\t" \
"xchg %%r15, guest_regs+0x80\n\t"
#define LOAD_GPR_C SAVE_GPR_C
/*
* selftests do not use interrupts so we dropped clgi/sti/cli/stgi
* for now. registers involved in LOAD/SAVE_GPR_C are eventually
* unmodified so they do not need to be in the clobber list.
*/
void run_guest(struct vmcb *vmcb, u64 vmcb_gpa)
{
asm volatile (
"vmload %[vmcb_gpa]\n\t"
"mov rflags, %%r15\n\t" // rflags
"mov %%r15, 0x170(%[vmcb])\n\t"
"mov guest_regs, %%r15\n\t" // rax
"mov %%r15, 0x1f8(%[vmcb])\n\t"
LOAD_GPR_C
"vmrun %[vmcb_gpa]\n\t"
SAVE_GPR_C
"mov 0x170(%[vmcb]), %%r15\n\t" // rflags
"mov %%r15, rflags\n\t"
"mov 0x1f8(%[vmcb]), %%r15\n\t" // rax
"mov %%r15, guest_regs\n\t"
"vmsave %[vmcb_gpa]\n\t"
: : [vmcb] "r" (vmcb), [vmcb_gpa] "a" (vmcb_gpa)
: "r15", "memory");
}
/*
* Open SEV_DEV_PATH if available, otherwise exit the entire program.
*
* Return:
* The opened file descriptor of /dev/sev.
*/
int open_sev_dev_path_or_exit(void)
{
return open_path_or_exit(SEV_DEV_PATH, 0);
}
@@ -0,0 +1,56 @@
// 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"
#define UCALL_PIO_PORT ((u16)0x1000)
void ucall_arch_do_ucall(gva_t uc)
{
/*
* FIXME: Revert this hack (the entire commit that added it) once nVMX
* preserves L2 GPRs across a nested VM-Exit. If a ucall from L2, e.g.
* to do a GUEST_SYNC(), lands the vCPU in L1, any and all GPRs can be
* clobbered by L1. Save and restore non-volatile GPRs (clobbering RBP
* in particular is problematic) along with RDX and RDI (which are
* inputs), and clobber volatile GPRs. *sigh*
*/
#define HORRIFIC_L2_UCALL_CLOBBER_HACK \
"rcx", "rsi", "r8", "r9", "r10", "r11"
asm volatile("push %%rbp\n\t"
"push %%r15\n\t"
"push %%r14\n\t"
"push %%r13\n\t"
"push %%r12\n\t"
"push %%rbx\n\t"
"push %%rdx\n\t"
"push %%rdi\n\t"
"in %[port], %%al\n\t"
"pop %%rdi\n\t"
"pop %%rdx\n\t"
"pop %%rbx\n\t"
"pop %%r12\n\t"
"pop %%r13\n\t"
"pop %%r14\n\t"
"pop %%r15\n\t"
"pop %%rbp\n\t"
: : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory",
HORRIFIC_L2_UCALL_CLOBBER_HACK);
}
void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
{
struct kvm_run *run = vcpu->run;
if (run->exit_reason == KVM_EXIT_IO && run->io.port == UCALL_PIO_PORT) {
struct kvm_regs regs;
vcpu_regs_get(vcpu, &regs);
return (void *)regs.rdi;
}
return NULL;
}
@@ -0,0 +1,396 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2018, Google LLC.
*/
#include <asm/msr-index.h>
#include "test_util.h"
#include "kvm_util.h"
#include "processor.h"
#include "vmx.h"
#define KVM_EPT_PAGE_TABLE_MIN_PADDR 0x1c0000
#define EPTP_MT_SHIFT 0 /* EPTP memtype bits 2:0 */
#define EPTP_PWL_SHIFT 3 /* EPTP page walk length bits 5:3 */
#define EPTP_AD_ENABLED_SHIFT 6 /* EPTP AD enabled bit 6 */
#define EPTP_WB (X86_MEMTYPE_WB << EPTP_MT_SHIFT)
#define EPTP_PWL_4 (3ULL << EPTP_PWL_SHIFT) /* PWL is (levels - 1) */
#define EPTP_AD_ENABLED (1ULL << EPTP_AD_ENABLED_SHIFT)
bool enable_evmcs;
struct hv_enlightened_vmcs *current_evmcs;
struct hv_vp_assist_page *current_vp_assist;
int vcpu_enable_evmcs(struct kvm_vcpu *vcpu)
{
u16 evmcs_ver;
vcpu_enable_cap(vcpu, KVM_CAP_HYPERV_ENLIGHTENED_VMCS,
(unsigned long)&evmcs_ver);
/* KVM should return supported EVMCS version range */
TEST_ASSERT(((evmcs_ver >> 8) >= (evmcs_ver & 0xff)) &&
(evmcs_ver & 0xff) > 0,
"Incorrect EVMCS version range: %x:%x",
evmcs_ver & 0xff, evmcs_ver >> 8);
return evmcs_ver;
}
void vm_enable_ept(struct kvm_vm *vm)
{
struct pte_masks pte_masks;
TEST_ASSERT(kvm_cpu_has_ept(), "KVM doesn't support nested EPT");
/*
* EPTs do not have 'present' or 'user' bits, instead bit 0 is the
* 'readable' bit.
*/
pte_masks = (struct pte_masks) {
.present = 0,
.user = 0,
.readable = BIT_ULL(0),
.writable = BIT_ULL(1),
.executable = BIT_ULL(2),
.huge = BIT_ULL(7),
.accessed = BIT_ULL(8),
.dirty = BIT_ULL(9),
.nx = 0,
};
/* TODO: Add support for 5-level EPT. */
tdp_mmu_init(vm, 4, &pte_masks);
}
/* Allocate memory regions for nested VMX tests.
*
* Input Args:
* vm - The VM to allocate guest-virtual addresses in.
*
* Output Args:
* p_vmx_gva - The guest virtual address for the struct vmx_pages.
*
* Return:
* Pointer to structure with the addresses of the VMX areas.
*/
struct vmx_pages *
vcpu_alloc_vmx(struct kvm_vm *vm, gva_t *p_vmx_gva)
{
gva_t vmx_gva = vm_alloc_page(vm);
struct vmx_pages *vmx = addr_gva2hva(vm, vmx_gva);
/* Setup of a region of guest memory for the vmxon region. */
vmx->vmxon = (void *)vm_alloc_page(vm);
vmx->vmxon_hva = addr_gva2hva(vm, (uintptr_t)vmx->vmxon);
vmx->vmxon_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->vmxon);
/* Setup of a region of guest memory for a vmcs. */
vmx->vmcs = (void *)vm_alloc_page(vm);
vmx->vmcs_hva = addr_gva2hva(vm, (uintptr_t)vmx->vmcs);
vmx->vmcs_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->vmcs);
/* Setup of a region of guest memory for the MSR bitmap. */
vmx->msr = (void *)vm_alloc_page(vm);
vmx->msr_hva = addr_gva2hva(vm, (uintptr_t)vmx->msr);
vmx->msr_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->msr);
memset(vmx->msr_hva, 0, getpagesize());
/* Setup of a region of guest memory for the shadow VMCS. */
vmx->shadow_vmcs = (void *)vm_alloc_page(vm);
vmx->shadow_vmcs_hva = addr_gva2hva(vm, (uintptr_t)vmx->shadow_vmcs);
vmx->shadow_vmcs_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->shadow_vmcs);
/* Setup of a region of guest memory for the VMREAD and VMWRITE bitmaps. */
vmx->vmread = (void *)vm_alloc_page(vm);
vmx->vmread_hva = addr_gva2hva(vm, (uintptr_t)vmx->vmread);
vmx->vmread_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->vmread);
memset(vmx->vmread_hva, 0, getpagesize());
vmx->vmwrite = (void *)vm_alloc_page(vm);
vmx->vmwrite_hva = addr_gva2hva(vm, (uintptr_t)vmx->vmwrite);
vmx->vmwrite_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->vmwrite);
memset(vmx->vmwrite_hva, 0, getpagesize());
if (vm->stage2_mmu.pgd_created)
vmx->eptp_gpa = vm->stage2_mmu.pgd;
*p_vmx_gva = vmx_gva;
return vmx;
}
bool prepare_for_vmx_operation(struct vmx_pages *vmx)
{
u64 feature_control;
u64 required;
unsigned long cr0;
unsigned long cr4;
/*
* Ensure bits in CR0 and CR4 are valid in VMX operation:
* - Bit X is 1 in _FIXED0: bit X is fixed to 1 in CRx.
* - Bit X is 0 in _FIXED1: bit X is fixed to 0 in CRx.
*/
__asm__ __volatile__("mov %%cr0, %0" : "=r"(cr0) : : "memory");
cr0 &= rdmsr(MSR_IA32_VMX_CR0_FIXED1);
cr0 |= rdmsr(MSR_IA32_VMX_CR0_FIXED0);
__asm__ __volatile__("mov %0, %%cr0" : : "r"(cr0) : "memory");
__asm__ __volatile__("mov %%cr4, %0" : "=r"(cr4) : : "memory");
cr4 &= rdmsr(MSR_IA32_VMX_CR4_FIXED1);
cr4 |= rdmsr(MSR_IA32_VMX_CR4_FIXED0);
/* Enable VMX operation */
cr4 |= X86_CR4_VMXE;
__asm__ __volatile__("mov %0, %%cr4" : : "r"(cr4) : "memory");
/*
* Configure IA32_FEATURE_CONTROL MSR to allow VMXON:
* Bit 0: Lock bit. If clear, VMXON causes a #GP.
* Bit 2: Enables VMXON outside of SMX operation. If clear, VMXON
* outside of SMX causes a #GP.
*/
required = FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX;
required |= FEAT_CTL_LOCKED;
feature_control = rdmsr(MSR_IA32_FEAT_CTL);
if ((feature_control & required) != required)
wrmsr(MSR_IA32_FEAT_CTL, feature_control | required);
/* Enter VMX root operation. */
*(u32 *)(vmx->vmxon) = vmcs_revision();
if (vmxon(vmx->vmxon_gpa))
return false;
return true;
}
bool load_vmcs(struct vmx_pages *vmx)
{
/* Load a VMCS. */
*(u32 *)(vmx->vmcs) = vmcs_revision();
if (vmclear(vmx->vmcs_gpa))
return false;
if (vmptrld(vmx->vmcs_gpa))
return false;
/* Setup shadow VMCS, do not load it yet. */
*(u32 *)(vmx->shadow_vmcs) = vmcs_revision() | 0x80000000ul;
if (vmclear(vmx->shadow_vmcs_gpa))
return false;
return true;
}
static bool ept_vpid_cap_supported(u64 mask)
{
return rdmsr(MSR_IA32_VMX_EPT_VPID_CAP) & mask;
}
bool ept_1g_pages_supported(void)
{
return ept_vpid_cap_supported(VMX_EPT_VPID_CAP_1G_PAGES);
}
/*
* Initialize the control fields to the most basic settings possible.
*/
static inline void init_vmcs_control_fields(struct vmx_pages *vmx)
{
u32 sec_exec_ctl = 0;
vmwrite(VIRTUAL_PROCESSOR_ID, 0);
vmwrite(POSTED_INTR_NV, 0);
vmwrite(PIN_BASED_VM_EXEC_CONTROL, rdmsr(MSR_IA32_VMX_TRUE_PINBASED_CTLS));
if (vmx->eptp_gpa) {
u64 eptp = vmx->eptp_gpa | EPTP_WB | EPTP_PWL_4;
TEST_ASSERT((vmx->eptp_gpa & ~PHYSICAL_PAGE_MASK) == 0,
"Illegal bits set in vmx->eptp_gpa");
if (ept_vpid_cap_supported(VMX_EPT_VPID_CAP_AD_BITS))
eptp |= EPTP_AD_ENABLED;
vmwrite(EPT_POINTER, eptp);
sec_exec_ctl |= SECONDARY_EXEC_ENABLE_EPT;
}
if (!vmwrite(SECONDARY_VM_EXEC_CONTROL, sec_exec_ctl))
vmwrite(CPU_BASED_VM_EXEC_CONTROL,
rdmsr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS) | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS);
else {
vmwrite(CPU_BASED_VM_EXEC_CONTROL, rdmsr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS));
GUEST_ASSERT(!sec_exec_ctl);
}
vmwrite(EXCEPTION_BITMAP, 0);
vmwrite(PAGE_FAULT_ERROR_CODE_MASK, 0);
vmwrite(PAGE_FAULT_ERROR_CODE_MATCH, -1); /* Never match */
vmwrite(CR3_TARGET_COUNT, 0);
vmwrite(VM_EXIT_CONTROLS, rdmsr(MSR_IA32_VMX_EXIT_CTLS) |
VM_EXIT_HOST_ADDR_SPACE_SIZE); /* 64-bit host */
vmwrite(VM_EXIT_MSR_STORE_COUNT, 0);
vmwrite(VM_EXIT_MSR_LOAD_COUNT, 0);
vmwrite(VM_ENTRY_CONTROLS, rdmsr(MSR_IA32_VMX_ENTRY_CTLS) |
VM_ENTRY_IA32E_MODE); /* 64-bit guest */
vmwrite(VM_ENTRY_MSR_LOAD_COUNT, 0);
vmwrite(VM_ENTRY_INTR_INFO_FIELD, 0);
vmwrite(TPR_THRESHOLD, 0);
vmwrite(CR0_GUEST_HOST_MASK, 0);
vmwrite(CR4_GUEST_HOST_MASK, 0);
vmwrite(CR0_READ_SHADOW, get_cr0());
vmwrite(CR4_READ_SHADOW, get_cr4());
vmwrite(MSR_BITMAP, vmx->msr_gpa);
vmwrite(VMREAD_BITMAP, vmx->vmread_gpa);
vmwrite(VMWRITE_BITMAP, vmx->vmwrite_gpa);
}
/*
* Initialize the host state fields based on the current host state, with
* the exception of HOST_RSP and HOST_RIP, which should be set by vmlaunch
* or vmresume.
*/
static inline void init_vmcs_host_state(void)
{
u32 exit_controls = vmreadz(VM_EXIT_CONTROLS);
vmwrite(HOST_ES_SELECTOR, get_es());
vmwrite(HOST_CS_SELECTOR, get_cs());
vmwrite(HOST_SS_SELECTOR, get_ss());
vmwrite(HOST_DS_SELECTOR, get_ds());
vmwrite(HOST_FS_SELECTOR, get_fs());
vmwrite(HOST_GS_SELECTOR, get_gs());
vmwrite(HOST_TR_SELECTOR, get_tr());
if (exit_controls & VM_EXIT_LOAD_IA32_PAT)
vmwrite(HOST_IA32_PAT, rdmsr(MSR_IA32_CR_PAT));
if (exit_controls & VM_EXIT_LOAD_IA32_EFER)
vmwrite(HOST_IA32_EFER, rdmsr(MSR_EFER));
if (exit_controls & VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL)
vmwrite(HOST_IA32_PERF_GLOBAL_CTRL,
rdmsr(MSR_CORE_PERF_GLOBAL_CTRL));
vmwrite(HOST_IA32_SYSENTER_CS, rdmsr(MSR_IA32_SYSENTER_CS));
vmwrite(HOST_CR0, get_cr0());
vmwrite(HOST_CR3, get_cr3());
vmwrite(HOST_CR4, get_cr4());
vmwrite(HOST_FS_BASE, rdmsr(MSR_FS_BASE));
vmwrite(HOST_GS_BASE, rdmsr(MSR_GS_BASE));
vmwrite(HOST_TR_BASE,
get_desc64_base((struct desc64 *)(get_gdt().address + get_tr())));
vmwrite(HOST_GDTR_BASE, get_gdt().address);
vmwrite(HOST_IDTR_BASE, get_idt().address);
vmwrite(HOST_IA32_SYSENTER_ESP, rdmsr(MSR_IA32_SYSENTER_ESP));
vmwrite(HOST_IA32_SYSENTER_EIP, rdmsr(MSR_IA32_SYSENTER_EIP));
}
/*
* Initialize the guest state fields essentially as a clone of
* the host state fields. Some host state fields have fixed
* values, and we set the corresponding guest state fields accordingly.
*/
static inline void init_vmcs_guest_state(void *rip, void *rsp)
{
vmwrite(GUEST_ES_SELECTOR, vmreadz(HOST_ES_SELECTOR));
vmwrite(GUEST_CS_SELECTOR, vmreadz(HOST_CS_SELECTOR));
vmwrite(GUEST_SS_SELECTOR, vmreadz(HOST_SS_SELECTOR));
vmwrite(GUEST_DS_SELECTOR, vmreadz(HOST_DS_SELECTOR));
vmwrite(GUEST_FS_SELECTOR, vmreadz(HOST_FS_SELECTOR));
vmwrite(GUEST_GS_SELECTOR, vmreadz(HOST_GS_SELECTOR));
vmwrite(GUEST_LDTR_SELECTOR, 0);
vmwrite(GUEST_TR_SELECTOR, vmreadz(HOST_TR_SELECTOR));
vmwrite(GUEST_INTR_STATUS, 0);
vmwrite(GUEST_PML_INDEX, 0);
vmwrite(VMCS_LINK_POINTER, -1ll);
vmwrite(GUEST_IA32_DEBUGCTL, 0);
vmwrite(GUEST_IA32_PAT, vmreadz(HOST_IA32_PAT));
vmwrite(GUEST_IA32_EFER, vmreadz(HOST_IA32_EFER));
vmwrite(GUEST_IA32_PERF_GLOBAL_CTRL,
vmreadz(HOST_IA32_PERF_GLOBAL_CTRL));
vmwrite(GUEST_ES_LIMIT, -1);
vmwrite(GUEST_CS_LIMIT, -1);
vmwrite(GUEST_SS_LIMIT, -1);
vmwrite(GUEST_DS_LIMIT, -1);
vmwrite(GUEST_FS_LIMIT, -1);
vmwrite(GUEST_GS_LIMIT, -1);
vmwrite(GUEST_LDTR_LIMIT, -1);
vmwrite(GUEST_TR_LIMIT, 0x67);
vmwrite(GUEST_GDTR_LIMIT, 0xffff);
vmwrite(GUEST_IDTR_LIMIT, 0xffff);
vmwrite(GUEST_ES_AR_BYTES,
vmreadz(GUEST_ES_SELECTOR) == 0 ? 0x10000 : 0xc093);
vmwrite(GUEST_CS_AR_BYTES, 0xa09b);
vmwrite(GUEST_SS_AR_BYTES, 0xc093);
vmwrite(GUEST_DS_AR_BYTES,
vmreadz(GUEST_DS_SELECTOR) == 0 ? 0x10000 : 0xc093);
vmwrite(GUEST_FS_AR_BYTES,
vmreadz(GUEST_FS_SELECTOR) == 0 ? 0x10000 : 0xc093);
vmwrite(GUEST_GS_AR_BYTES,
vmreadz(GUEST_GS_SELECTOR) == 0 ? 0x10000 : 0xc093);
vmwrite(GUEST_LDTR_AR_BYTES, 0x10000);
vmwrite(GUEST_TR_AR_BYTES, 0x8b);
vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0);
vmwrite(GUEST_ACTIVITY_STATE, 0);
vmwrite(GUEST_SYSENTER_CS, vmreadz(HOST_IA32_SYSENTER_CS));
vmwrite(VMX_PREEMPTION_TIMER_VALUE, 0);
vmwrite(GUEST_CR0, vmreadz(HOST_CR0));
vmwrite(GUEST_CR3, vmreadz(HOST_CR3));
vmwrite(GUEST_CR4, vmreadz(HOST_CR4));
vmwrite(GUEST_ES_BASE, 0);
vmwrite(GUEST_CS_BASE, 0);
vmwrite(GUEST_SS_BASE, 0);
vmwrite(GUEST_DS_BASE, 0);
vmwrite(GUEST_FS_BASE, vmreadz(HOST_FS_BASE));
vmwrite(GUEST_GS_BASE, vmreadz(HOST_GS_BASE));
vmwrite(GUEST_LDTR_BASE, 0);
vmwrite(GUEST_TR_BASE, vmreadz(HOST_TR_BASE));
vmwrite(GUEST_GDTR_BASE, vmreadz(HOST_GDTR_BASE));
vmwrite(GUEST_IDTR_BASE, vmreadz(HOST_IDTR_BASE));
vmwrite(GUEST_DR7, 0x400);
vmwrite(GUEST_RSP, (u64)rsp);
vmwrite(GUEST_RIP, (u64)rip);
vmwrite(GUEST_RFLAGS, X86_EFLAGS_FIXED);
vmwrite(GUEST_PENDING_DBG_EXCEPTIONS, 0);
vmwrite(GUEST_SYSENTER_ESP, vmreadz(HOST_IA32_SYSENTER_ESP));
vmwrite(GUEST_SYSENTER_EIP, vmreadz(HOST_IA32_SYSENTER_EIP));
}
void prepare_vmcs(struct vmx_pages *vmx, void *guest_rip, void *guest_rsp)
{
init_vmcs_control_fields(vmx);
init_vmcs_host_state();
init_vmcs_guest_state(guest_rip, guest_rsp);
}
bool kvm_cpu_has_ept(void)
{
u64 ctrl;
if (!kvm_cpu_has(X86_FEATURE_VMX))
return false;
ctrl = kvm_get_feature_msr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS) >> 32;
if (!(ctrl & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS))
return false;
ctrl = kvm_get_feature_msr(MSR_IA32_VMX_PROCBASED_CTLS2) >> 32;
return ctrl & SECONDARY_EXEC_ENABLE_EPT;
}
void prepare_virtualize_apic_accesses(struct vmx_pages *vmx, struct kvm_vm *vm)
{
vmx->apic_access = (void *)vm_alloc_page(vm);
vmx->apic_access_hva = addr_gva2hva(vm, (uintptr_t)vmx->apic_access);
vmx->apic_access_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->apic_access);
}