* [PATCH v2 01/10] KVM: arm64: selftests: Add MMIO readl/writel support
2021-08-18 18:43 [PATCH v2 00/10] KVM: arm64: selftests: Introduce arch_timer selftest Raghavendra Rao Ananta
@ 2021-08-18 18:43 ` Raghavendra Rao Ananta
2021-08-18 18:43 ` [PATCH v2 02/10] KVM: arm64: selftests: Add write_sysreg_s and read_sysreg_s Raghavendra Rao Ananta
` (8 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Raghavendra Rao Ananta @ 2021-08-18 18:43 UTC (permalink / raw)
To: Paolo Bonzini, Marc Zyngier
Cc: kvm, Peter Shier, Raghavendra Rao Anata, kvmarm
Define the readl() and writel() functions for the guests to
access (4-byte) the MMIO region.
The routines, and their dependents, are inspired from the kernel's
arch/arm64/include/asm/io.h and arch/arm64/include/asm/barrier.h.
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
.../selftests/kvm/include/aarch64/processor.h | 45 ++++++++++++++++++-
1 file changed, 44 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/kvm/include/aarch64/processor.h b/tools/testing/selftests/kvm/include/aarch64/processor.h
index 27dc5c2e56b9..14f68bf55036 100644
--- a/tools/testing/selftests/kvm/include/aarch64/processor.h
+++ b/tools/testing/selftests/kvm/include/aarch64/processor.h
@@ -127,6 +127,49 @@ void vm_install_sync_handler(struct kvm_vm *vm,
val; \
})
-#define isb() asm volatile("isb" : : : "memory")
+#define isb() asm volatile("isb" : : : "memory")
+#define dsb(opt) asm volatile("dsb " #opt : : : "memory")
+#define dmb(opt) asm volatile("dmb " #opt : : : "memory")
+
+#define dma_wmb() dmb(oshst)
+#define __iowmb() dma_wmb()
+
+#define dma_rmb() dmb(oshld)
+
+#define __iormb(v) \
+({ \
+ unsigned long tmp; \
+ \
+ dma_rmb(); \
+ \
+ /* \
+ * Courtesy of arch/arm64/include/asm/io.h: \
+ * Create a dummy control dependency from the IO read to any \
+ * later instructions. This ensures that a subsequent call \
+ * to udelay() will be ordered due to the ISB in __delay(). \
+ */ \
+ asm volatile("eor %0, %1, %1\n" \
+ "cbnz %0, ." \
+ : "=r" (tmp) : "r" ((unsigned long)(v)) \
+ : "memory"); \
+})
+
+static __always_inline void __raw_writel(u32 val, volatile void *addr)
+{
+ asm volatile("str %w0, [%1]" : : "rZ" (val), "r" (addr));
+}
+
+static __always_inline u32 __raw_readl(const volatile void *addr)
+{
+ u32 val;
+ asm volatile("ldr %w0, [%1]" : "=r" (val) : "r" (addr));
+ return val;
+}
+
+#define writel_relaxed(v,c) ((void)__raw_writel((__force u32)cpu_to_le32(v),(c)))
+#define readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32)__raw_readl(c)); __r; })
+
+#define writel(v,c) ({ __iowmb(); writel_relaxed((v),(c));})
+#define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(__v); __v; })
#endif /* SELFTEST_KVM_PROCESSOR_H */
--
2.33.0.rc1.237.g0d66db33f3-goog
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 02/10] KVM: arm64: selftests: Add write_sysreg_s and read_sysreg_s
2021-08-18 18:43 [PATCH v2 00/10] KVM: arm64: selftests: Introduce arch_timer selftest Raghavendra Rao Ananta
2021-08-18 18:43 ` [PATCH v2 01/10] KVM: arm64: selftests: Add MMIO readl/writel support Raghavendra Rao Ananta
@ 2021-08-18 18:43 ` Raghavendra Rao Ananta
2021-08-18 18:43 ` [PATCH v2 03/10] KVM: arm64: selftests: Add support for cpu_relax Raghavendra Rao Ananta
` (7 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Raghavendra Rao Ananta @ 2021-08-18 18:43 UTC (permalink / raw)
To: Paolo Bonzini, Marc Zyngier
Cc: kvm, Peter Shier, Raghavendra Rao Anata, kvmarm
For register names that are unsupported by the assembler or the ones
without architectural names, add the macros write_sysreg_s and
read_sysreg_s to support them.
The functionality is derived from kvm-unit-tests and kernel's
arch/arm64/include/asm/sysreg.h.
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
.../selftests/kvm/include/aarch64/processor.h | 61 +++++++++++++++++++
1 file changed, 61 insertions(+)
diff --git a/tools/testing/selftests/kvm/include/aarch64/processor.h b/tools/testing/selftests/kvm/include/aarch64/processor.h
index 14f68bf55036..b4bbce837288 100644
--- a/tools/testing/selftests/kvm/include/aarch64/processor.h
+++ b/tools/testing/selftests/kvm/include/aarch64/processor.h
@@ -115,6 +115,67 @@ void vm_install_exception_handler(struct kvm_vm *vm,
void vm_install_sync_handler(struct kvm_vm *vm,
int vector, int ec, handler_fn handler);
+/*
+ * ARMv8 ARM reserves the following encoding for system registers:
+ * (Ref: ARMv8 ARM, Section: "System instruction class encoding overview",
+ * C5.2, version:ARM DDI 0487A.f)
+ * [20-19] : Op0
+ * [18-16] : Op1
+ * [15-12] : CRn
+ * [11-8] : CRm
+ * [7-5] : Op2
+ */
+#define Op0_shift 19
+#define Op0_mask 0x3
+#define Op1_shift 16
+#define Op1_mask 0x7
+#define CRn_shift 12
+#define CRn_mask 0xf
+#define CRm_shift 8
+#define CRm_mask 0xf
+#define Op2_shift 5
+#define Op2_mask 0x7
+
+/*
+ * When accessed from guests, the ARM64_SYS_REG() doesn't work since it
+ * generates a different encoding for additional KVM processing, and is
+ * only suitable for userspace to access the register via ioctls.
+ * Hence, define a 'pure' sys_reg() here to generate the encodings as per spec.
+ */
+#define sys_reg(op0, op1, crn, crm, op2) \
+ (((op0) << Op0_shift) | ((op1) << Op1_shift) | \
+ ((crn) << CRn_shift) | ((crm) << CRm_shift) | \
+ ((op2) << Op2_shift))
+
+asm(
+" .irp num,0,1,2,3,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\n"
+" .equ .L__reg_num_x\\num, \\num\n"
+" .endr\n"
+" .equ .L__reg_num_xzr, 31\n"
+"\n"
+" .macro mrs_s, rt, sreg\n"
+" .inst 0xd5200000|(\\sreg)|(.L__reg_num_\\rt)\n"
+" .endm\n"
+"\n"
+" .macro msr_s, sreg, rt\n"
+" .inst 0xd5000000|(\\sreg)|(.L__reg_num_\\rt)\n"
+" .endm\n"
+);
+
+/*
+ * read_sysreg_s() and write_sysreg_s()'s 'reg' has to be encoded via sys_reg()
+ */
+#define read_sysreg_s(reg) ({ \
+ u64 __val; \
+ asm volatile("mrs_s %0, "__stringify(reg) : "=r" (__val)); \
+ __val; \
+})
+
+#define write_sysreg_s(reg, val) do { \
+ u64 __val = (u64)val; \
+ asm volatile("msr_s "__stringify(reg) ", %x0" : : "rZ" (__val));\
+} while (0)
+
#define write_sysreg(reg, val) \
({ \
u64 __val = (u64)(val); \
--
2.33.0.rc1.237.g0d66db33f3-goog
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 03/10] KVM: arm64: selftests: Add support for cpu_relax
2021-08-18 18:43 [PATCH v2 00/10] KVM: arm64: selftests: Introduce arch_timer selftest Raghavendra Rao Ananta
2021-08-18 18:43 ` [PATCH v2 01/10] KVM: arm64: selftests: Add MMIO readl/writel support Raghavendra Rao Ananta
2021-08-18 18:43 ` [PATCH v2 02/10] KVM: arm64: selftests: Add write_sysreg_s and read_sysreg_s Raghavendra Rao Ananta
@ 2021-08-18 18:43 ` Raghavendra Rao Ananta
2021-08-18 18:43 ` [PATCH v2 04/10] KVM: arm64: selftests: Add basic support for arch_timers Raghavendra Rao Ananta
` (6 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Raghavendra Rao Ananta @ 2021-08-18 18:43 UTC (permalink / raw)
To: Paolo Bonzini, Marc Zyngier
Cc: kvm, Peter Shier, Raghavendra Rao Anata, kvmarm
Implement the guest helper routine, cpu_relax(), to yield
the processor to other tasks.
The function was derived from
arch/arm64/include/asm/vdso/processor.h.
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
tools/testing/selftests/kvm/include/aarch64/processor.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/tools/testing/selftests/kvm/include/aarch64/processor.h b/tools/testing/selftests/kvm/include/aarch64/processor.h
index b4bbce837288..c83ff99282ed 100644
--- a/tools/testing/selftests/kvm/include/aarch64/processor.h
+++ b/tools/testing/selftests/kvm/include/aarch64/processor.h
@@ -188,6 +188,11 @@ asm(
val; \
})
+static inline void cpu_relax(void)
+{
+ asm volatile("yield" ::: "memory");
+}
+
#define isb() asm volatile("isb" : : : "memory")
#define dsb(opt) asm volatile("dsb " #opt : : : "memory")
#define dmb(opt) asm volatile("dmb " #opt : : : "memory")
--
2.33.0.rc1.237.g0d66db33f3-goog
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 04/10] KVM: arm64: selftests: Add basic support for arch_timers
2021-08-18 18:43 [PATCH v2 00/10] KVM: arm64: selftests: Introduce arch_timer selftest Raghavendra Rao Ananta
` (2 preceding siblings ...)
2021-08-18 18:43 ` [PATCH v2 03/10] KVM: arm64: selftests: Add support for cpu_relax Raghavendra Rao Ananta
@ 2021-08-18 18:43 ` Raghavendra Rao Ananta
2021-08-18 18:43 ` [PATCH v2 05/10] KVM: arm64: selftests: Add basic support to generate delays Raghavendra Rao Ananta
` (5 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Raghavendra Rao Ananta @ 2021-08-18 18:43 UTC (permalink / raw)
To: Paolo Bonzini, Marc Zyngier
Cc: kvm, Peter Shier, Raghavendra Rao Anata, kvmarm
Add a minimalistic library support to access the virtual timers,
that can be used for simple timing functionalities, such as
introducing delays in the guest.
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
.../kvm/include/aarch64/arch_timer.h | 142 ++++++++++++++++++
1 file changed, 142 insertions(+)
create mode 100644 tools/testing/selftests/kvm/include/aarch64/arch_timer.h
diff --git a/tools/testing/selftests/kvm/include/aarch64/arch_timer.h b/tools/testing/selftests/kvm/include/aarch64/arch_timer.h
new file mode 100644
index 000000000000..9df5b63abc47
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/aarch64/arch_timer.h
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ARM Generic Timer specific interface
+ */
+
+#ifndef SELFTEST_KVM_ARCH_TIMER_H
+#define SELFTEST_KVM_ARCH_TIMER_H
+
+#include "processor.h"
+
+enum arch_timer {
+ VIRTUAL,
+ PHYSICAL,
+};
+
+#define CTL_ENABLE (1 << 0)
+#define CTL_IMASK (1 << 1)
+#define CTL_ISTATUS (1 << 2)
+
+#define msec_to_cycles(msec) \
+ (timer_get_cntfrq() * (uint64_t)(msec) / 1000)
+
+#define usec_to_cycles(usec) \
+ (timer_get_cntfrq() * (uint64_t)(usec) / 1000000)
+
+#define cycles_to_usec(cycles) \
+ ((uint64_t)(cycles) * 1000000 / timer_get_cntfrq())
+
+static inline uint32_t timer_get_cntfrq(void)
+{
+ return read_sysreg(cntfrq_el0);
+}
+
+static inline uint64_t timer_get_cntct(enum arch_timer timer)
+{
+ isb();
+
+ switch (timer) {
+ case VIRTUAL:
+ return read_sysreg(cntvct_el0);
+ case PHYSICAL:
+ return read_sysreg(cntpct_el0);
+ default:
+ GUEST_ASSERT_1(0, timer);
+ }
+
+ /* We should not reach here */
+ return 0;
+}
+
+static inline void timer_set_cval(enum arch_timer timer, uint64_t cval)
+{
+ switch (timer) {
+ case VIRTUAL:
+ write_sysreg(cntv_cval_el0, cval);
+ break;
+ case PHYSICAL:
+ write_sysreg(cntp_cval_el0, cval);
+ break;
+ default:
+ GUEST_ASSERT_1(0, timer);
+ }
+
+ isb();
+}
+
+static inline uint64_t timer_get_cval(enum arch_timer timer)
+{
+ switch (timer) {
+ case VIRTUAL:
+ return read_sysreg(cntv_cval_el0);
+ case PHYSICAL:
+ return read_sysreg(cntp_cval_el0);
+ default:
+ GUEST_ASSERT_1(0, timer);
+ }
+
+ /* We should not reach here */
+ return 0;
+}
+
+static inline void timer_set_tval(enum arch_timer timer, uint32_t tval)
+{
+ switch (timer) {
+ case VIRTUAL:
+ write_sysreg(cntv_tval_el0, tval);
+ break;
+ case PHYSICAL:
+ write_sysreg(cntp_tval_el0, tval);
+ break;
+ default:
+ GUEST_ASSERT_1(0, timer);
+ }
+
+ isb();
+}
+
+static inline void timer_set_ctl(enum arch_timer timer, uint32_t ctl)
+{
+ switch (timer) {
+ case VIRTUAL:
+ write_sysreg(cntv_ctl_el0, ctl);
+ break;
+ case PHYSICAL:
+ write_sysreg(cntp_ctl_el0, ctl);
+ break;
+ default:
+ GUEST_ASSERT_1(0, timer);
+ }
+
+ isb();
+}
+
+static inline uint32_t timer_get_ctl(enum arch_timer timer)
+{
+ switch (timer) {
+ case VIRTUAL:
+ return read_sysreg(cntv_ctl_el0);
+ case PHYSICAL:
+ return read_sysreg(cntp_ctl_el0);
+ default:
+ GUEST_ASSERT_1(0, timer);
+ }
+
+ /* We should not reach here */
+ return 0;
+}
+
+static inline void timer_set_next_cval_ms(enum arch_timer timer, uint32_t msec)
+{
+ uint64_t now_ct = timer_get_cntct(timer);
+ uint64_t next_ct = now_ct + msec_to_cycles(msec);
+
+ timer_set_cval(timer, next_ct);
+}
+
+static inline void timer_set_next_tval_ms(enum arch_timer timer, uint32_t msec)
+{
+ timer_set_tval(timer, msec_to_cycles(msec));
+}
+
+#endif /* SELFTEST_KVM_ARCH_TIMER_H */
--
2.33.0.rc1.237.g0d66db33f3-goog
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 05/10] KVM: arm64: selftests: Add basic support to generate delays
2021-08-18 18:43 [PATCH v2 00/10] KVM: arm64: selftests: Introduce arch_timer selftest Raghavendra Rao Ananta
` (3 preceding siblings ...)
2021-08-18 18:43 ` [PATCH v2 04/10] KVM: arm64: selftests: Add basic support for arch_timers Raghavendra Rao Ananta
@ 2021-08-18 18:43 ` Raghavendra Rao Ananta
2021-08-18 18:43 ` [PATCH v2 06/10] KVM: arm64: selftests: Add support to disable and enable local IRQs Raghavendra Rao Ananta
` (4 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Raghavendra Rao Ananta @ 2021-08-18 18:43 UTC (permalink / raw)
To: Paolo Bonzini, Marc Zyngier
Cc: kvm, Peter Shier, Raghavendra Rao Anata, kvmarm
Add udelay() support to generate a delay in the guest.
The routines are derived and simplified from kernel's
arch/arm64/lib/delay.c.
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
.../selftests/kvm/include/aarch64/delay.h | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 tools/testing/selftests/kvm/include/aarch64/delay.h
diff --git a/tools/testing/selftests/kvm/include/aarch64/delay.h b/tools/testing/selftests/kvm/include/aarch64/delay.h
new file mode 100644
index 000000000000..329e4f5079ea
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/aarch64/delay.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ARM simple delay routines
+ */
+
+#ifndef SELFTEST_KVM_ARM_DELAY_H
+#define SELFTEST_KVM_ARM_DELAY_H
+
+#include "arch_timer.h"
+
+static inline void __delay(uint64_t cycles)
+{
+ enum arch_timer timer = VIRTUAL;
+ uint64_t start = timer_get_cntct(timer);
+
+ while ((timer_get_cntct(timer) - start) < cycles)
+ cpu_relax();
+}
+
+static inline void udelay(unsigned long usec)
+{
+ __delay(usec_to_cycles(usec));
+}
+
+#endif /* SELFTEST_KVM_ARM_DELAY_H */
--
2.33.0.rc1.237.g0d66db33f3-goog
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 06/10] KVM: arm64: selftests: Add support to disable and enable local IRQs
2021-08-18 18:43 [PATCH v2 00/10] KVM: arm64: selftests: Introduce arch_timer selftest Raghavendra Rao Ananta
` (4 preceding siblings ...)
2021-08-18 18:43 ` [PATCH v2 05/10] KVM: arm64: selftests: Add basic support to generate delays Raghavendra Rao Ananta
@ 2021-08-18 18:43 ` Raghavendra Rao Ananta
2021-08-18 18:43 ` [PATCH v2 07/10] KVM: arm64: selftests: Add support to get the vcpuid from MPIDR_EL1 Raghavendra Rao Ananta
` (3 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Raghavendra Rao Ananta @ 2021-08-18 18:43 UTC (permalink / raw)
To: Paolo Bonzini, Marc Zyngier
Cc: kvm, Peter Shier, Raghavendra Rao Anata, kvmarm
Add functions local_irq_enable() and local_irq_disable() to
enable and disable the IRQs from the guest, respectively.
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
.../testing/selftests/kvm/include/aarch64/processor.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/tools/testing/selftests/kvm/include/aarch64/processor.h b/tools/testing/selftests/kvm/include/aarch64/processor.h
index c83ff99282ed..ae7a079ae180 100644
--- a/tools/testing/selftests/kvm/include/aarch64/processor.h
+++ b/tools/testing/selftests/kvm/include/aarch64/processor.h
@@ -238,4 +238,14 @@ static __always_inline u32 __raw_readl(const volatile void *addr)
#define writel(v,c) ({ __iowmb(); writel_relaxed((v),(c));})
#define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(__v); __v; })
+static inline void local_irq_enable(void)
+{
+ asm volatile("msr daifclr, #3" : : : "memory");
+}
+
+static inline void local_irq_disable(void)
+{
+ asm volatile("msr daifset, #3" : : : "memory");
+}
+
#endif /* SELFTEST_KVM_PROCESSOR_H */
--
2.33.0.rc1.237.g0d66db33f3-goog
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 07/10] KVM: arm64: selftests: Add support to get the vcpuid from MPIDR_EL1
2021-08-18 18:43 [PATCH v2 00/10] KVM: arm64: selftests: Introduce arch_timer selftest Raghavendra Rao Ananta
` (5 preceding siblings ...)
2021-08-18 18:43 ` [PATCH v2 06/10] KVM: arm64: selftests: Add support to disable and enable local IRQs Raghavendra Rao Ananta
@ 2021-08-18 18:43 ` Raghavendra Rao Ananta
2021-08-18 18:43 ` [PATCH v2 08/10] KVM: arm64: selftests: Add light-weight spinlock support Raghavendra Rao Ananta
` (2 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Raghavendra Rao Ananta @ 2021-08-18 18:43 UTC (permalink / raw)
To: Paolo Bonzini, Marc Zyngier
Cc: kvm, Peter Shier, Raghavendra Rao Anata, kvmarm
At times, such as when in the interrupt handler, the guest wants to
get the vCPU-id that it's running on. As a result, introduce
get_vcpuid() that parses the MPIDR_EL1 and returns the vcpuid to the
requested caller.
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
.../selftests/kvm/include/aarch64/processor.h | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/tools/testing/selftests/kvm/include/aarch64/processor.h b/tools/testing/selftests/kvm/include/aarch64/processor.h
index ae7a079ae180..e9342e63d05d 100644
--- a/tools/testing/selftests/kvm/include/aarch64/processor.h
+++ b/tools/testing/selftests/kvm/include/aarch64/processor.h
@@ -248,4 +248,23 @@ static inline void local_irq_disable(void)
asm volatile("msr daifset, #3" : : : "memory");
}
+#define MPIDR_LEVEL_BITS 8
+#define MPIDR_LEVEL_SHIFT(level) (MPIDR_LEVEL_BITS * level)
+#define MPIDR_LEVEL_MASK ((1 << MPIDR_LEVEL_BITS) - 1)
+#define MPIDR_AFFINITY_LEVEL(mpidr, level) \
+ ((mpidr >> MPIDR_LEVEL_SHIFT(level)) & MPIDR_LEVEL_MASK)
+
+static inline uint32_t get_vcpuid(void)
+{
+ uint32_t vcpuid = 0;
+ uint64_t mpidr = read_sysreg(mpidr_el1);
+
+ /* KVM limits only 16 vCPUs at level 0 */
+ vcpuid = mpidr & 0x0f;
+ vcpuid |= MPIDR_AFFINITY_LEVEL(mpidr, 1) << 4;
+ vcpuid |= MPIDR_AFFINITY_LEVEL(mpidr, 2) << 12;
+
+ return vcpuid;
+}
+
#endif /* SELFTEST_KVM_PROCESSOR_H */
--
2.33.0.rc1.237.g0d66db33f3-goog
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 08/10] KVM: arm64: selftests: Add light-weight spinlock support
2021-08-18 18:43 [PATCH v2 00/10] KVM: arm64: selftests: Introduce arch_timer selftest Raghavendra Rao Ananta
` (6 preceding siblings ...)
2021-08-18 18:43 ` [PATCH v2 07/10] KVM: arm64: selftests: Add support to get the vcpuid from MPIDR_EL1 Raghavendra Rao Ananta
@ 2021-08-18 18:43 ` Raghavendra Rao Ananta
2021-08-18 18:43 ` [PATCH v2 09/10] KVM: arm64: selftests: Add basic GICv3 support Raghavendra Rao Ananta
2021-08-18 18:43 ` [PATCH v2 10/10] KVM: arm64: selftests: Add arch_timer test Raghavendra Rao Ananta
9 siblings, 0 replies; 13+ messages in thread
From: Raghavendra Rao Ananta @ 2021-08-18 18:43 UTC (permalink / raw)
To: Paolo Bonzini, Marc Zyngier
Cc: kvm, Peter Shier, Raghavendra Rao Anata, kvmarm
Add a simpler version of spinlock support for ARM64 for
the guests to use.
The implementation is loosely based on the spinlock
implementation in kvm-unit-tests.
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
tools/testing/selftests/kvm/Makefile | 2 +-
.../selftests/kvm/include/aarch64/spinlock.h | 13 +++++++++
.../selftests/kvm/lib/aarch64/spinlock.c | 27 +++++++++++++++++++
3 files changed, 41 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/kvm/include/aarch64/spinlock.h
create mode 100644 tools/testing/selftests/kvm/lib/aarch64/spinlock.c
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 5832f510a16c..8f6d82b570bd 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -35,7 +35,7 @@ endif
LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/rbtree.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c
LIBKVM_x86_64 = lib/x86_64/apic.c lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S
-LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c lib/aarch64/handlers.S
+LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c lib/aarch64/handlers.S lib/aarch64/spinlock.c
LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c
TEST_GEN_PROGS_x86_64 = x86_64/cr4_cpuid_sync_test
diff --git a/tools/testing/selftests/kvm/include/aarch64/spinlock.h b/tools/testing/selftests/kvm/include/aarch64/spinlock.h
new file mode 100644
index 000000000000..cf0984106d14
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/aarch64/spinlock.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef SELFTEST_KVM_ARM64_SPINLOCK_H
+#define SELFTEST_KVM_ARM64_SPINLOCK_H
+
+struct spinlock {
+ int v;
+};
+
+extern void spin_lock(struct spinlock *lock);
+extern void spin_unlock(struct spinlock *lock);
+
+#endif /* SELFTEST_KVM_ARM64_SPINLOCK_H */
diff --git a/tools/testing/selftests/kvm/lib/aarch64/spinlock.c b/tools/testing/selftests/kvm/lib/aarch64/spinlock.c
new file mode 100644
index 000000000000..6d66a3dac237
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/aarch64/spinlock.c
@@ -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)
+{
+ uint32_t 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");
+}
--
2.33.0.rc1.237.g0d66db33f3-goog
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 09/10] KVM: arm64: selftests: Add basic GICv3 support
2021-08-18 18:43 [PATCH v2 00/10] KVM: arm64: selftests: Introduce arch_timer selftest Raghavendra Rao Ananta
` (7 preceding siblings ...)
2021-08-18 18:43 ` [PATCH v2 08/10] KVM: arm64: selftests: Add light-weight spinlock support Raghavendra Rao Ananta
@ 2021-08-18 18:43 ` Raghavendra Rao Ananta
2021-08-18 18:43 ` [PATCH v2 10/10] KVM: arm64: selftests: Add arch_timer test Raghavendra Rao Ananta
9 siblings, 0 replies; 13+ messages in thread
From: Raghavendra Rao Ananta @ 2021-08-18 18:43 UTC (permalink / raw)
To: Paolo Bonzini, Marc Zyngier
Cc: kvm, Peter Shier, Raghavendra Rao Anata, kvmarm
Add basic support for ARM Generic Interrupt Controller v3.
The support provides guests to setup interrupts.
The work is inspired from kvm-unit-tests and the kernel's
GIC driver (drivers/irqchip/irq-gic-v3.c).
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
tools/testing/selftests/kvm/Makefile | 2 +-
.../selftests/kvm/include/aarch64/gic.h | 21 ++
tools/testing/selftests/kvm/lib/aarch64/gic.c | 93 +++++++
.../selftests/kvm/lib/aarch64/gic_private.h | 21 ++
.../selftests/kvm/lib/aarch64/gic_v3.c | 240 ++++++++++++++++++
.../selftests/kvm/lib/aarch64/gic_v3.h | 70 +++++
6 files changed, 446 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/kvm/include/aarch64/gic.h
create mode 100644 tools/testing/selftests/kvm/lib/aarch64/gic.c
create mode 100644 tools/testing/selftests/kvm/lib/aarch64/gic_private.h
create mode 100644 tools/testing/selftests/kvm/lib/aarch64/gic_v3.c
create mode 100644 tools/testing/selftests/kvm/lib/aarch64/gic_v3.h
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 8f6d82b570bd..a170166334a3 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -35,7 +35,7 @@ endif
LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/rbtree.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c
LIBKVM_x86_64 = lib/x86_64/apic.c lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S
-LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c lib/aarch64/handlers.S lib/aarch64/spinlock.c
+LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c lib/aarch64/handlers.S lib/aarch64/spinlock.c lib/aarch64/gic.c lib/aarch64/gic_v3.c
LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c
TEST_GEN_PROGS_x86_64 = x86_64/cr4_cpuid_sync_test
diff --git a/tools/testing/selftests/kvm/include/aarch64/gic.h b/tools/testing/selftests/kvm/include/aarch64/gic.h
new file mode 100644
index 000000000000..85dd1e53048e
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/aarch64/gic.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ARM Generic Interrupt Controller (GIC) specific defines
+ */
+
+#ifndef SELFTEST_KVM_GIC_H
+#define SELFTEST_KVM_GIC_H
+
+enum gic_type {
+ GIC_V3,
+ GIC_TYPE_MAX,
+};
+
+void gic_init(enum gic_type type, unsigned int nr_cpus,
+ void *dist_base, void *redist_base);
+void gic_irq_enable(unsigned int intid);
+void gic_irq_disable(unsigned int intid);
+unsigned int gic_get_and_ack_irq(void);
+void gic_set_eoi(unsigned int intid);
+
+#endif /* SELFTEST_KVM_GIC_H */
diff --git a/tools/testing/selftests/kvm/lib/aarch64/gic.c b/tools/testing/selftests/kvm/lib/aarch64/gic.c
new file mode 100644
index 000000000000..b0b67f5aeaa6
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/aarch64/gic.c
@@ -0,0 +1,93 @@
+// 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, void *redist_base)
+{
+ gic_common_ops->gic_cpu_init(cpu, redist_base);
+}
+
+static void
+gic_dist_init(enum gic_type type, unsigned int nr_cpus, void *dist_base)
+{
+ const struct gic_common_ops *gic_ops;
+
+ 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;
+
+ gic_ops->gic_init(nr_cpus, dist_base);
+ 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,
+ void *dist_base, void *redist_base)
+{
+ uint32_t cpu = get_vcpuid();
+
+ GUEST_ASSERT(type < GIC_TYPE_MAX);
+ GUEST_ASSERT(dist_base);
+ GUEST_ASSERT(redist_base);
+ GUEST_ASSERT(nr_cpus);
+
+ gic_dist_init(type, nr_cpus, dist_base);
+ gic_cpu_init(cpu, redist_base);
+}
+
+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)
+{
+ uint64_t 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);
+}
diff --git a/tools/testing/selftests/kvm/lib/aarch64/gic_private.h b/tools/testing/selftests/kvm/lib/aarch64/gic_private.h
new file mode 100644
index 000000000000..d81d739433dc
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/aarch64/gic_private.h
@@ -0,0 +1,21 @@
+/* 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 *dist_base);
+ void (*gic_cpu_init)(unsigned int cpu, void *redist_base);
+ void (*gic_irq_enable)(unsigned int intid);
+ void (*gic_irq_disable)(unsigned int intid);
+ uint64_t (*gic_read_iar)(void);
+ void (*gic_write_eoir)(uint32_t irq);
+};
+
+extern const struct gic_common_ops gicv3_ops;
+
+#endif /* SELFTEST_KVM_GIC_PRIVATE_H */
diff --git a/tools/testing/selftests/kvm/lib/aarch64/gic_v3.c b/tools/testing/selftests/kvm/lib/aarch64/gic_v3.c
new file mode 100644
index 000000000000..4b635ca6a8cb
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/aarch64/gic_v3.c
@@ -0,0 +1,240 @@
+// 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_v3.h"
+#include "gic_private.h"
+
+struct gicv3_data {
+ void *dist_base;
+ void *redist_base[GICV3_MAX_CPUS];
+ unsigned int nr_cpus;
+ unsigned int nr_spis;
+};
+
+#define sgi_base_from_redist(redist_base) (redist_base + SZ_64K)
+
+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(gicv3_data.dist_base + GICD_CTLR) & GICD_CTLR_RWP) {
+ GUEST_ASSERT(count--);
+ udelay(10);
+ }
+}
+
+static void gicv3_gicr_wait_for_rwp(void *redist_base)
+{
+ unsigned int count = 100000; /* 1s */
+
+ while (readl(redist_base + GICR_CTLR) & GICR_CTLR_RWP) {
+ GUEST_ASSERT(count--);
+ udelay(10);
+ }
+}
+
+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 uint64_t gicv3_read_iar(void)
+{
+ uint64_t irqstat = read_sysreg_s(SYS_ICC_IAR1_EL1);
+
+ dsb(sy);
+ return irqstat;
+}
+
+static void gicv3_write_eoir(uint32_t irq)
+{
+ write_sysreg_s(SYS_ICC_EOIR1_EL1, irq);
+ isb();
+}
+
+static void
+gicv3_config_irq(unsigned int intid, unsigned int offset)
+{
+ uint32_t cpu = get_vcpuid();
+ uint32_t mask = 1 << (intid % 32);
+ enum gicv3_intid_range intid_range = get_intid_range(intid);
+ void *reg;
+
+ /* We care about 'cpu' only for SGIs or PPIs */
+ if (intid_range == SGI_RANGE || intid_range == PPI_RANGE) {
+ GUEST_ASSERT(cpu < gicv3_data.nr_cpus);
+
+ reg = sgi_base_from_redist(gicv3_data.redist_base[cpu]) +
+ offset;
+ writel(mask, reg);
+ gicv3_gicr_wait_for_rwp(gicv3_data.redist_base[cpu]);
+ } else if (intid_range == SPI_RANGE) {
+ reg = gicv3_data.dist_base + offset + (intid / 32) * 4;
+ writel(mask, reg);
+ gicv3_gicd_wait_for_rwp();
+ } else {
+ GUEST_ASSERT(0);
+ }
+}
+
+static void gicv3_irq_enable(unsigned int intid)
+{
+ gicv3_config_irq(intid, GICD_ISENABLER);
+}
+
+static void gicv3_irq_disable(unsigned int intid)
+{
+ gicv3_config_irq(intid, GICD_ICENABLER);
+}
+
+static void gicv3_enable_redist(void *redist_base)
+{
+ uint32_t 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 inline void *gicr_base_gpa_cpu(void *redist_base, uint32_t cpu)
+{
+ /* Align all the redistributors sequentially */
+ return redist_base + cpu * SZ_64K * 2;
+}
+
+static void gicv3_cpu_init(unsigned int cpu, void *redist_base)
+{
+ void *sgi_base;
+ unsigned int i;
+ void *redist_base_cpu;
+
+ GUEST_ASSERT(cpu < gicv3_data.nr_cpus);
+
+ redist_base_cpu = gicr_base_gpa_cpu(redist_base, cpu);
+ sgi_base = sgi_base_from_redist(redist_base_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(redist_base_cpu);
+
+ /* Enable the GIC system register (ICC_*) access */
+ write_sysreg_s(SYS_ICC_SRE_EL1,
+ read_sysreg_s(SYS_ICC_SRE_EL1) | ICC_SRE_EL1_SRE);
+
+ /* Set a default priority threshold */
+ write_sysreg_s(SYS_ICC_PMR_EL1, ICC_PMR_DEF_PRIO);
+
+ /* Enable non-secure Group-1 interrupts */
+ write_sysreg_s(SYS_ICC_GRPEN1_EL1, ICC_IGRPEN1_EL1_ENABLE);
+
+ gicv3_data.redist_base[cpu] = redist_base_cpu;
+}
+
+static void gicv3_dist_init(void)
+{
+ void *dist_base = gicv3_data.dist_base;
+ unsigned int i;
+
+ /* Disable the distributor until we set things up */
+ writel(0, dist_base + 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, dist_base + GICD_IGROUPR + i / 8);
+ writel(~0, dist_base + GICD_ICACTIVER + i / 8);
+ writel(~0, dist_base + 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,
+ dist_base + 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, dist_base + GICD_CTLR);
+ gicv3_gicd_wait_for_rwp();
+}
+
+static void gicv3_init(unsigned int nr_cpus, void *dist_base)
+{
+ GUEST_ASSERT(nr_cpus <= GICV3_MAX_CPUS);
+
+ gicv3_data.nr_cpus = nr_cpus;
+ gicv3_data.dist_base = dist_base;
+ gicv3_data.nr_spis = GICD_TYPER_SPIS(
+ readl(gicv3_data.dist_base + 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,
+};
diff --git a/tools/testing/selftests/kvm/lib/aarch64/gic_v3.h b/tools/testing/selftests/kvm/lib/aarch64/gic_v3.h
new file mode 100644
index 000000000000..d41195e347b3
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/aarch64/gic_v3.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ARM Generic Interrupt Controller (GIC) v3 specific defines
+ */
+
+#ifndef SELFTEST_KVM_GICV3_H
+#define SELFTEST_KVM_GICV3_H
+
+#include "processor.h"
+
+/*
+ * Distributor registers
+ */
+#define GICD_CTLR 0x0000
+#define GICD_TYPER 0x0004
+#define GICD_IGROUPR 0x0080
+#define GICD_ISENABLER 0x0100
+#define GICD_ICENABLER 0x0180
+#define GICD_ICACTIVER 0x0380
+#define GICD_IPRIORITYR 0x0400
+
+/*
+ * The assumption is that the guest runs in a non-secure mode.
+ * The following bits of GICD_CTLR are defined accordingly.
+ */
+#define GICD_CTLR_RWP (1U << 31)
+#define GICD_CTLR_nASSGIreq (1U << 8)
+#define GICD_CTLR_ARE_NS (1U << 4)
+#define GICD_CTLR_ENABLE_G1A (1U << 1)
+#define GICD_CTLR_ENABLE_G1 (1U << 0)
+
+#define GICD_TYPER_SPIS(typer) ((((typer) & 0x1f) + 1) * 32)
+#define GICD_INT_DEF_PRI_X4 0xa0a0a0a0
+
+/*
+ * Redistributor registers
+ */
+#define GICR_CTLR 0x000
+#define GICR_WAKER 0x014
+
+#define GICR_CTLR_RWP (1U << 3)
+
+#define GICR_WAKER_ProcessorSleep (1U << 1)
+#define GICR_WAKER_ChildrenAsleep (1U << 2)
+
+/*
+ * Redistributor registers, offsets from SGI base
+ */
+#define GICR_IGROUPR0 GICD_IGROUPR
+#define GICR_ISENABLER0 GICD_ISENABLER
+#define GICR_ICENABLER0 GICD_ICENABLER
+#define GICR_ICACTIVER0 GICD_ICACTIVER
+#define GICR_IPRIORITYR0 GICD_IPRIORITYR
+
+/* CPU interface registers */
+#define SYS_ICC_PMR_EL1 sys_reg(3, 0, 4, 6, 0)
+#define SYS_ICC_IAR1_EL1 sys_reg(3, 0, 12, 12, 0)
+#define SYS_ICC_EOIR1_EL1 sys_reg(3, 0, 12, 12, 1)
+#define SYS_ICC_SRE_EL1 sys_reg(3, 0, 12, 12, 5)
+#define SYS_ICC_GRPEN1_EL1 sys_reg(3, 0, 12, 12, 7)
+
+#define ICC_PMR_DEF_PRIO 0xf0
+
+#define ICC_SRE_EL1_SRE (1U << 0)
+
+#define ICC_IGRPEN1_EL1_ENABLE (1U << 0)
+
+#define GICV3_MAX_CPUS 512
+
+#endif /* SELFTEST_KVM_GICV3_H */
--
2.33.0.rc1.237.g0d66db33f3-goog
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 10/10] KVM: arm64: selftests: Add arch_timer test
2021-08-18 18:43 [PATCH v2 00/10] KVM: arm64: selftests: Introduce arch_timer selftest Raghavendra Rao Ananta
` (8 preceding siblings ...)
2021-08-18 18:43 ` [PATCH v2 09/10] KVM: arm64: selftests: Add basic GICv3 support Raghavendra Rao Ananta
@ 2021-08-18 18:43 ` Raghavendra Rao Ananta
2021-08-26 21:54 ` Ricardo Koller
9 siblings, 1 reply; 13+ messages in thread
From: Raghavendra Rao Ananta @ 2021-08-18 18:43 UTC (permalink / raw)
To: Paolo Bonzini, Marc Zyngier
Cc: kvm, Peter Shier, Raghavendra Rao Anata, kvmarm
Add a KVM selftest to validate the arch_timer functionality.
Primarily, the test sets up periodic timer interrupts and
validates the basic architectural expectations upon its receipt.
The test provides command-line options to configure the period
of the timer, number of iterations, and number of vCPUs.
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
tools/testing/selftests/kvm/.gitignore | 1 +
tools/testing/selftests/kvm/Makefile | 1 +
.../selftests/kvm/aarch64/arch_timer.c | 382 ++++++++++++++++++
3 files changed, 384 insertions(+)
create mode 100644 tools/testing/selftests/kvm/aarch64/arch_timer.c
diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index 0709af0144c8..5f6cf0c76cf8 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
+/aarch64/arch_timer
/aarch64/debug-exceptions
/aarch64/get-reg-list
/aarch64/vgic_init
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index a170166334a3..62a2c3e4b50c 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -84,6 +84,7 @@ TEST_GEN_PROGS_x86_64 += set_memory_region_test
TEST_GEN_PROGS_x86_64 += steal_time
TEST_GEN_PROGS_x86_64 += kvm_binary_stats_test
+TEST_GEN_PROGS_aarch64 += aarch64/arch_timer
TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
diff --git a/tools/testing/selftests/kvm/aarch64/arch_timer.c b/tools/testing/selftests/kvm/aarch64/arch_timer.c
new file mode 100644
index 000000000000..88333fe1e567
--- /dev/null
+++ b/tools/testing/selftests/kvm/aarch64/arch_timer.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * arch_timer.c - Tests the aarch64 timer IRQ functionality
+ *
+ * The test validates both the virtual and physical timer IRQs using
+ * CVAL and TVAL registers. This consitutes the four stages in the test.
+ * The guest's main thread configures the timer interrupt for a stage
+ * and waits for it to fire, with a timeout equal to the timer period.
+ * It asserts that the timeout doesn't exceed the timer period.
+ *
+ * On the other hand, upon receipt of an interrupt, the guest's interrupt
+ * handler validates the interrupt by checking if the architectural state
+ * is in compliance with the specifications.
+ *
+ * The test provides command-line options to configure the timer's
+ * period (-p), number of vCPUs (-n), and iterations per stage (-i).
+ *
+ * Copyright (c) 2021, Google LLC.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <linux/kvm.h>
+#include <linux/sizes.h>
+
+#include "kvm_util.h"
+#include "processor.h"
+#include "delay.h"
+#include "arch_timer.h"
+#include "gic.h"
+
+#define NR_VCPUS_DEF 4
+#define NR_TEST_ITERS_DEF 5
+#define TIMER_TEST_PERIOD_MS_DEF 10
+#define TIMER_TEST_ERR_MARGIN_US 100
+
+struct test_args {
+ int nr_vcpus;
+ int nr_iter;
+ int timer_period_ms;
+};
+
+static struct test_args test_args = {
+ .nr_vcpus = NR_VCPUS_DEF,
+ .nr_iter = NR_TEST_ITERS_DEF,
+ .timer_period_ms = TIMER_TEST_PERIOD_MS_DEF,
+};
+
+#define msecs_to_usecs(msec) ((msec) * 1000LL)
+
+#define VTIMER_IRQ 27
+#define PTIMER_IRQ 30
+
+#define REDIST_REGION_ATTR_ADDR(count, base, flags, index) \
+ (((uint64_t)(count) << 52) | \
+ ((uint64_t)((base) >> 16) << 16) | \
+ ((uint64_t)(flags) << 12) | \
+ index)
+
+#define GICD_BASE_GPA 0x8000000ULL
+#define GICR_BASE_GPA 0x80A0000ULL
+
+enum guest_stage {
+ GUEST_STAGE_VTIMER_CVAL = 1,
+ GUEST_STAGE_VTIMER_TVAL,
+ GUEST_STAGE_PTIMER_CVAL,
+ GUEST_STAGE_PTIMER_TVAL,
+ GUEST_STAGE_MAX,
+};
+
+/* Sahred variables between host and guest */
+struct test_vcpu_shared_data {
+ int nr_iter;
+ enum guest_stage guest_stage;
+ uint64_t xcnt;
+};
+
+struct test_vcpu {
+ uint32_t vcpuid;
+ pthread_t pt_vcpu_run;
+ struct kvm_vm *vm;
+};
+
+static struct test_vcpu test_vcpu[KVM_MAX_VCPUS];
+static struct test_vcpu_shared_data vcpu_shared_data[KVM_MAX_VCPUS];
+
+static void
+guest_configure_timer_action(struct test_vcpu_shared_data *shared_data)
+{
+ switch (shared_data->guest_stage) {
+ case GUEST_STAGE_VTIMER_CVAL:
+ timer_set_next_cval_ms(VIRTUAL, test_args.timer_period_ms);
+ shared_data->xcnt = timer_get_cntct(VIRTUAL);
+ timer_set_ctl(VIRTUAL, CTL_ENABLE);
+ break;
+ case GUEST_STAGE_VTIMER_TVAL:
+ timer_set_next_tval_ms(VIRTUAL, test_args.timer_period_ms);
+ shared_data->xcnt = timer_get_cntct(VIRTUAL);
+ timer_set_ctl(VIRTUAL, CTL_ENABLE);
+ break;
+ case GUEST_STAGE_PTIMER_CVAL:
+ timer_set_next_cval_ms(PHYSICAL, test_args.timer_period_ms);
+ shared_data->xcnt = timer_get_cntct(PHYSICAL);
+ timer_set_ctl(PHYSICAL, CTL_ENABLE);
+ break;
+ case GUEST_STAGE_PTIMER_TVAL:
+ timer_set_next_tval_ms(PHYSICAL, test_args.timer_period_ms);
+ shared_data->xcnt = timer_get_cntct(PHYSICAL);
+ timer_set_ctl(PHYSICAL, CTL_ENABLE);
+ break;
+ default:
+ GUEST_ASSERT(0);
+ }
+}
+
+static void guest_validate_irq(unsigned int intid,
+ struct test_vcpu_shared_data *shared_data)
+{
+ enum guest_stage stage = shared_data->guest_stage;
+ uint64_t xcnt = 0, xcnt_diff_us, cval = 0;
+ unsigned long xctl = 0;
+ unsigned int timer_irq = 0;
+
+ if (stage == GUEST_STAGE_VTIMER_CVAL ||
+ stage == GUEST_STAGE_VTIMER_TVAL) {
+ xctl = timer_get_ctl(VIRTUAL);
+ timer_set_ctl(VIRTUAL, CTL_IMASK);
+ xcnt = timer_get_cntct(VIRTUAL);
+ cval = timer_get_cval(VIRTUAL);
+ timer_irq = VTIMER_IRQ;
+ } else if (stage == GUEST_STAGE_PTIMER_CVAL ||
+ stage == GUEST_STAGE_PTIMER_TVAL) {
+ xctl = timer_get_ctl(PHYSICAL);
+ timer_set_ctl(PHYSICAL, CTL_IMASK);
+ xcnt = timer_get_cntct(PHYSICAL);
+ cval = timer_get_cval(PHYSICAL);
+ timer_irq = PTIMER_IRQ;
+ } else {
+ GUEST_ASSERT(0);
+ }
+
+ xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt);
+
+ /* Make sure we are dealing with the correct timer IRQ */
+ GUEST_ASSERT_2(intid == timer_irq, intid, timer_irq);
+
+ /* Basic 'timer codition met' check */
+ GUEST_ASSERT_3(xcnt >= cval, xcnt, cval, xcnt_diff_us);
+ GUEST_ASSERT_1(xctl & CTL_ISTATUS, xctl);
+}
+
+static void guest_irq_handler(struct ex_regs *regs)
+{
+ unsigned int intid = gic_get_and_ack_irq();
+ uint32_t cpu = get_vcpuid();
+ struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
+
+ guest_validate_irq(intid, shared_data);
+
+ WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);
+
+ gic_set_eoi(intid);
+}
+
+static void guest_run_stage(struct test_vcpu_shared_data *shared_data,
+ enum guest_stage stage)
+{
+ uint32_t irq_iter, config_iter;
+
+ shared_data->guest_stage = stage;
+ shared_data->nr_iter = 0;
+
+ for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
+ /* Setup the next interrupt */
+ guest_configure_timer_action(shared_data);
+
+ /* Setup a timeout for the interrupt to arrive */
+ udelay(msecs_to_usecs(test_args.timer_period_ms) +
+ TIMER_TEST_ERR_MARGIN_US);
+
+ irq_iter = READ_ONCE(shared_data->nr_iter);
+ GUEST_ASSERT_2(config_iter + 1 == irq_iter,
+ config_iter + 1, irq_iter);
+ };
+}
+
+static void guest_code(void)
+{
+ uint32_t cpu = get_vcpuid();
+ struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
+
+ local_irq_disable();
+
+ gic_init(GIC_V3, test_args.nr_vcpus,
+ (void *)GICD_BASE_GPA, (void *)GICR_BASE_GPA);
+
+ timer_set_ctl(VIRTUAL, CTL_IMASK);
+ timer_set_ctl(PHYSICAL, CTL_IMASK);
+
+ gic_irq_enable(VTIMER_IRQ);
+ gic_irq_enable(PTIMER_IRQ);
+ local_irq_enable();
+
+ guest_run_stage(shared_data, GUEST_STAGE_VTIMER_CVAL);
+ guest_run_stage(shared_data, GUEST_STAGE_VTIMER_TVAL);
+ guest_run_stage(shared_data, GUEST_STAGE_PTIMER_CVAL);
+ guest_run_stage(shared_data, GUEST_STAGE_PTIMER_TVAL);
+
+ GUEST_DONE();
+}
+
+static void *test_vcpu_run(void *arg)
+{
+ struct ucall uc;
+ struct test_vcpu *vcpu = arg;
+ struct kvm_vm *vm = vcpu->vm;
+ uint32_t vcpuid = vcpu->vcpuid;
+ struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[vcpuid];
+
+ vcpu_run(vm, vcpuid);
+
+ switch (get_ucall(vm, vcpuid, &uc)) {
+ case UCALL_SYNC:
+ case UCALL_DONE:
+ break;
+ case UCALL_ABORT:
+ sync_global_from_guest(vm, *shared_data);
+ TEST_ASSERT(false,
+ "%s at %s:%ld\n\tvalues: %lu, %lu; %lu, vcpu: %u; stage: %u; iter: %u",
+ (const char *)uc.args[0], __FILE__, uc.args[1],
+ uc.args[2], uc.args[3], uc.args[4], vcpuid,
+ shared_data->guest_stage, shared_data->nr_iter);
+ break;
+ default:
+ TEST_FAIL("Unexpected guest exit\n");
+ }
+
+ return NULL;
+}
+
+static void test_run(struct kvm_vm *vm)
+{
+ int i, ret;
+
+ for (i = 0; i < test_args.nr_vcpus; i++) {
+ ret = pthread_create(&test_vcpu[i].pt_vcpu_run, NULL,
+ test_vcpu_run, &test_vcpu[i]);
+ TEST_ASSERT(!ret, "Failed to create vCPU-%d pthread\n", i);
+ }
+
+ for (i = 0; i < test_args.nr_vcpus; i++)
+ pthread_join(test_vcpu[i].pt_vcpu_run, NULL);
+}
+
+static void test_vm_setup_gic(struct kvm_vm *vm, unsigned int n_gicr_pages)
+{
+ uint64_t addr;
+ int gic_fd;
+
+ gic_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3, false);
+
+ addr = GICD_BASE_GPA;
+ kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V3_ADDR_TYPE_DIST, &addr, true);
+ virt_pg_map(vm, GICD_BASE_GPA, GICD_BASE_GPA);
+
+ addr = REDIST_REGION_ATTR_ADDR(test_args.nr_vcpus, GICR_BASE_GPA, 0, 0);
+ kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true);
+ virt_map(vm, GICR_BASE_GPA, GICR_BASE_GPA, n_gicr_pages);
+
+ kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
+}
+
+static struct kvm_vm *test_vm_create(void)
+{
+ struct kvm_vm *vm;
+ unsigned int i, n_gicr_pages;
+ int nr_vcpus = test_args.nr_vcpus;
+
+ /* Reserve additional pages for GICR MMIO based on nr_vcpus */
+ n_gicr_pages = vm_calc_num_guest_pages(VM_MODE_DEFAULT,
+ 2 * SZ_64K * nr_vcpus);
+
+ vm = vm_create_default_with_vcpus(nr_vcpus, n_gicr_pages, 0,
+ guest_code, NULL);
+
+ vm_init_descriptor_tables(vm);
+ vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler);
+
+ for (i = 0; i < nr_vcpus; i++) {
+ vcpu_init_descriptor_tables(vm, i);
+
+ test_vcpu[i].vcpuid = i;
+ test_vcpu[i].vm = vm;
+ }
+
+ ucall_init(vm, NULL);
+ test_vm_setup_gic(vm, n_gicr_pages);
+
+ /* Make all the test's cmdline args visible to the guest */
+ sync_global_to_guest(vm, test_args);
+
+ return vm;
+}
+
+static void test_print_help(char *name)
+{
+ pr_info("Usage: %s [-h] [-n nr_vcpus] [-i iterations] [-p timer_period_ms]\n",
+ name);
+ pr_info("\t-n: Number of vCPUs to configure (default: %u; max: %u)\n",
+ NR_VCPUS_DEF, KVM_MAX_VCPUS);
+ pr_info("\t-i: Number of iterations per stage (default: %u)\n",
+ NR_TEST_ITERS_DEF);
+ pr_info("\t-p: Periodicity (in ms) of the guest timer (default: %u)\n",
+ TIMER_TEST_PERIOD_MS_DEF);
+ pr_info("\t-h: print this help screen\n");
+}
+
+static bool parse_args(int argc, char *argv[])
+{
+ int opt;
+
+ while ((opt = getopt(argc, argv, "hn:i:p:")) != -1) {
+ switch (opt) {
+ case 'n':
+ test_args.nr_vcpus = atoi(optarg);
+ if (test_args.nr_vcpus <= 0) {
+ pr_info("Positive value needed for -n\n");
+ goto err;
+ } else if (test_args.nr_vcpus > KVM_MAX_VCPUS) {
+ pr_info("Max allowed vCPUs: %u\n",
+ KVM_MAX_VCPUS);
+ goto err;
+ }
+ break;
+ case 'i':
+ test_args.nr_iter = atoi(optarg);
+ if (test_args.nr_iter <= 0) {
+ pr_info("Positive value needed for -i\n");
+ goto err;
+ }
+ break;
+ case 'p':
+ test_args.timer_period_ms = atoi(optarg);
+ if (test_args.timer_period_ms <= 0) {
+ pr_info("Positive value needed for -p\n");
+ goto err;
+ }
+ break;
+ case 'h':
+ default:
+ goto err;
+ }
+ }
+
+ return true;
+
+err:
+ test_print_help(argv[0]);
+ return false;
+}
+
+int main(int argc, char *argv[])
+{
+ struct kvm_vm *vm;
+
+ /* Tell stdout not to buffer its content */
+ setbuf(stdout, NULL);
+
+ if (!parse_args(argc, argv))
+ exit(KSFT_SKIP);
+
+ vm = test_vm_create();
+ test_run(vm);
+ kvm_vm_free(vm);
+
+ return 0;
+}
--
2.33.0.rc1.237.g0d66db33f3-goog
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v2 10/10] KVM: arm64: selftests: Add arch_timer test
2021-08-18 18:43 ` [PATCH v2 10/10] KVM: arm64: selftests: Add arch_timer test Raghavendra Rao Ananta
@ 2021-08-26 21:54 ` Ricardo Koller
2021-08-27 16:36 ` Raghavendra Rao Ananta
0 siblings, 1 reply; 13+ messages in thread
From: Ricardo Koller @ 2021-08-26 21:54 UTC (permalink / raw)
To: Raghavendra Rao Ananta
Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, kvmarm
On Wed, Aug 18, 2021 at 06:43:11PM +0000, Raghavendra Rao Ananta wrote:
> Add a KVM selftest to validate the arch_timer functionality.
> Primarily, the test sets up periodic timer interrupts and
> validates the basic architectural expectations upon its receipt.
>
> The test provides command-line options to configure the period
> of the timer, number of iterations, and number of vCPUs.
>
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
> tools/testing/selftests/kvm/.gitignore | 1 +
> tools/testing/selftests/kvm/Makefile | 1 +
> .../selftests/kvm/aarch64/arch_timer.c | 382 ++++++++++++++++++
> 3 files changed, 384 insertions(+)
> create mode 100644 tools/testing/selftests/kvm/aarch64/arch_timer.c
>
> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> index 0709af0144c8..5f6cf0c76cf8 100644
> --- a/tools/testing/selftests/kvm/.gitignore
> +++ b/tools/testing/selftests/kvm/.gitignore
> @@ -1,4 +1,5 @@
> # SPDX-License-Identifier: GPL-2.0-only
> +/aarch64/arch_timer
> /aarch64/debug-exceptions
> /aarch64/get-reg-list
> /aarch64/vgic_init
> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> index a170166334a3..62a2c3e4b50c 100644
> --- a/tools/testing/selftests/kvm/Makefile
> +++ b/tools/testing/selftests/kvm/Makefile
> @@ -84,6 +84,7 @@ TEST_GEN_PROGS_x86_64 += set_memory_region_test
> TEST_GEN_PROGS_x86_64 += steal_time
> TEST_GEN_PROGS_x86_64 += kvm_binary_stats_test
>
> +TEST_GEN_PROGS_aarch64 += aarch64/arch_timer
> TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
> TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
> TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
> diff --git a/tools/testing/selftests/kvm/aarch64/arch_timer.c b/tools/testing/selftests/kvm/aarch64/arch_timer.c
> new file mode 100644
> index 000000000000..88333fe1e567
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/aarch64/arch_timer.c
> @@ -0,0 +1,382 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * arch_timer.c - Tests the aarch64 timer IRQ functionality
> + *
> + * The test validates both the virtual and physical timer IRQs using
> + * CVAL and TVAL registers. This consitutes the four stages in the test.
> + * The guest's main thread configures the timer interrupt for a stage
> + * and waits for it to fire, with a timeout equal to the timer period.
> + * It asserts that the timeout doesn't exceed the timer period.
> + *
> + * On the other hand, upon receipt of an interrupt, the guest's interrupt
> + * handler validates the interrupt by checking if the architectural state
> + * is in compliance with the specifications.
> + *
> + * The test provides command-line options to configure the timer's
> + * period (-p), number of vCPUs (-n), and iterations per stage (-i).
> + *
> + * Copyright (c) 2021, Google LLC.
> + */
> +
> +#define _GNU_SOURCE
> +
> +#include <stdlib.h>
> +#include <pthread.h>
> +#include <linux/kvm.h>
> +#include <linux/sizes.h>
> +
> +#include "kvm_util.h"
> +#include "processor.h"
> +#include "delay.h"
> +#include "arch_timer.h"
> +#include "gic.h"
> +
> +#define NR_VCPUS_DEF 4
> +#define NR_TEST_ITERS_DEF 5
> +#define TIMER_TEST_PERIOD_MS_DEF 10
> +#define TIMER_TEST_ERR_MARGIN_US 100
> +
> +struct test_args {
> + int nr_vcpus;
> + int nr_iter;
> + int timer_period_ms;
> +};
> +
> +static struct test_args test_args = {
> + .nr_vcpus = NR_VCPUS_DEF,
> + .nr_iter = NR_TEST_ITERS_DEF,
> + .timer_period_ms = TIMER_TEST_PERIOD_MS_DEF,
> +};
> +
> +#define msecs_to_usecs(msec) ((msec) * 1000LL)
> +
> +#define VTIMER_IRQ 27
> +#define PTIMER_IRQ 30
> +
> +#define REDIST_REGION_ATTR_ADDR(count, base, flags, index) \
> + (((uint64_t)(count) << 52) | \
> + ((uint64_t)((base) >> 16) << 16) | \
> + ((uint64_t)(flags) << 12) | \
> + index)
> +
> +#define GICD_BASE_GPA 0x8000000ULL
> +#define GICR_BASE_GPA 0x80A0000ULL
> +
> +enum guest_stage {
> + GUEST_STAGE_VTIMER_CVAL = 1,
> + GUEST_STAGE_VTIMER_TVAL,
> + GUEST_STAGE_PTIMER_CVAL,
> + GUEST_STAGE_PTIMER_TVAL,
> + GUEST_STAGE_MAX,
> +};
> +
> +/* Sahred variables between host and guest */
> +struct test_vcpu_shared_data {
> + int nr_iter;
> + enum guest_stage guest_stage;
> + uint64_t xcnt;
> +};
> +
> +struct test_vcpu {
> + uint32_t vcpuid;
> + pthread_t pt_vcpu_run;
> + struct kvm_vm *vm;
> +};
> +
> +static struct test_vcpu test_vcpu[KVM_MAX_VCPUS];
> +static struct test_vcpu_shared_data vcpu_shared_data[KVM_MAX_VCPUS];
> +
> +static void
> +guest_configure_timer_action(struct test_vcpu_shared_data *shared_data)
> +{
> + switch (shared_data->guest_stage) {
> + case GUEST_STAGE_VTIMER_CVAL:
> + timer_set_next_cval_ms(VIRTUAL, test_args.timer_period_ms);
> + shared_data->xcnt = timer_get_cntct(VIRTUAL);
> + timer_set_ctl(VIRTUAL, CTL_ENABLE);
> + break;
> + case GUEST_STAGE_VTIMER_TVAL:
> + timer_set_next_tval_ms(VIRTUAL, test_args.timer_period_ms);
> + shared_data->xcnt = timer_get_cntct(VIRTUAL);
> + timer_set_ctl(VIRTUAL, CTL_ENABLE);
> + break;
> + case GUEST_STAGE_PTIMER_CVAL:
> + timer_set_next_cval_ms(PHYSICAL, test_args.timer_period_ms);
> + shared_data->xcnt = timer_get_cntct(PHYSICAL);
> + timer_set_ctl(PHYSICAL, CTL_ENABLE);
> + break;
> + case GUEST_STAGE_PTIMER_TVAL:
> + timer_set_next_tval_ms(PHYSICAL, test_args.timer_period_ms);
> + shared_data->xcnt = timer_get_cntct(PHYSICAL);
> + timer_set_ctl(PHYSICAL, CTL_ENABLE);
> + break;
> + default:
> + GUEST_ASSERT(0);
> + }
> +}
> +
> +static void guest_validate_irq(unsigned int intid,
> + struct test_vcpu_shared_data *shared_data)
> +{
> + enum guest_stage stage = shared_data->guest_stage;
> + uint64_t xcnt = 0, xcnt_diff_us, cval = 0;
> + unsigned long xctl = 0;
> + unsigned int timer_irq = 0;
> +
> + if (stage == GUEST_STAGE_VTIMER_CVAL ||
> + stage == GUEST_STAGE_VTIMER_TVAL) {
> + xctl = timer_get_ctl(VIRTUAL);
> + timer_set_ctl(VIRTUAL, CTL_IMASK);
> + xcnt = timer_get_cntct(VIRTUAL);
> + cval = timer_get_cval(VIRTUAL);
> + timer_irq = VTIMER_IRQ;
> + } else if (stage == GUEST_STAGE_PTIMER_CVAL ||
> + stage == GUEST_STAGE_PTIMER_TVAL) {
> + xctl = timer_get_ctl(PHYSICAL);
> + timer_set_ctl(PHYSICAL, CTL_IMASK);
> + xcnt = timer_get_cntct(PHYSICAL);
> + cval = timer_get_cval(PHYSICAL);
> + timer_irq = PTIMER_IRQ;
> + } else {
> + GUEST_ASSERT(0);
> + }
> +
> + xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt);
> +
> + /* Make sure we are dealing with the correct timer IRQ */
> + GUEST_ASSERT_2(intid == timer_irq, intid, timer_irq);
> +
> + /* Basic 'timer codition met' check */
> + GUEST_ASSERT_3(xcnt >= cval, xcnt, cval, xcnt_diff_us);
> + GUEST_ASSERT_1(xctl & CTL_ISTATUS, xctl);
> +}
> +
> +static void guest_irq_handler(struct ex_regs *regs)
> +{
> + unsigned int intid = gic_get_and_ack_irq();
> + uint32_t cpu = get_vcpuid();
> + struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
> +
> + guest_validate_irq(intid, shared_data);
> +
> + WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);
> +
> + gic_set_eoi(intid);
> +}
> +
> +static void guest_run_stage(struct test_vcpu_shared_data *shared_data,
> + enum guest_stage stage)
> +{
> + uint32_t irq_iter, config_iter;
> +
> + shared_data->guest_stage = stage;
> + shared_data->nr_iter = 0;
> +
> + for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
> + /* Setup the next interrupt */
> + guest_configure_timer_action(shared_data);
> +
> + /* Setup a timeout for the interrupt to arrive */
> + udelay(msecs_to_usecs(test_args.timer_period_ms) +
> + TIMER_TEST_ERR_MARGIN_US);
> +
> + irq_iter = READ_ONCE(shared_data->nr_iter);
> + GUEST_ASSERT_2(config_iter + 1 == irq_iter,
> + config_iter + 1, irq_iter);
> + };
> +}
> +
> +static void guest_code(void)
> +{
> + uint32_t cpu = get_vcpuid();
> + struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
> +
> + local_irq_disable();
> +
> + gic_init(GIC_V3, test_args.nr_vcpus,
> + (void *)GICD_BASE_GPA, (void *)GICR_BASE_GPA);
> +
> + timer_set_ctl(VIRTUAL, CTL_IMASK);
> + timer_set_ctl(PHYSICAL, CTL_IMASK);
> +
> + gic_irq_enable(VTIMER_IRQ);
> + gic_irq_enable(PTIMER_IRQ);
> + local_irq_enable();
> +
> + guest_run_stage(shared_data, GUEST_STAGE_VTIMER_CVAL);
> + guest_run_stage(shared_data, GUEST_STAGE_VTIMER_TVAL);
> + guest_run_stage(shared_data, GUEST_STAGE_PTIMER_CVAL);
> + guest_run_stage(shared_data, GUEST_STAGE_PTIMER_TVAL);
> +
> + GUEST_DONE();
> +}
> +
> +static void *test_vcpu_run(void *arg)
> +{
> + struct ucall uc;
> + struct test_vcpu *vcpu = arg;
> + struct kvm_vm *vm = vcpu->vm;
> + uint32_t vcpuid = vcpu->vcpuid;
> + struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[vcpuid];
> +
> + vcpu_run(vm, vcpuid);
> +
> + switch (get_ucall(vm, vcpuid, &uc)) {
> + case UCALL_SYNC:
> + case UCALL_DONE:
> + break;
> + case UCALL_ABORT:
> + sync_global_from_guest(vm, *shared_data);
> + TEST_ASSERT(false,
> + "%s at %s:%ld\n\tvalues: %lu, %lu; %lu, vcpu: %u; stage: %u; iter: %u",
> + (const char *)uc.args[0], __FILE__, uc.args[1],
> + uc.args[2], uc.args[3], uc.args[4], vcpuid,
> + shared_data->guest_stage, shared_data->nr_iter);
> + break;
> + default:
> + TEST_FAIL("Unexpected guest exit\n");
> + }
> +
> + return NULL;
> +}
> +
> +static void test_run(struct kvm_vm *vm)
> +{
> + int i, ret;
> +
> + for (i = 0; i < test_args.nr_vcpus; i++) {
> + ret = pthread_create(&test_vcpu[i].pt_vcpu_run, NULL,
> + test_vcpu_run, &test_vcpu[i]);
> + TEST_ASSERT(!ret, "Failed to create vCPU-%d pthread\n", i);
> + }
> +
> + for (i = 0; i < test_args.nr_vcpus; i++)
> + pthread_join(test_vcpu[i].pt_vcpu_run, NULL);
> +}
> +
> +static void test_vm_setup_gic(struct kvm_vm *vm, unsigned int n_gicr_pages)
I think it's nicer if this takes nr_vcpus instead of gicr_pages.
And, it possible, it would be doubly nice if it could be be made a
library function (e.g., inside lib/aarch64/vgic.c?).
> +{
> + uint64_t addr;
> + int gic_fd;
> +
> + gic_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3, false);
> +
> + addr = GICD_BASE_GPA;
> + kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
> + KVM_VGIC_V3_ADDR_TYPE_DIST, &addr, true);
> + virt_pg_map(vm, GICD_BASE_GPA, GICD_BASE_GPA);
Not really a big deal at the moment, but if KVM implemented ESPIs, then
a 4k page wouldn't be enough. Why not map 64Ks as there's space for it?
> +
> + addr = REDIST_REGION_ATTR_ADDR(test_args.nr_vcpus, GICR_BASE_GPA, 0, 0);
> + kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
> + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true);
> + virt_map(vm, GICR_BASE_GPA, GICR_BASE_GPA, n_gicr_pages);
> +
> + kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
> + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
> +}
> +
> +static struct kvm_vm *test_vm_create(void)
> +{
> + struct kvm_vm *vm;
> + unsigned int i, n_gicr_pages;
> + int nr_vcpus = test_args.nr_vcpus;
> +
> + /* Reserve additional pages for GICR MMIO based on nr_vcpus */
> + n_gicr_pages = vm_calc_num_guest_pages(VM_MODE_DEFAULT,
> + 2 * SZ_64K * nr_vcpus);
Might be better to use vm_get_page_size instead as it doesn't need the
mode arg. Note that you would have to call it after creating the vm
(vm_create..), but the nice thing is that that call uses the actual mode
that the vm was created with. I propose moving this pages calculation to
inside test_vm_setup_gic, which could take the number of vcpus as an arg
(instead of nr_pages).
> +
> + vm = vm_create_default_with_vcpus(nr_vcpus, n_gicr_pages, 0,
> + guest_code, NULL);
> +
> + vm_init_descriptor_tables(vm);
> + vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler);
> +
> + for (i = 0; i < nr_vcpus; i++) {
> + vcpu_init_descriptor_tables(vm, i);
> +
> + test_vcpu[i].vcpuid = i;
> + test_vcpu[i].vm = vm;
> + }
> +
> + ucall_init(vm, NULL);
> + test_vm_setup_gic(vm, n_gicr_pages);
> +
> + /* Make all the test's cmdline args visible to the guest */
> + sync_global_to_guest(vm, test_args);
> +
> + return vm;
> +}
> +
> +static void test_print_help(char *name)
> +{
> + pr_info("Usage: %s [-h] [-n nr_vcpus] [-i iterations] [-p timer_period_ms]\n",
> + name);
> + pr_info("\t-n: Number of vCPUs to configure (default: %u; max: %u)\n",
> + NR_VCPUS_DEF, KVM_MAX_VCPUS);
> + pr_info("\t-i: Number of iterations per stage (default: %u)\n",
> + NR_TEST_ITERS_DEF);
> + pr_info("\t-p: Periodicity (in ms) of the guest timer (default: %u)\n",
> + TIMER_TEST_PERIOD_MS_DEF);
> + pr_info("\t-h: print this help screen\n");
> +}
> +
> +static bool parse_args(int argc, char *argv[])
> +{
> + int opt;
> +
> + while ((opt = getopt(argc, argv, "hn:i:p:")) != -1) {
> + switch (opt) {
> + case 'n':
> + test_args.nr_vcpus = atoi(optarg);
> + if (test_args.nr_vcpus <= 0) {
> + pr_info("Positive value needed for -n\n");
> + goto err;
> + } else if (test_args.nr_vcpus > KVM_MAX_VCPUS) {
> + pr_info("Max allowed vCPUs: %u\n",
> + KVM_MAX_VCPUS);
> + goto err;
> + }
> + break;
> + case 'i':
> + test_args.nr_iter = atoi(optarg);
> + if (test_args.nr_iter <= 0) {
> + pr_info("Positive value needed for -i\n");
> + goto err;
> + }
> + break;
> + case 'p':
> + test_args.timer_period_ms = atoi(optarg);
> + if (test_args.timer_period_ms <= 0) {
> + pr_info("Positive value needed for -p\n");
> + goto err;
> + }
> + break;
> + case 'h':
> + default:
> + goto err;
> + }
> + }
> +
> + return true;
> +
> +err:
> + test_print_help(argv[0]);
> + return false;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + struct kvm_vm *vm;
> +
> + /* Tell stdout not to buffer its content */
> + setbuf(stdout, NULL);
> +
> + if (!parse_args(argc, argv))
> + exit(KSFT_SKIP);
> +
> + vm = test_vm_create();
> + test_run(vm);
> + kvm_vm_free(vm);
> +
> + return 0;
> +}
> --
> 2.33.0.rc1.237.g0d66db33f3-goog
>
Thanks,
Ricardo
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v2 10/10] KVM: arm64: selftests: Add arch_timer test
2021-08-26 21:54 ` Ricardo Koller
@ 2021-08-27 16:36 ` Raghavendra Rao Ananta
0 siblings, 0 replies; 13+ messages in thread
From: Raghavendra Rao Ananta @ 2021-08-27 16:36 UTC (permalink / raw)
To: Ricardo Koller; +Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, kvmarm
On Thu, Aug 26, 2021 at 2:54 PM Ricardo Koller <ricarkol@google.com> wrote:
>
> On Wed, Aug 18, 2021 at 06:43:11PM +0000, Raghavendra Rao Ananta wrote:
> > Add a KVM selftest to validate the arch_timer functionality.
> > Primarily, the test sets up periodic timer interrupts and
> > validates the basic architectural expectations upon its receipt.
> >
> > The test provides command-line options to configure the period
> > of the timer, number of iterations, and number of vCPUs.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> > tools/testing/selftests/kvm/.gitignore | 1 +
> > tools/testing/selftests/kvm/Makefile | 1 +
> > .../selftests/kvm/aarch64/arch_timer.c | 382 ++++++++++++++++++
> > 3 files changed, 384 insertions(+)
> > create mode 100644 tools/testing/selftests/kvm/aarch64/arch_timer.c
> >
> > diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> > index 0709af0144c8..5f6cf0c76cf8 100644
> > --- a/tools/testing/selftests/kvm/.gitignore
> > +++ b/tools/testing/selftests/kvm/.gitignore
> > @@ -1,4 +1,5 @@
> > # SPDX-License-Identifier: GPL-2.0-only
> > +/aarch64/arch_timer
> > /aarch64/debug-exceptions
> > /aarch64/get-reg-list
> > /aarch64/vgic_init
> > diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> > index a170166334a3..62a2c3e4b50c 100644
> > --- a/tools/testing/selftests/kvm/Makefile
> > +++ b/tools/testing/selftests/kvm/Makefile
> > @@ -84,6 +84,7 @@ TEST_GEN_PROGS_x86_64 += set_memory_region_test
> > TEST_GEN_PROGS_x86_64 += steal_time
> > TEST_GEN_PROGS_x86_64 += kvm_binary_stats_test
> >
> > +TEST_GEN_PROGS_aarch64 += aarch64/arch_timer
> > TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
> > TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
> > TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
> > diff --git a/tools/testing/selftests/kvm/aarch64/arch_timer.c b/tools/testing/selftests/kvm/aarch64/arch_timer.c
> > new file mode 100644
> > index 000000000000..88333fe1e567
> > --- /dev/null
> > +++ b/tools/testing/selftests/kvm/aarch64/arch_timer.c
> > @@ -0,0 +1,382 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * arch_timer.c - Tests the aarch64 timer IRQ functionality
> > + *
> > + * The test validates both the virtual and physical timer IRQs using
> > + * CVAL and TVAL registers. This consitutes the four stages in the test.
> > + * The guest's main thread configures the timer interrupt for a stage
> > + * and waits for it to fire, with a timeout equal to the timer period.
> > + * It asserts that the timeout doesn't exceed the timer period.
> > + *
> > + * On the other hand, upon receipt of an interrupt, the guest's interrupt
> > + * handler validates the interrupt by checking if the architectural state
> > + * is in compliance with the specifications.
> > + *
> > + * The test provides command-line options to configure the timer's
> > + * period (-p), number of vCPUs (-n), and iterations per stage (-i).
> > + *
> > + * Copyright (c) 2021, Google LLC.
> > + */
> > +
> > +#define _GNU_SOURCE
> > +
> > +#include <stdlib.h>
> > +#include <pthread.h>
> > +#include <linux/kvm.h>
> > +#include <linux/sizes.h>
> > +
> > +#include "kvm_util.h"
> > +#include "processor.h"
> > +#include "delay.h"
> > +#include "arch_timer.h"
> > +#include "gic.h"
> > +
> > +#define NR_VCPUS_DEF 4
> > +#define NR_TEST_ITERS_DEF 5
> > +#define TIMER_TEST_PERIOD_MS_DEF 10
> > +#define TIMER_TEST_ERR_MARGIN_US 100
> > +
> > +struct test_args {
> > + int nr_vcpus;
> > + int nr_iter;
> > + int timer_period_ms;
> > +};
> > +
> > +static struct test_args test_args = {
> > + .nr_vcpus = NR_VCPUS_DEF,
> > + .nr_iter = NR_TEST_ITERS_DEF,
> > + .timer_period_ms = TIMER_TEST_PERIOD_MS_DEF,
> > +};
> > +
> > +#define msecs_to_usecs(msec) ((msec) * 1000LL)
> > +
> > +#define VTIMER_IRQ 27
> > +#define PTIMER_IRQ 30
> > +
> > +#define REDIST_REGION_ATTR_ADDR(count, base, flags, index) \
> > + (((uint64_t)(count) << 52) | \
> > + ((uint64_t)((base) >> 16) << 16) | \
> > + ((uint64_t)(flags) << 12) | \
> > + index)
> > +
> > +#define GICD_BASE_GPA 0x8000000ULL
> > +#define GICR_BASE_GPA 0x80A0000ULL
> > +
> > +enum guest_stage {
> > + GUEST_STAGE_VTIMER_CVAL = 1,
> > + GUEST_STAGE_VTIMER_TVAL,
> > + GUEST_STAGE_PTIMER_CVAL,
> > + GUEST_STAGE_PTIMER_TVAL,
> > + GUEST_STAGE_MAX,
> > +};
> > +
> > +/* Sahred variables between host and guest */
> > +struct test_vcpu_shared_data {
> > + int nr_iter;
> > + enum guest_stage guest_stage;
> > + uint64_t xcnt;
> > +};
> > +
> > +struct test_vcpu {
> > + uint32_t vcpuid;
> > + pthread_t pt_vcpu_run;
> > + struct kvm_vm *vm;
> > +};
> > +
> > +static struct test_vcpu test_vcpu[KVM_MAX_VCPUS];
> > +static struct test_vcpu_shared_data vcpu_shared_data[KVM_MAX_VCPUS];
> > +
> > +static void
> > +guest_configure_timer_action(struct test_vcpu_shared_data *shared_data)
> > +{
> > + switch (shared_data->guest_stage) {
> > + case GUEST_STAGE_VTIMER_CVAL:
> > + timer_set_next_cval_ms(VIRTUAL, test_args.timer_period_ms);
> > + shared_data->xcnt = timer_get_cntct(VIRTUAL);
> > + timer_set_ctl(VIRTUAL, CTL_ENABLE);
> > + break;
> > + case GUEST_STAGE_VTIMER_TVAL:
> > + timer_set_next_tval_ms(VIRTUAL, test_args.timer_period_ms);
> > + shared_data->xcnt = timer_get_cntct(VIRTUAL);
> > + timer_set_ctl(VIRTUAL, CTL_ENABLE);
> > + break;
> > + case GUEST_STAGE_PTIMER_CVAL:
> > + timer_set_next_cval_ms(PHYSICAL, test_args.timer_period_ms);
> > + shared_data->xcnt = timer_get_cntct(PHYSICAL);
> > + timer_set_ctl(PHYSICAL, CTL_ENABLE);
> > + break;
> > + case GUEST_STAGE_PTIMER_TVAL:
> > + timer_set_next_tval_ms(PHYSICAL, test_args.timer_period_ms);
> > + shared_data->xcnt = timer_get_cntct(PHYSICAL);
> > + timer_set_ctl(PHYSICAL, CTL_ENABLE);
> > + break;
> > + default:
> > + GUEST_ASSERT(0);
> > + }
> > +}
> > +
> > +static void guest_validate_irq(unsigned int intid,
> > + struct test_vcpu_shared_data *shared_data)
> > +{
> > + enum guest_stage stage = shared_data->guest_stage;
> > + uint64_t xcnt = 0, xcnt_diff_us, cval = 0;
> > + unsigned long xctl = 0;
> > + unsigned int timer_irq = 0;
> > +
> > + if (stage == GUEST_STAGE_VTIMER_CVAL ||
> > + stage == GUEST_STAGE_VTIMER_TVAL) {
> > + xctl = timer_get_ctl(VIRTUAL);
> > + timer_set_ctl(VIRTUAL, CTL_IMASK);
> > + xcnt = timer_get_cntct(VIRTUAL);
> > + cval = timer_get_cval(VIRTUAL);
> > + timer_irq = VTIMER_IRQ;
> > + } else if (stage == GUEST_STAGE_PTIMER_CVAL ||
> > + stage == GUEST_STAGE_PTIMER_TVAL) {
> > + xctl = timer_get_ctl(PHYSICAL);
> > + timer_set_ctl(PHYSICAL, CTL_IMASK);
> > + xcnt = timer_get_cntct(PHYSICAL);
> > + cval = timer_get_cval(PHYSICAL);
> > + timer_irq = PTIMER_IRQ;
> > + } else {
> > + GUEST_ASSERT(0);
> > + }
> > +
> > + xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt);
> > +
> > + /* Make sure we are dealing with the correct timer IRQ */
> > + GUEST_ASSERT_2(intid == timer_irq, intid, timer_irq);
> > +
> > + /* Basic 'timer codition met' check */
> > + GUEST_ASSERT_3(xcnt >= cval, xcnt, cval, xcnt_diff_us);
> > + GUEST_ASSERT_1(xctl & CTL_ISTATUS, xctl);
> > +}
> > +
> > +static void guest_irq_handler(struct ex_regs *regs)
> > +{
> > + unsigned int intid = gic_get_and_ack_irq();
> > + uint32_t cpu = get_vcpuid();
> > + struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
> > +
> > + guest_validate_irq(intid, shared_data);
> > +
> > + WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);
> > +
> > + gic_set_eoi(intid);
> > +}
> > +
> > +static void guest_run_stage(struct test_vcpu_shared_data *shared_data,
> > + enum guest_stage stage)
> > +{
> > + uint32_t irq_iter, config_iter;
> > +
> > + shared_data->guest_stage = stage;
> > + shared_data->nr_iter = 0;
> > +
> > + for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
> > + /* Setup the next interrupt */
> > + guest_configure_timer_action(shared_data);
> > +
> > + /* Setup a timeout for the interrupt to arrive */
> > + udelay(msecs_to_usecs(test_args.timer_period_ms) +
> > + TIMER_TEST_ERR_MARGIN_US);
> > +
> > + irq_iter = READ_ONCE(shared_data->nr_iter);
> > + GUEST_ASSERT_2(config_iter + 1 == irq_iter,
> > + config_iter + 1, irq_iter);
> > + };
> > +}
> > +
> > +static void guest_code(void)
> > +{
> > + uint32_t cpu = get_vcpuid();
> > + struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
> > +
> > + local_irq_disable();
> > +
> > + gic_init(GIC_V3, test_args.nr_vcpus,
> > + (void *)GICD_BASE_GPA, (void *)GICR_BASE_GPA);
> > +
> > + timer_set_ctl(VIRTUAL, CTL_IMASK);
> > + timer_set_ctl(PHYSICAL, CTL_IMASK);
> > +
> > + gic_irq_enable(VTIMER_IRQ);
> > + gic_irq_enable(PTIMER_IRQ);
> > + local_irq_enable();
> > +
> > + guest_run_stage(shared_data, GUEST_STAGE_VTIMER_CVAL);
> > + guest_run_stage(shared_data, GUEST_STAGE_VTIMER_TVAL);
> > + guest_run_stage(shared_data, GUEST_STAGE_PTIMER_CVAL);
> > + guest_run_stage(shared_data, GUEST_STAGE_PTIMER_TVAL);
> > +
> > + GUEST_DONE();
> > +}
> > +
> > +static void *test_vcpu_run(void *arg)
> > +{
> > + struct ucall uc;
> > + struct test_vcpu *vcpu = arg;
> > + struct kvm_vm *vm = vcpu->vm;
> > + uint32_t vcpuid = vcpu->vcpuid;
> > + struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[vcpuid];
> > +
> > + vcpu_run(vm, vcpuid);
> > +
> > + switch (get_ucall(vm, vcpuid, &uc)) {
> > + case UCALL_SYNC:
> > + case UCALL_DONE:
> > + break;
> > + case UCALL_ABORT:
> > + sync_global_from_guest(vm, *shared_data);
> > + TEST_ASSERT(false,
> > + "%s at %s:%ld\n\tvalues: %lu, %lu; %lu, vcpu: %u; stage: %u; iter: %u",
> > + (const char *)uc.args[0], __FILE__, uc.args[1],
> > + uc.args[2], uc.args[3], uc.args[4], vcpuid,
> > + shared_data->guest_stage, shared_data->nr_iter);
> > + break;
> > + default:
> > + TEST_FAIL("Unexpected guest exit\n");
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +static void test_run(struct kvm_vm *vm)
> > +{
> > + int i, ret;
> > +
> > + for (i = 0; i < test_args.nr_vcpus; i++) {
> > + ret = pthread_create(&test_vcpu[i].pt_vcpu_run, NULL,
> > + test_vcpu_run, &test_vcpu[i]);
> > + TEST_ASSERT(!ret, "Failed to create vCPU-%d pthread\n", i);
> > + }
> > +
> > + for (i = 0; i < test_args.nr_vcpus; i++)
> > + pthread_join(test_vcpu[i].pt_vcpu_run, NULL);
> > +}
> > +
> > +static void test_vm_setup_gic(struct kvm_vm *vm, unsigned int n_gicr_pages)
>
> I think it's nicer if this takes nr_vcpus instead of gicr_pages.
>
> And, it possible, it would be doubly nice if it could be be made a
> library function (e.g., inside lib/aarch64/vgic.c?).
>
Yeah, great idea. I had the same suggestion from Reiji as well.
> > +{
> > + uint64_t addr;
> > + int gic_fd;
> > +
> > + gic_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3, false);
> > +
> > + addr = GICD_BASE_GPA;
> > + kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
> > + KVM_VGIC_V3_ADDR_TYPE_DIST, &addr, true);
> > + virt_pg_map(vm, GICD_BASE_GPA, GICD_BASE_GPA);
>
> Not really a big deal at the moment, but if KVM implemented ESPIs, then
> a 4k page wouldn't be enough. Why not map 64Ks as there's space for it?
>
Let's fix it while we are adding a new lib function.
> > +
> > + addr = REDIST_REGION_ATTR_ADDR(test_args.nr_vcpus, GICR_BASE_GPA, 0, 0);
> > + kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
> > + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true);
> > + virt_map(vm, GICR_BASE_GPA, GICR_BASE_GPA, n_gicr_pages);
> > +
> > + kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
> > + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
> > +}
> > +
> > +static struct kvm_vm *test_vm_create(void)
> > +{
> > + struct kvm_vm *vm;
> > + unsigned int i, n_gicr_pages;
> > + int nr_vcpus = test_args.nr_vcpus;
> > +
> > + /* Reserve additional pages for GICR MMIO based on nr_vcpus */
> > + n_gicr_pages = vm_calc_num_guest_pages(VM_MODE_DEFAULT,
> > + 2 * SZ_64K * nr_vcpus);
>
> Might be better to use vm_get_page_size instead as it doesn't need the
> mode arg. Note that you would have to call it after creating the vm
> (vm_create..), but the nice thing is that that call uses the actual mode
> that the vm was created with. I propose moving this pages calculation to
> inside test_vm_setup_gic, which could take the number of vcpus as an arg
> (instead of nr_pages).
>
My idea was to reserve all the pages that we need in one go and the fact that
we know that we'd be creating a VM in a 'default' mode. But now that we are
moving to a lib function, I'll move the logic there.
Thanks for the suggestions.
Regards,
Raghavendra
> > +
> > + vm = vm_create_default_with_vcpus(nr_vcpus, n_gicr_pages, 0,
> > + guest_code, NULL);
> > +
> > + vm_init_descriptor_tables(vm);
> > + vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler);
> > +
> > + for (i = 0; i < nr_vcpus; i++) {
> > + vcpu_init_descriptor_tables(vm, i);
> > +
> > + test_vcpu[i].vcpuid = i;
> > + test_vcpu[i].vm = vm;
> > + }
> > +
> > + ucall_init(vm, NULL);
> > + test_vm_setup_gic(vm, n_gicr_pages);
> > +
> > + /* Make all the test's cmdline args visible to the guest */
> > + sync_global_to_guest(vm, test_args);
> > +
> > + return vm;
> > +}
> > +
> > +static void test_print_help(char *name)
> > +{
> > + pr_info("Usage: %s [-h] [-n nr_vcpus] [-i iterations] [-p timer_period_ms]\n",
> > + name);
> > + pr_info("\t-n: Number of vCPUs to configure (default: %u; max: %u)\n",
> > + NR_VCPUS_DEF, KVM_MAX_VCPUS);
> > + pr_info("\t-i: Number of iterations per stage (default: %u)\n",
> > + NR_TEST_ITERS_DEF);
> > + pr_info("\t-p: Periodicity (in ms) of the guest timer (default: %u)\n",
> > + TIMER_TEST_PERIOD_MS_DEF);
> > + pr_info("\t-h: print this help screen\n");
> > +}
> > +
> > +static bool parse_args(int argc, char *argv[])
> > +{
> > + int opt;
> > +
> > + while ((opt = getopt(argc, argv, "hn:i:p:")) != -1) {
> > + switch (opt) {
> > + case 'n':
> > + test_args.nr_vcpus = atoi(optarg);
> > + if (test_args.nr_vcpus <= 0) {
> > + pr_info("Positive value needed for -n\n");
> > + goto err;
> > + } else if (test_args.nr_vcpus > KVM_MAX_VCPUS) {
> > + pr_info("Max allowed vCPUs: %u\n",
> > + KVM_MAX_VCPUS);
> > + goto err;
> > + }
> > + break;
> > + case 'i':
> > + test_args.nr_iter = atoi(optarg);
> > + if (test_args.nr_iter <= 0) {
> > + pr_info("Positive value needed for -i\n");
> > + goto err;
> > + }
> > + break;
> > + case 'p':
> > + test_args.timer_period_ms = atoi(optarg);
> > + if (test_args.timer_period_ms <= 0) {
> > + pr_info("Positive value needed for -p\n");
> > + goto err;
> > + }
> > + break;
> > + case 'h':
> > + default:
> > + goto err;
> > + }
> > + }
> > +
> > + return true;
> > +
> > +err:
> > + test_print_help(argv[0]);
> > + return false;
> > +}
> > +
> > +int main(int argc, char *argv[])
> > +{
> > + struct kvm_vm *vm;
> > +
> > + /* Tell stdout not to buffer its content */
> > + setbuf(stdout, NULL);
> > +
> > + if (!parse_args(argc, argv))
> > + exit(KSFT_SKIP);
> > +
> > + vm = test_vm_create();
> > + test_run(vm);
> > + kvm_vm_free(vm);
> > +
> > + return 0;
> > +}
> > --
> > 2.33.0.rc1.237.g0d66db33f3-goog
> >
>
> Thanks,
> Ricardo
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
^ permalink raw reply [flat|nested] 13+ messages in thread