Skip to contentSkip to conversation

KVM/ARM Implementation

PATCH Applied Christoffer Dall 5 participants 54 messages Jun 15, 2012 – Jul 2, 2012

Subsystems (8)

v8 of 10 Applied Latest is v10 →

lore
Share & tools
lore
#0 lore raw

v8 cover KVM/ARM Implementation

The following series implements KVM support for ARM processors, specifically on the Cortex A-15 platform. Work is done in collaboration between Columbia University, Virtual Open Systems and ARM/Linaro.

The patch series applies to kvm/next, specifically commit:
25e531a988ea5a64bd97a72dc9d3c65ad5850120

This is Version 8 of the patch series, but the first two versions were reviewed outside of the KVM mailing list. Changes can also be pulled from:
git://github.com/virtualopensystems/linux-kvm-arm.git kvm-a15-v8

A non-flattened edition of the patch series can be found at:
git://github.com/virtualopensystems/linux-kvm-arm.git kvm-a15-v8-stage

The implementation is broken up into a logical set of patches, the first four are preparatory patches:
1. Add mem_type prot_pte accessor (ARM community)
2. Use KVM_CAP_IRQ_ROUTING to protect routing code (KVM community)
3. Introduce __KVM_HAVE_IRQ_LINE (KVM community)
4. Guard code with CONFIG_MMU_NOTIFIER (KVM community)

KVM guys, please consider pulling the KVM generic patches as early as possible. Thanks.

The main implementation is broken up into separate patches, the first containing a skeleton of files, makefile changes, the basic user space interface and KVM architecture specific stubs. Subsequent patches implement parts of the system as listed:

 1.  Preparatory patch introducing __KVM_HAVE_IRQ_LINE
 2.  Preparatory patch guarding mmu_notifier code with CONFIG_MMU_NOTIFIER
 3.  Skeleton
 4.  Identity Mapping for Hyp mode
 5.  Hypervisor initialization
 6.  Hypervisor module unloading
 7.  Memory virtualization setup (hyp mode mappings and 2nd stage)
 8.  Inject IRQs and FIQs from userspace
 9.  World-switch implementation and Hyp exception vectors

10. Emulation framework and CP15 emulation
11. Handle guest user memory aborts
12. Handle guest MMIO aborts
13. Support guest wait-for-interrupt instructions

Testing:
Limited testing, but have run GCC inside guest, which compiled a small hello-world program, which was successfully run. For v8 both ARM/Thumb-2 kernels were tested as both host/guest and both a compiled-in version and a kernel module version of KVM was tested. Hardware still unavailable to me, so all testing has been done on ARM Fast Models.

For a guide on how to set up a testing environment and try out these patches, see:
http://www.virtualopensystems.com/media/pdf/kvm-arm-guide.pdf

There is an issue list available using the issue tracker on:
https://github.com/virtualopensystems/linux-kvm-arm

Additionally a few major milestones are coming up shortly:
- Support Thumb MMIO emulation and test MMIO emulation code
- Use section-based permanent identity mappings for init code.
- Merge Marc Zyngier's patch series for VGIC and timers (review in progress)
- Change from SMC based install to relying on booting the kernel in Hyp mode. This requires some larger changes, but will allow a guest kernel to boot with KVM configured.
- Guest NEON/VFP support (work-in-progress from Virtual Open Systems)

Changes since v7:
- Traps accesses to ACTLR
- Do not trap WFE execution
- Upgrade barriers and TLB operations to inner-shareable domain
- Restrucure hyp_pgd related code to be more opaque
- Random SMP fixes
- Random BUG fixes
- Improve commenting
- Support module loading/unloading of KVM/ARM
- Thumb-2 support for host kernel and KVM
- Unaligned cross-page wide guest Thumb instruction fetching
- Support ITSTATE fields in CPSR for Thumb guests
- Document HCR settings

Changes since v6:
- Support for MMU notifiers to not pin user pages in memory
- Suport build with log debugging
- Bugfix: v6 clobbered r7 in init code
- Simplify hyp code mapping
- Cleanup of register access code
- Table-based CP15 emulation from Rusty Russell
- Various other bug fixes and cleanups

Changes since v5:
- General bugfixes and nit fixes from reviews
- Implemented re-use of VMIDs
- Cleaned up the Hyp-mapping code to be readable by non-mm hackers (including myself)
- Integrated preliminary SMP support in base patches
- Lock-less interrupt injection and WFI support
- Fixed signal-handling in while in guest (increases overall stability)

Changes since v4:
- Addressed reviewer comments from v4
* cleanup debug and trace code
* remove printks
* fixup kvm_arch_vcpu_ioctl_run
* add trace details to mmio emulation
- Fix from Marc Zyngier: Move kvm_guest_enter/exit into non-preemptible section (squashed into world-switch patch)
- Cleanup create_hyp_mappings/remove_hyp_mappings from Marc Zyngier (squashed into hypervisor initialization patch)
- Removed the remove_hyp_mappings feature. Removing hypervisor mappings could potentially unmap other important data shared in the same page.
- Removed the arm_ prefix from the arch-specific files.
- Initial SMP host/guest support

Changes since v3:
- v4 actually works, fully boots a guest
- Support compiling as a module
- Use static inlines instead of macros for vcpu_reg and friends
- Optimize kvm_vcpu_reg function
- Use Ftrace for trace capabilities
- Updated documentation and commenting
- Use KVM_IRQ_LINE instead of KVM_INTERRUPT
- Emulates load/store instructions not supported through HSR syndrome information.
- Frees 2nd stage translation tables on VM teardown
- Handles IRQ/FIQ instructions
- Handles more CP15 accesses
- Support guest WFI calls
- Uses debugfs instead of /proc
- Support compiling in Thumb mode

Changes since v2:
- Performs world-switch code
- Maps guest memory using 2nd stage translation
- Emulates co-processor 15 instructions
- Forwards I/O faults to QEMU.

---

Christoffer Dall (12):
KVM: Introduce __KVM_HAVE_IRQ_LINE
ARM: KVM: Initial skeleton to compile KVM support
ARM: KVM: Hypervisor identity mapping
ARM: KVM: Hypervisor inititalization
ARM: KVM: Module unloading support
ARM: KVM: Memory virtualization setup
ARM: KVM: Inject IRQs and FIQs from userspace
ARM: KVM: World-switch implementation
ARM: KVM: Emulation framework and CP15 emulation
ARM: KVM: Handle guest faults in KVM
ARM: KVM: Handle I/O aborts
ARM: KVM: Guest wait-for-interrupts (WFI) support

Marc Zyngier (3):
ARM: add mem_type prot_pte accessor
KVM: use KVM_CAP_IRQ_ROUTING to protect the routing related code
KVM: Guard mmu_notifier specific code with CONFIG_MMU_NOTIFIER

 Documentation/virtual/kvm/api.txt           |   12 
 arch/arm/Kconfig                            |    2 
 arch/arm/Makefile                           |    1 
 arch/arm/include/asm/kvm.h                  |   87 +++
 arch/arm/include/asm/kvm_arm.h              |  144 +++++
 arch/arm/include/asm/kvm_asm.h              |   56 ++
 arch/arm/include/asm/kvm_emulate.h          |  108 +++
 arch/arm/include/asm/kvm_host.h             |  140 ++++
 arch/arm/include/asm/kvm_mmu.h              |   43 +
 arch/arm/include/asm/mach/map.h             |    1 
 arch/arm/include/asm/pgtable-3level-hwdef.h |    5 
 arch/arm/include/asm/pgtable-3level.h       |   12 
 arch/arm/include/asm/pgtable.h              |   10 
 arch/arm/include/asm/unified.h              |   12 
 arch/arm/kernel/armksyms.c                  |    7 
 arch/arm/kernel/asm-offsets.c               |   35 +
 arch/arm/kernel/entry-armv.S                |    1 
 arch/arm/kvm/Kconfig                        |   45 +
 arch/arm/kvm/Makefile                       |   17 +
 arch/arm/kvm/arm.c                          |  843 +++++++++++++++++++++++++++
 arch/arm/kvm/emulate.c                      |  747 ++++++++++++++++++++++++
 arch/arm/kvm/exports.c                      |   35 +
 arch/arm/kvm/guest.c                        |  147 +++++
 arch/arm/kvm/init.S                         |  144 +++++
 arch/arm/kvm/interrupts.S                   |  596 +++++++++++++++++++
 arch/arm/kvm/mmu.c                          |  608 +++++++++++++++++++
 arch/arm/kvm/trace.h                        |  117 ++++
 arch/arm/mm/Kconfig                         |   10 
 arch/arm/mm/idmap.c                         |   47 +-
 arch/arm/mm/mmu.c                           |    9 
 arch/ia64/include/asm/kvm.h                 |    1 
 arch/x86/include/asm/kvm.h                  |    1 
 include/linux/kvm.h                         |    1 
 include/linux/kvm_host.h                    |    6 
 include/trace/events/kvm.h                  |    4 
 mm/memory.c                                 |    2 
 virt/kvm/kvm_main.c                         |    2 
 37 files changed, 4045 insertions(+), 13 deletions(-)
 create mode 100644 arch/arm/include/asm/kvm.h
 create mode 100644 arch/arm/include/asm/kvm_arm.h
 create mode 100644 arch/arm/include/asm/kvm_asm.h
 create mode 100644 arch/arm/include/asm/kvm_emulate.h
 create mode 100644 arch/arm/include/asm/kvm_host.h
 create mode 100644 arch/arm/include/asm/kvm_mmu.h
 create mode 100644 arch/arm/kvm/Kconfig
 create mode 100644 arch/arm/kvm/Makefile
 create mode 100644 arch/arm/kvm/arm.c
 create mode 100644 arch/arm/kvm/emulate.c
 create mode 100644 arch/arm/kvm/exports.c
 create mode 100644 arch/arm/kvm/guest.c
 create mode 100644 arch/arm/kvm/init.S
 create mode 100644 arch/arm/kvm/interrupts.S
 create mode 100644 arch/arm/kvm/mmu.c
 create mode 100644 arch/arm/kvm/trace.h
#1 lore raw

PATCH v8 1/15 ARM: add mem_type prot_pte accessor

From: Marc Zyngier <[email protected]>

The KVM hypervisor mmu code requires requires access to the mem_type prot_pte field when setting up page tables pointing to a device. Unfortunately, the mem_type structure is opaque.

Add an accessor (get_mem_type_prot_pte()) to retrieve the prot_pte value.

---
 arch/arm/include/asm/mach/map.h |    1 +
 arch/arm/mm/mmu.c               |    6 ++++++
 2 files changed, 7 insertions(+)
arch/arm/include/asm/mach/map.h +1 -0
index a6efcdd..3787c9f 100644
@@ -37,6 +37,7 @@ extern void iotable_init(struct map_desc *, int);
 
 struct mem_type;
 extern const struct mem_type *get_mem_type(unsigned int type);
+extern pteval_t get_mem_type_prot_pte(unsigned int type);
 /*
  * external interface to remap single page with appropriate type
  */
arch/arm/mm/mmu.c +6 -0
index e5dad60..f7439e7 100644
@@ -301,6 +301,12 @@ const struct mem_type *get_mem_type(unsigned int type)
 }
 EXPORT_SYMBOL(get_mem_type);
 
+pteval_t get_mem_type_prot_pte(unsigned int type)
+{
+	return get_mem_type(type)->prot_pte;
+}
+EXPORT_SYMBOL(get_mem_type_prot_pte);
+
 /*
  * Adjust the PMD section entries according to the CPU in use.
  */
#2 lore raw

PATCH v8 2/15 KVM: use KVM_CAP_IRQ_ROUTING to protect the routing related code

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

The KVM code sometimes uses CONFIG_HAVE_KVM_IRQCHIP to protect code that is related to IRQ routing, which not all in-kernel irqchips may support.

Use KVM_CAP_IRQ_ROUTING instead.

---
 include/linux/kvm_host.h |    2 +-
 virt/kvm/kvm_main.c      |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
include/linux/kvm_host.h +1 -1
index 27ac8a4..c7f7787 100644
@@ -802,7 +802,7 @@ static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_se
 }
 #endif
 
-#ifdef CONFIG_HAVE_KVM_IRQCHIP
+#ifdef KVM_CAP_IRQ_ROUTING
 
 #define KVM_MAX_IRQ_ROUTES 1024
 
virt/kvm/kvm_main.c +1 -1
index 02cb440..636bd08 100644
@@ -2225,7 +2225,7 @@ static long kvm_dev_ioctl_check_extension_generic(long arg)
 	case KVM_CAP_SIGNAL_MSI:
 #endif
 		return 1;
-#ifdef CONFIG_HAVE_KVM_IRQCHIP
+#ifdef KVM_CAP_IRQ_ROUTING
 	case KVM_CAP_IRQ_ROUTING:
 		return KVM_MAX_IRQ_ROUTES;
 #endif
Applied
#3 lore raw

Re: [PATCH v8 02/15] KVM: use KVM_CAP_IRQ_ROUTING to protect the routing related code

Quoted 7 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

The KVM code sometimes uses CONFIG_HAVE_KVM_IRQCHIP to protect code that is related to IRQ routing, which not all in-kernel irqchips may support.

Use KVM_CAP_IRQ_ROUTING instead.

Thanks, applied.

Signature · 1 line
error compiling committee.c: too many arguments to function
#4 lore raw

PATCH v8 3/15 KVM: Introduce __KVM_HAVE_IRQ_LINE

In reply to Christoffer Dall

This is a preparatory patch for the KVM/ARM implementation. KVM/ARM will use the KVM_IRQ_LINE ioctl, which is currently conditional on __KVM_HAVE_IOAPIC, but ARM obviously doesn't have any IOAPIC support and we need a separate define.

---
 arch/ia64/include/asm/kvm.h |    1 +
 arch/x86/include/asm/kvm.h  |    1 +
 include/trace/events/kvm.h  |    4 +++-
 3 files changed, 5 insertions(+), 1 deletion(-)
arch/ia64/include/asm/kvm.h +1 -0
index b9f82c8..ec6c6b3 100644
@@ -26,6 +26,7 @@
 
 /* Select x86 specific features in <linux/kvm.h> */
 #define __KVM_HAVE_IOAPIC
+#define __KVM_HAVE_IRQ_LINE
 #define __KVM_HAVE_DEVICE_ASSIGNMENT
 
 /* Architectural interrupt line count. */
arch/x86/include/asm/kvm.h +1 -0
index e7d1c19..246617e 100644
@@ -12,6 +12,7 @@
 /* Select x86 specific features in <linux/kvm.h> */
 #define __KVM_HAVE_PIT
 #define __KVM_HAVE_IOAPIC
+#define __KVM_HAVE_IRQ_LINE
 #define __KVM_HAVE_DEVICE_ASSIGNMENT
 #define __KVM_HAVE_MSI
 #define __KVM_HAVE_USER_NMI
include/trace/events/kvm.h +3 -1
index 46e3cd8..ae9779d 100644
@@ -36,7 +36,7 @@ TRACE_EVENT(kvm_userspace_exit,
 		  __entry->errno < 0 ? -__entry->errno : __entry->reason)
 );
 
-#if defined(__KVM_HAVE_IOAPIC)
+#if defined(__KVM_HAVE_IRQ_LINE)
 TRACE_EVENT(kvm_set_irq,
 	TP_PROTO(unsigned int gsi, int level, int irq_source_id),
 	TP_ARGS(gsi, level, irq_source_id),
@@ -56,7 +56,9 @@ TRACE_EVENT(kvm_set_irq,
 	TP_printk("gsi %u level %d source %d",
 		  __entry->gsi, __entry->level, __entry->irq_source_id)
 );
+#endif
 
+#if defined(__KVM_HAVE_IOAPIC)
 #define kvm_deliver_mode		\
 	{0x0, "Fixed"},			\
 	{0x1, "LowPrio"},		\
Applied
#5 lore raw

Re: [PATCH v8 03/15] KVM: Introduce __KVM_HAVE_IRQ_LINE

Quoted 5 lines · Christoffer Dall

This is a preparatory patch for the KVM/ARM implementation. KVM/ARM will use the KVM_IRQ_LINE ioctl, which is currently conditional on __KVM_HAVE_IOAPIC, but ARM obviously doesn't have any IOAPIC support and we need a separate define.

Thanks, applied.

Signature · 1 line
error compiling committee.c: too many arguments to function
#6 lore raw

PATCH v8 4/15 KVM: Guard mmu_notifier specific code with CONFIG_MMU_NOTIFIER

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

In order to avoid compilation failure when KVM is not compiled in, guard the mmu_notifier specific sections with both CONFIG_MMU_NOTIFIER and KVM_ARCH_WANT_MMU_NOTIFIER, like it is being done in the rest of the KVM code.

---
 include/linux/kvm_host.h |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
include/linux/kvm_host.h +2 -2
index c7f7787..e3c86f8 100644
@@ -306,7 +306,7 @@ struct kvm {
 	struct hlist_head irq_ack_notifier_list;
 #endif
 
-#ifdef KVM_ARCH_WANT_MMU_NOTIFIER
+#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
 	struct mmu_notifier mmu_notifier;
 	unsigned long mmu_notifier_seq;
 	long mmu_notifier_count;
@@ -780,7 +780,7 @@ struct kvm_stats_debugfs_item {
 extern struct kvm_stats_debugfs_item debugfs_entries[];
 extern struct dentry *kvm_debugfs_dir;
 
-#ifdef KVM_ARCH_WANT_MMU_NOTIFIER
+#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
 static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_seq)
 {
 	if (unlikely(vcpu->kvm->mmu_notifier_count))
#7 lore raw

Re: [PATCH v8 04/15] KVM: Guard mmu_notifier specific code with CONFIG_MMU_NOTIFIER

Quoted 14 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

In order to avoid compilation failure when KVM is not compiled in, guard the mmu_notifier specific sections with both CONFIG_MMU_NOTIFIER and KVM_ARCH_WANT_MMU_NOTIFIER, like it is being done in the rest of the KVM code.

include/linux/kvm_host.h +1 -1
-#ifdef KVM_ARCH_WANT_MMU_NOTIFIER
+#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
 	struct mmu_notifier mmu_notifier;
 	unsigned long mmu_notifier_seq;
 	long mmu_notifier_count;

include/linux/kvm_host.h struct kvm_stats_debugfs_item { · line 785

Quoted 7 lines
include/linux/kvm_host.h +1 -1
@@ -780,7 +780,7 @@ struct kvm_stats_debugfs_item {
 extern struct kvm_stats_debugfs_item debugfs_entries[];
 extern struct dentry *kvm_debugfs_dir;
 
-#ifdef KVM_ARCH_WANT_MMU_NOTIFIER
+#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
 static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_seq)
 {

Why not have Kconfig select CONFIG_MMU_NOTIFIER?

Signature · 1 line
error compiling committee.c: too many arguments to function
#8 lore raw

Re: [PATCH v8 04/15] KVM: Guard mmu_notifier specific code with CONFIG_MMU_NOTIFIER

Quoted 18 lines · Avi Kivity
Quoted 17 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

In order to avoid compilation failure when KVM is not compiled in, guard the mmu_notifier specific sections with both CONFIG_MMU_NOTIFIER and KVM_ARCH_WANT_MMU_NOTIFIER, like it is being done in the rest of the KVM code.

(quoted diff) +1 -1
-#ifdef KVM_ARCH_WANT_MMU_NOTIFIER
+#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)

      struct mmu_notifier mmu_notifier;
      unsigned long mmu_notifier_seq;
      long mmu_notifier_count;

include/linux/kvm_host.h +0 -0
@@ -780,7 +780,7 @@ struct kvm_stats_debugfs_item {
  extern struct kvm_stats_debugfs_item debugfs_entries[];
  extern struct dentry *kvm_debugfs_dir;
 
Quoted 8 lines

include/linux/kvm_host.h struct kvm_stats_debugfs_item { · line 785

Quoted 4 lines
include/linux/kvm_host.h +1 -1
-#ifdef KVM_ARCH_WANT_MMU_NOTIFIER
+#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
  static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_seq)
  {

Why not have Kconfig select CONFIG_MMU_NOTIFIER?

Not sure I understand. Where would you select this option?

We do select this option when choosing to compile KVM on, but when we do _not_, then other includes of kvm_host.h fails.

-Christoffer

#9 lore raw

Re: [PATCH v8 04/15] KVM: Guard mmu_notifier specific code with CONFIG_MMU_NOTIFIER

Quoted 27 lines · Christoffer Dall
Quoted 18 lines · Avi Kivity
Quoted 17 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

In order to avoid compilation failure when KVM is not compiled in, guard the mmu_notifier specific sections with both CONFIG_MMU_NOTIFIER and KVM_ARCH_WANT_MMU_NOTIFIER, like it is being done in the rest of the KVM code.

include/linux/kvm_host.h +1 -1
-#ifdef KVM_ARCH_WANT_MMU_NOTIFIER
+#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
      struct mmu_notifier mmu_notifier;
      unsigned long mmu_notifier_seq;
      long mmu_notifier_count;
@@ -780,7 +780,7 @@ struct kvm_stats_debugfs_item {
 extern struct kvm_stats_debugfs_item debugfs_entries[];
 extern struct dentry *kvm_debugfs_dir;
 
Quoted 8 lines

include/linux/kvm_host.h struct kvm_stats_debugfs_item { · line 785

Quoted 4 lines
include/linux/kvm_host.h +1 -1
-#ifdef KVM_ARCH_WANT_MMU_NOTIFIER
+#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
 static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_seq)
 {

Why not have Kconfig select CONFIG_MMU_NOTIFIER?

Quoted 4 lines

Not sure I understand. Where would you select this option?

We do select this option when choosing to compile KVM on, but when we do _not_, then other includes of kvm_host.h fails.

Right, my mistake. Didn't notice it was a header.

Signature · 1 line
error compiling committee.c: too many arguments to function
#10 lore raw

Re: [PATCH v8 04/15] KVM: Guard mmu_notifier specific code with CONFIG_MMU_NOTIFIER

In reply to Christoffer Dall
Quoted 7 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

In order to avoid compilation failure when KVM is not compiled in, guard the mmu_notifier specific sections with both CONFIG_MMU_NOTIFIER and KVM_ARCH_WANT_MMU_NOTIFIER, like it is being done in the rest of the KVM code.

Quoted 5 lines
---
 include/linux/kvm_host.h |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

Applied, thanks.

#11 lore raw

PATCH v8 5/15 ARM: KVM: Initial skeleton to compile KVM support

In reply to Christoffer Dall

From: Christoffer Dall <[email protected]>

Targets KVM support for Cortex A-15 processors.

Contains no real functionality but all the framework components, make files, header files and some tracing functionality.

“Nothing to see here. Move along, move along..."

Most functionality is in arch/arm/kvm/* or arch/arm/include/asm/kvm_*.h.

---
 arch/arm/Kconfig                   |    2 
 arch/arm/Makefile                  |    1 
 arch/arm/include/asm/kvm.h         |   78 ++++++++++
 arch/arm/include/asm/kvm_asm.h     |   28 ++++
 arch/arm/include/asm/kvm_emulate.h |   97 +++++++++++++
 arch/arm/include/asm/kvm_host.h    |  116 +++++++++++++++
 arch/arm/include/asm/unified.h     |   12 ++
 arch/arm/kvm/Kconfig               |   44 ++++++
 arch/arm/kvm/Makefile              |   17 ++
 arch/arm/kvm/arm.c                 |  279 ++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/emulate.c             |  125 ++++++++++++++++
 arch/arm/kvm/exports.c             |   16 ++
 arch/arm/kvm/guest.c               |  148 +++++++++++++++++++
 arch/arm/kvm/init.S                |   17 ++
 arch/arm/kvm/interrupts.S          |   17 ++
 arch/arm/kvm/mmu.c                 |   15 ++
 arch/arm/kvm/trace.h               |   52 +++++++
 arch/arm/mm/Kconfig                |   10 +
 18 files changed, 1074 insertions(+)
 create mode 100644 arch/arm/include/asm/kvm.h
 create mode 100644 arch/arm/include/asm/kvm_asm.h
 create mode 100644 arch/arm/include/asm/kvm_emulate.h
 create mode 100644 arch/arm/include/asm/kvm_host.h
 create mode 100644 arch/arm/kvm/Kconfig
 create mode 100644 arch/arm/kvm/Makefile
 create mode 100644 arch/arm/kvm/arm.c
 create mode 100644 arch/arm/kvm/emulate.c
 create mode 100644 arch/arm/kvm/exports.c
 create mode 100644 arch/arm/kvm/guest.c
 create mode 100644 arch/arm/kvm/init.S
 create mode 100644 arch/arm/kvm/interrupts.S
 create mode 100644 arch/arm/kvm/mmu.c
 create mode 100644 arch/arm/kvm/trace.h
arch/arm/Kconfig +2 -0
index b649c59..736244c 100644
@@ -2273,3 +2273,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "arch/arm/kvm/Kconfig"
arch/arm/Makefile +1 -0
index 0298b00..64f1e16 100644
@@ -250,6 +250,7 @@ core-$(CONFIG_VFP)		+= arch/arm/vfp/
 # If we have a machine-specific directory, then include it in the build.
 core-y				+= arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
 core-y				+= arch/arm/net/
+core-y 				+= arch/arm/kvm/
 core-y				+= $(machdirs) $(platdirs)
 
 drivers-$(CONFIG_OPROFILE)      += arch/arm/oprofile/
arch/arm/include/asm/kvm.h new file +78 -0

78 lines not on this page · raw message

arch/arm/include/asm/kvm_asm.h new file +28 -0
new file mode 100644
index 0000000..c3d4458
@@ -0,0 +1,28 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef __ARM_KVM_ASM_H__
+#define __ARM_KVM_ASM_H__
+
+#define ARM_EXCEPTION_RESET	  0
+#define ARM_EXCEPTION_UNDEFINED   1
+#define ARM_EXCEPTION_SOFTWARE    2
+#define ARM_EXCEPTION_PREF_ABORT  3
+#define ARM_EXCEPTION_DATA_ABORT  4
+#define ARM_EXCEPTION_IRQ	  5
+#define ARM_EXCEPTION_FIQ	  6
+
+#endif /* __ARM_KVM_ASM_H__ */
arch/arm/include/asm/kvm_emulate.h new file +97 -0

97 lines not on this page · raw message

arch/arm/include/asm/kvm_host.h new file +116 -0

116 lines not on this page · raw message

arch/arm/include/asm/unified.h +12 -0
index f5989f4..e5dc5f6 100644
@@ -54,6 +54,18 @@
 
 #endif	/* CONFIG_THUMB2_KERNEL */
 
+#ifdef CONFIG_KVM_ARM_HOST
+#ifdef __ASSEMBLY__
+.arch_extension sec
+.arch_extension virt
+#else
+__asm__(
+"	.arch_extension sec\n"
+"	.arch_extension virt\n"
+);
+#endif
+#endif
+
 #ifndef CONFIG_ARM_ASM_UNIFIED
 
 /*
arch/arm/kvm/Kconfig new file +44 -0

44 lines not on this page · raw message

arch/arm/kvm/Makefile new file +17 -0
new file mode 100644
index 0000000..e69a8e1
@@ -0,0 +1,17 @@
+#
+# Makefile for Kernel-based Virtual Machine module
+#
+
+ccflags-y += -Ivirt/kvm -Iarch/arm/kvm
+CFLAGS_arm.o     := -I.
+CFLAGS_mmu.o := -I.
+
+AFLAGS_interrupts.o := -I$(obj)
+
+obj-$(CONFIG_KVM_ARM_HOST) += init.o interrupts.o exports.o
+
+kvm-arm-y += $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
+
+kvm-arm-y += arm.o guest.o mmu.o emulate.o
+
+obj-$(CONFIG_KVM) += kvm-arm.o
arch/arm/kvm/arm.c new file +279 -0

279 lines not on this page · raw message

arch/arm/kvm/emulate.c new file +125 -0

125 lines not on this page · raw message

arch/arm/kvm/exports.c new file +16 -0
new file mode 100644
index 0000000..d8a7fd5
@@ -0,0 +1,16 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/module.h>
arch/arm/kvm/guest.c new file +148 -0

148 lines not on this page · raw message

arch/arm/kvm/init.S new file +17 -0
new file mode 100644
index 0000000..073a494
@@ -0,0 +1,17 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+#include <asm/asm-offsets.h>
+#include <asm/kvm_asm.h>
arch/arm/kvm/interrupts.S new file +17 -0
new file mode 100644
index 0000000..073a494
@@ -0,0 +1,17 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+#include <asm/asm-offsets.h>
+#include <asm/kvm_asm.h>
arch/arm/kvm/mmu.c new file +15 -0
new file mode 100644
index 0000000..2cccd48
@@ -0,0 +1,15 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
arch/arm/kvm/trace.h new file +52 -0

52 lines not on this page · raw message

arch/arm/mm/Kconfig +10 -0
index 101b968..037dc53 100644
@@ -597,6 +597,16 @@ config ARM_LPAE
 
 	  If unsure, say N.
 
+config ARM_VIRT_EXT
+	bool "Support for ARM Virtualization Extensions"
+	depends on ARM_LPAE
+	help
+	  Say Y if you have an ARMv7 processor supporting the ARM hardware
+	  Virtualization extensions. KVM depends on this feature and will
+	  not run without it being selected. If you say Y here, the kernel
+	  will not boot on a machine without virtualization extensions and
+	  will not boot as a KVM guest.
+
 config ARCH_PHYS_ADDR_T_64BIT
 	def_bool ARM_LPAE
 
#12 lore raw

PATCH v8 6/15 ARM: KVM: Hypervisor identity mapping

In reply to Christoffer Dall

Adds support in the identity mapping feature that allows KVM to setup identity mapping for the Hyp mode with the AP[1] bit set as required by the specification and also supports freeing created sub pmd's after finished use.

These two functions:
- hyp_idmap_add(pgd, addr, end);
- hyp_idmap_del(pgd, addr, end);
are essentially calls to the same function as the non-hyp versions but with a different argument value. KVM calls these functions to setup and teardown the identity mapping used to initialize the hypervisor.

Note, the hyp-version of the _del function actually frees the pmd's pointed to by the pgd as opposed to the non-hyp version which just clears them.

---
 arch/arm/include/asm/pgtable-3level-hwdef.h |    1 +
 arch/arm/include/asm/pgtable.h              |    5 +++
 arch/arm/kvm/guest.c                        |    1 -
 arch/arm/mm/idmap.c                         |   47 +++++++++++++++++++++++++--
 4 files changed, 49 insertions(+), 5 deletions(-)
arch/arm/include/asm/pgtable-3level-hwdef.h +1 -0
index d795282..a2d404e 100644
@@ -44,6 +44,7 @@
 #define PMD_SECT_XN		(_AT(pmdval_t, 1) << 54)
 #define PMD_SECT_AP_WRITE	(_AT(pmdval_t, 0))
 #define PMD_SECT_AP_READ	(_AT(pmdval_t, 0))
+#define PMD_SECT_AP1		(_AT(pmdval_t, 1) << 6)
 #define PMD_SECT_TEX(x)		(_AT(pmdval_t, 0))
 
 /*
arch/arm/include/asm/pgtable.h +5 -0
index f66626d..c7bd809 100644
@@ -310,6 +310,11 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 
 #define pgtable_cache_init() do { } while (0)
 
+#ifdef CONFIG_KVM_ARM_HOST
+void hyp_idmap_add(pgd_t *, unsigned long, unsigned long);
+void hyp_idmap_del(pgd_t *pgd, unsigned long addr, unsigned long end);
+#endif
+
 #endif /* !__ASSEMBLY__ */
 
 #endif /* CONFIG_MMU */
arch/arm/kvm/guest.c +0 -1
index 9c75ec4..c0adab0 100644
@@ -24,7 +24,6 @@
 #include <asm/kvm_asm.h>
 #include <asm/kvm_emulate.h>
 
-
 struct kvm_stats_debugfs_item debugfs_entries[] = {
 	{ NULL }
 };
arch/arm/mm/idmap.c +43 -4

69 lines not on this page · raw message

#13 lore raw

Re: [PATCH v8 06/15] ARM: KVM: Hypervisor identity mapping

Quoted 12 lines · Christoffer Dall

Adds support in the identity mapping feature that allows KVM to setup identity mapping for the Hyp mode with the AP[1] bit set as required by the specification and also supports freeing created sub pmd's after finished use.

These two functions:
- hyp_idmap_add(pgd, addr, end);
- hyp_idmap_del(pgd, addr, end);
are essentially calls to the same function as the non-hyp versions but with a different argument value. KVM calls these functions to setup and teardown the identity mapping used to initialize the hypervisor.

Quoted 3 lines

Note, the hyp-version of the _del function actually frees the pmd's pointed to by the pgd as opposed to the non-hyp version which just clears them.

I asked previously what happens if two data structures share a page, and one of them is removed. Is that handled now? How?

Why not just identity map all memory? You can use large pages so it's fast and doesn't consume a lot of page table memory.-- error compiling committee.c: too many arguments to function

#14 lore raw

Re: [PATCH v8 06/15] ARM: KVM: Hypervisor identity mapping

Quoted 13 lines · Avi Kivity
Quoted 12 lines · Christoffer Dall

Adds support in the identity mapping feature that allows KVM to setup identity mapping for the Hyp mode with the AP[1] bit set as required by the specification and also supports freeing created sub pmd's after finished use.

These two functions:
 - hyp_idmap_add(pgd, addr, end);
 - hyp_idmap_del(pgd, addr, end);
are essentially calls to the same function as the non-hyp versions but with a different argument value. KVM calls these functions to setup and teardown the identity mapping used to initialize the hypervisor.

Quoted 8 lines
Quoted 3 lines

Note, the hyp-version of the _del function actually frees the pmd's pointed to by the pgd as opposed to the non-hyp version which just clears them.

I asked previously what happens if two data structures share a page, and one of them is removed.  Is that handled now?  How?

I think you asked previously for the general hyp-mode mappings, not the identity mappings. For the general hyp-mode mappings we simply don't unmap the data structures, potentially leaking a few pages for the page tables themselves.

This is only for initialization, so there are not really any data structures mapped, only one/two pages to initialize the hypervisor mode.

Quoted 2 lines

Why not just identity map all memory?  You can use large pages so it's fast and doesn't consume a lot of page table memory.

That's an option, but it still seems like an awful waste since it's only used once (unless you unload and re-load the module) and there's really no problem with data structures here.

The truth is that this is going to go away, and the code will be put in a section that's idmapped from kernel start. There's a patch under way from Marc taking care of this which I assmue we'll merge for v9.

-Christoffer

#15 lore raw

Re: [PATCH v8 06/15] ARM: KVM: Hypervisor identity mapping

Quoted 35 lines · Christoffer Dall
Quoted 13 lines · Avi Kivity
Quoted 12 lines · Christoffer Dall

Adds support in the identity mapping feature that allows KVM to setup identity mapping for the Hyp mode with the AP[1] bit set as required by the specification and also supports freeing created sub pmd's after finished use.

These two functions:
- hyp_idmap_add(pgd, addr, end);
- hyp_idmap_del(pgd, addr, end);
are essentially calls to the same function as the non-hyp versions but with a different argument value. KVM calls these functions to setup and teardown the identity mapping used to initialize the hypervisor.

Quoted 8 lines
Quoted 3 lines

Note, the hyp-version of the _del function actually frees the pmd's pointed to by the pgd as opposed to the non-hyp version which just clears them.

I asked previously what happens if two data structures share a page, and one of them is removed. Is that handled now? How?

I think you asked previously for the general hyp-mode mappings, not the identity mappings. For the general hyp-mode mappings we simply don't unmap the data structures, potentially leaking a few pages for the page tables themselves.

This is only for initialization, so there are not really any data structures mapped, only one/two pages to initialize the hypervisor mode.

Quoted 2 lines

Why not just identity map all memory? You can use large pages so it's fast and doesn't consume a lot of page table memory.

Quoted 7 lines

That's an option, but it still seems like an awful waste since it's only used once (unless you unload and re-load the module) and there's really no problem with data structures here.

The truth is that this is going to go away, and the code will be put in a section that's idmapped from kernel start. There's a patch under way from Marc taking care of this which I assmue we'll merge for v9.

Okay, thanks.

Signature · 1 line
error compiling committee.c: too many arguments to function
#16 lore raw

PATCH v8 7/15 ARM: KVM: Hypervisor inititalization

In reply to Christoffer Dall

Sets up the required registers to run code in HYP-mode from the kernel.

By setting the HVBAR the kernel can execute code in Hyp-mode with the MMU disabled. The HVBAR initially points to initialization code, which initializes other Hyp-mode registers and enables the MMU for Hyp-mode. Afterwards, the HVBAR is changed to point to KVM Hyp vectors used to catch guest faults and to switch to Hyp mode to perform a world-switch into a KVM guest.

Also provides memory mapping code to map required code pages and data structures accessed in Hyp mode at the same virtual address as the host kernel virtual addresses, but which conforms to the architectural requirements for translations in Hyp mode. This interface is added in arch/arm/kvm/arm_mmu.c and is comprised of:
- create_hyp_mappings(hyp_pgd, start, end);
- free_hyp_pmds(pgd_hyp);

Note: The initialization mechanism currently relies on an SMC #0 call to the secure monitor, which was merely a fast way of getting to the hypervisor. Dave Marting and Rusty Russel have patches out to make the boot-wrapper and the kernel boot in Hyp-mode and setup a generic way for hypervisors to get access to Hyp-mode if the boot-loader allows such access.

---
 arch/arm/include/asm/kvm_arm.h              |  117 +++++++++++++++++++
 arch/arm/include/asm/kvm_asm.h              |   22 +++
 arch/arm/include/asm/kvm_mmu.h              |   37 ++++++
 arch/arm/include/asm/pgtable-3level-hwdef.h |    4 +
 arch/arm/include/asm/pgtable-3level.h       |    4 +
 arch/arm/include/asm/pgtable.h              |    1 
 arch/arm/kvm/arm.c                          |  167 +++++++++++++++++++++++++++
 arch/arm/kvm/exports.c                      |   15 ++
 arch/arm/kvm/init.S                         |   99 ++++++++++++++++
 arch/arm/kvm/interrupts.S                   |   47 +++++++
 arch/arm/kvm/mmu.c                          |  170 +++++++++++++++++++++++++++
 mm/memory.c                                 |    2 
 12 files changed, 685 insertions(+)
 create mode 100644 arch/arm/include/asm/kvm_arm.h
 create mode 100644 arch/arm/include/asm/kvm_mmu.h
arch/arm/include/asm/kvm_arm.h new file +117 -0
new file mode 100644
index 0000000..7f30cbd
@@ -0,0 +1,117 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef __KVM_ARM_H__
+#define __KVM_ARM_H__
+
+#include <asm/types.h>
+
+/* Hyp Configuration Register (HCR) bits */
+#define HCR_TGE		(1 << 27)
+#define HCR_TVM		(1 << 26)
+#define HCR_TTLB	(1 << 25)
+#define HCR_TPU		(1 << 24)
+#define HCR_TPC		(1 << 23)
+#define HCR_TSW		(1 << 22)
+#define HCR_TAC		(1 << 21)
+#define HCR_TIDCP	(1 << 20)
+#define HCR_TSC		(1 << 19)
+#define HCR_TID3	(1 << 18)
+#define HCR_TID2	(1 << 17)
+#define HCR_TID1	(1 << 16)
+#define HCR_TID0	(1 << 15)
+#define HCR_TWE		(1 << 14)
+#define HCR_TWI		(1 << 13)
+#define HCR_DC		(1 << 12)
+#define HCR_BSU		(3 << 10)
+#define HCR_BSU_IS	(1 << 10)
+#define HCR_FB		(1 << 9)
+#define HCR_VA		(1 << 8)
+#define HCR_VI		(1 << 7)
+#define HCR_VF		(1 << 6)
+#define HCR_AMO		(1 << 5)
+#define HCR_IMO		(1 << 4)
+#define HCR_FMO		(1 << 3)
+#define HCR_PTW		(1 << 2)
+#define HCR_SWIO	(1 << 1)
+#define HCR_VM		1
+
+/*
+ * The bits we set in HCR:
+ * TAC:		Trap ACTLR
+ * TSC:		Trap SMC
+ * TWI:		Trap WFI
+ * BSU_IS:	Upgrade barriers to the inner shareable domain
+ * FB:		Force broadcast of all maintainance operations
+ * AMO:		Override CPSR.A and enable signaling with VA
+ * IMO:		Override CPSR.I and enable signaling with VI
+ * FMO:		Override CPSR.F and enable signaling with VF
+ * SWIO:	Turn set/way invalidates into set/way clean+invalidate
+ */
+#define HCR_GUEST_MASK (HCR_TSC | HCR_TWI | HCR_VM | HCR_BSU_IS | HCR_FB | \
+			HCR_AMO | HCR_IMO | HCR_FMO | HCR_FMO | HCR_SWIO)
+
+/* Hyp System Control Register (HSCTLR) bits */
+#define HSCTLR_TE	(1 << 30)
+#define HSCTLR_EE	(1 << 25)
+#define HSCTLR_FI	(1 << 21)
+#define HSCTLR_WXN	(1 << 19)
+#define HSCTLR_I	(1 << 12)
+#define HSCTLR_C	(1 << 2)
+#define HSCTLR_A	(1 << 1)
+#define HSCTLR_M	1
+#define HSCTLR_MASK	(HSCTLR_M | HSCTLR_A | HSCTLR_C | HSCTLR_I | \
+			 HSCTLR_WXN | HSCTLR_FI | HSCTLR_EE | HSCTLR_TE)
+
+/* TTBCR and HTCR Registers bits */
+#define TTBCR_EAE	(1 << 31)
+#define TTBCR_IMP	(1 << 30)
+#define TTBCR_SH1	(3 << 28)
+#define TTBCR_ORGN1	(3 << 26)
+#define TTBCR_IRGN1	(3 << 24)
+#define TTBCR_EPD1	(1 << 23)
+#define TTBCR_A1	(1 << 22)
+#define TTBCR_T1SZ	(3 << 16)
+#define TTBCR_SH0	(3 << 12)
+#define TTBCR_ORGN0	(3 << 10)
+#define TTBCR_IRGN0	(3 << 8)
+#define TTBCR_EPD0	(1 << 7)
+#define TTBCR_T0SZ	3
+#define HTCR_MASK	(TTBCR_T0SZ | TTBCR_IRGN0 | TTBCR_ORGN0 | TTBCR_SH0)
+
+
+/* Virtualization Translation Control Register (VTCR) bits */
+#define VTCR_SH0	(3 << 12)
+#define VTCR_ORGN0	(3 << 10)
+#define VTCR_IRGN0	(3 << 8)
+#define VTCR_SL0	(3 << 6)
+#define VTCR_S		(1 << 4)
+#define VTCR_T0SZ	3
+#define VTCR_MASK	(VTCR_SH0 | VTCR_ORGN0 | VTCR_IRGN0 | VTCR_SL0 | \
+			 VTCR_S | VTCR_T0SZ | VTCR_MASK)
+#define VTCR_HTCR_SH	(VTCR_SH0 | VTCR_ORGN0 | VTCR_IRGN0)
+#define VTCR_SL_L2	0		/* Starting-level: 2 */
+#define VTCR_SL_L1	(1 << 6)	/* Starting-level: 1 */
+#define VTCR_GUEST_SL	VTCR_SL_L1
+#define VTCR_GUEST_T0SZ	0
+#if VTCR_GUEST_SL == 0
+#define VTTBR_X		(14 - VTCR_GUEST_T0SZ)
+#else
+#define VTTBR_X		(5 - VTCR_GUEST_T0SZ)
+#endif
+
+
+#endif /* __KVM_ARM_H__ */
arch/arm/include/asm/kvm_asm.h +22 -0
index c3d4458..69afdf3 100644
@@ -24,5 +24,27 @@
 #define ARM_EXCEPTION_DATA_ABORT  4
 #define ARM_EXCEPTION_IRQ	  5
 #define ARM_EXCEPTION_FIQ	  6
+#define ARM_EXCEPTION_HVC	  7
+
+/*
+ * SMC Hypervisor API call number
+ */
+#define SMCHYP_HVBAR_W 0xfffffff0
+
+#ifndef __ASSEMBLY__
+struct kvm_vcpu;
+
+extern char __kvm_hyp_init[];
+extern char __kvm_hyp_init_end[];
+
+extern char __kvm_hyp_vector[];
+
+extern char __kvm_hyp_code_start[];
+extern char __kvm_hyp_code_end[];
+
+extern void __kvm_flush_vm_context(void);
+
+extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
+#endif
 
 #endif /* __ARM_KVM_ASM_H__ */
arch/arm/include/asm/kvm_mmu.h new file +37 -0
new file mode 100644
index 0000000..1aa1af4
@@ -0,0 +1,37 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef __ARM_KVM_MMU_H__
+#define __ARM_KVM_MMU_H__
+
+/*
+ * The architecture supports 40-bit IPA as input to the 2nd stage translations
+ * and PTRS_PER_PGD2 could therefore be 1024.
+ *
+ * To save a bit of memory and to avoid alignment issues we assume 39-bit IPA
+ * for now, but remember that the level-1 table must be aligned to its size.
+ */
+#define PTRS_PER_PGD2	512
+#define PGD2_ORDER	get_order(PTRS_PER_PGD2 * sizeof(pgd_t))
+
+int create_hyp_mappings(void *from, void *to);
+void free_hyp_pmds(void);
+
+int kvm_hyp_pgd_alloc(void);
+pgd_t *kvm_hyp_pgd_get(void);
+void kvm_hyp_pgd_free(void);
+
+#endif /* __ARM_KVM_MMU_H__ */
arch/arm/include/asm/pgtable-3level-hwdef.h +4 -0
index a2d404e..18f5cef 100644
@@ -32,6 +32,9 @@
 #define PMD_TYPE_SECT		(_AT(pmdval_t, 1) << 0)
 #define PMD_BIT4		(_AT(pmdval_t, 0))
 #define PMD_DOMAIN(x)		(_AT(pmdval_t, 0))
+#define PMD_APTABLE_SHIFT	(61)
+#define PMD_APTABLE		(_AT(pgdval_t, 3) << PGD_APTABLE_SHIFT)
+#define PMD_PXNTABLE		(_AT(pgdval_t, 1) << 59)
 
 /*
  *   - section
@@ -41,6 +44,7 @@
 #define PMD_SECT_S		(_AT(pmdval_t, 3) << 8)
 #define PMD_SECT_AF		(_AT(pmdval_t, 1) << 10)
 #define PMD_SECT_nG		(_AT(pmdval_t, 1) << 11)
+#define PMD_SECT_PXN		(_AT(pmdval_t, 1) << 53)
 #define PMD_SECT_XN		(_AT(pmdval_t, 1) << 54)
 #define PMD_SECT_AP_WRITE	(_AT(pmdval_t, 0))
 #define PMD_SECT_AP_READ	(_AT(pmdval_t, 0))
arch/arm/include/asm/pgtable-3level.h +4 -0
index b249035..1169a8a 100644
@@ -107,6 +107,10 @@
 #define pud_none(pud)		(!pud_val(pud))
 #define pud_bad(pud)		(!(pud_val(pud) & 2))
 #define pud_present(pud)	(pud_val(pud))
+#define pmd_table(pmd)		((pmd_val(pmd) & PMD_TYPE_MASK) == \
+						 PMD_TYPE_TABLE)
+#define pmd_sect(pmd)		((pmd_val(pmd) & PMD_TYPE_MASK) == \
+						 PMD_TYPE_SECT)
 
 #define pud_clear(pudp)			\
 	do {				\
arch/arm/include/asm/pgtable.h +1 -0
index c7bd809..4b72287 100644
@@ -82,6 +82,7 @@ extern pgprot_t		pgprot_kernel;
 #define PAGE_READONLY_EXEC	_MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_RDONLY)
 #define PAGE_KERNEL		_MOD_PROT(pgprot_kernel, L_PTE_XN)
 #define PAGE_KERNEL_EXEC	pgprot_kernel
+#define PAGE_HYP		_MOD_PROT(pgprot_kernel, L_PTE_USER)
 
 #define __PAGE_NONE		__pgprot(_L_PTE_DEFAULT | L_PTE_RDONLY | L_PTE_XN)
 #define __PAGE_SHARED		__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN)
arch/arm/kvm/arm.c +167 -0

186 lines not on this page · raw message

arch/arm/kvm/exports.c +15 -0
index d8a7fd5..2631609 100644
@@ -14,3 +14,18 @@
  */
 
 #include <linux/module.h>
+#include <asm/kvm_asm.h>
+
+EXPORT_SYMBOL_GPL(__kvm_hyp_init);
+EXPORT_SYMBOL_GPL(__kvm_hyp_init_end);
+
+EXPORT_SYMBOL_GPL(__kvm_hyp_vector);
+
+EXPORT_SYMBOL_GPL(__kvm_hyp_code_start);
+EXPORT_SYMBOL_GPL(__kvm_hyp_code_end);
+
+EXPORT_SYMBOL_GPL(__kvm_vcpu_run);
+
+EXPORT_SYMBOL_GPL(__kvm_flush_vm_context);
+
+EXPORT_SYMBOL_GPL(smp_send_reschedule);
arch/arm/kvm/init.S +99 -0

104 lines not on this page · raw message

arch/arm/kvm/interrupts.S +47 -0

52 lines not on this page · raw message

arch/arm/kvm/mmu.c +170 -0

173 lines not on this page · raw message

mm/memory.c +2 -0
index 1b7dc66..b09d781 100644
@@ -379,12 +379,14 @@ void pgd_clear_bad(pgd_t *pgd)
 	pgd_ERROR(*pgd);
 	pgd_clear(pgd);
 }
+EXPORT_SYMBOL_GPL(pgd_clear_bad);
 
 void pud_clear_bad(pud_t *pud)
 {
 	pud_ERROR(*pud);
 	pud_clear(pud);
 }
+EXPORT_SYMBOL_GPL(pud_clear_bad);
 
 void pmd_clear_bad(pmd_t *pmd)
 {
#17 lore raw

Re: [PATCH v8 07/15] ARM: KVM: Hypervisor inititalization

Quoted 441 lines · Christoffer Dall

Sets up the required registers to run code in HYP-mode from the kernel.

By setting the HVBAR the kernel can execute code in Hyp-mode with the MMU disabled. The HVBAR initially points to initialization code, which initializes other Hyp-mode registers and enables the MMU for Hyp-mode. Afterwards, the HVBAR is changed to point to KVM Hyp vectors used to catch guest faults and to switch to Hyp mode to perform a world-switch into a KVM guest.

Also provides memory mapping code to map required code pages and data structures accessed in Hyp mode at the same virtual address as the host kernel virtual addresses, but which conforms to the architectural requirements for translations in Hyp mode. This interface is added in arch/arm/kvm/arm_mmu.c and is comprised of:
- create_hyp_mappings(hyp_pgd, start, end);
- free_hyp_pmds(pgd_hyp);

Note: The initialization mechanism currently relies on an SMC #0 call to the secure monitor, which was merely a fast way of getting to the hypervisor. Dave Marting and Rusty Russel have patches out to make the boot-wrapper and the kernel boot in Hyp-mode and setup a generic way for hypervisors to get access to Hyp-mode if the boot-loader allows such access.

---
 arch/arm/include/asm/kvm_arm.h              |  117 +++++++++++++++++++
 arch/arm/include/asm/kvm_asm.h              |   22 +++
 arch/arm/include/asm/kvm_mmu.h              |   37 ++++++
 arch/arm/include/asm/pgtable-3level-hwdef.h |    4 +
 arch/arm/include/asm/pgtable-3level.h       |    4 +
 arch/arm/include/asm/pgtable.h              |    1 
 arch/arm/kvm/arm.c                          |  167 +++++++++++++++++++++++++++
 arch/arm/kvm/exports.c                      |   15 ++
 arch/arm/kvm/init.S                         |   99 ++++++++++++++++
 arch/arm/kvm/interrupts.S                   |   47 +++++++
 arch/arm/kvm/mmu.c                          |  170 +++++++++++++++++++++++++++
 mm/memory.c                                 |    2 
 12 files changed, 685 insertions(+)
 create mode 100644 arch/arm/include/asm/kvm_arm.h
 create mode 100644 arch/arm/include/asm/kvm_mmu.h
arch/arm/include/asm/kvm_arm.h new file +117 -0
new file mode 100644
index 0000000..7f30cbd
@@ -0,0 +1,117 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef __KVM_ARM_H__
+#define __KVM_ARM_H__
+
+#include <asm/types.h>
+
+/* Hyp Configuration Register (HCR) bits */
+#define HCR_TGE		(1 << 27)
+#define HCR_TVM		(1 << 26)
+#define HCR_TTLB	(1 << 25)
+#define HCR_TPU		(1 << 24)
+#define HCR_TPC		(1 << 23)
+#define HCR_TSW		(1 << 22)
+#define HCR_TAC		(1 << 21)
+#define HCR_TIDCP	(1 << 20)
+#define HCR_TSC		(1 << 19)
+#define HCR_TID3	(1 << 18)
+#define HCR_TID2	(1 << 17)
+#define HCR_TID1	(1 << 16)
+#define HCR_TID0	(1 << 15)
+#define HCR_TWE		(1 << 14)
+#define HCR_TWI		(1 << 13)
+#define HCR_DC		(1 << 12)
+#define HCR_BSU		(3 << 10)
+#define HCR_BSU_IS	(1 << 10)
+#define HCR_FB		(1 << 9)
+#define HCR_VA		(1 << 8)
+#define HCR_VI		(1 << 7)
+#define HCR_VF		(1 << 6)
+#define HCR_AMO		(1 << 5)
+#define HCR_IMO		(1 << 4)
+#define HCR_FMO		(1 << 3)
+#define HCR_PTW		(1 << 2)
+#define HCR_SWIO	(1 << 1)
+#define HCR_VM		1
+
+/*
+ * The bits we set in HCR:
+ * TAC:		Trap ACTLR
+ * TSC:		Trap SMC
+ * TWI:		Trap WFI
+ * BSU_IS:	Upgrade barriers to the inner shareable domain
+ * FB:		Force broadcast of all maintainance operations
+ * AMO:		Override CPSR.A and enable signaling with VA
+ * IMO:		Override CPSR.I and enable signaling with VI
+ * FMO:		Override CPSR.F and enable signaling with VF
+ * SWIO:	Turn set/way invalidates into set/way clean+invalidate
+ */
+#define HCR_GUEST_MASK (HCR_TSC | HCR_TWI | HCR_VM | HCR_BSU_IS | HCR_FB | \
+			HCR_AMO | HCR_IMO | HCR_FMO | HCR_FMO | HCR_SWIO)
+
+/* Hyp System Control Register (HSCTLR) bits */
+#define HSCTLR_TE	(1 << 30)
+#define HSCTLR_EE	(1 << 25)
+#define HSCTLR_FI	(1 << 21)
+#define HSCTLR_WXN	(1 << 19)
+#define HSCTLR_I	(1 << 12)
+#define HSCTLR_C	(1 << 2)
+#define HSCTLR_A	(1 << 1)
+#define HSCTLR_M	1
+#define HSCTLR_MASK	(HSCTLR_M | HSCTLR_A | HSCTLR_C | HSCTLR_I | \
+			 HSCTLR_WXN | HSCTLR_FI | HSCTLR_EE | HSCTLR_TE)
+
+/* TTBCR and HTCR Registers bits */
+#define TTBCR_EAE	(1 << 31)
+#define TTBCR_IMP	(1 << 30)
+#define TTBCR_SH1	(3 << 28)
+#define TTBCR_ORGN1	(3 << 26)
+#define TTBCR_IRGN1	(3 << 24)
+#define TTBCR_EPD1	(1 << 23)
+#define TTBCR_A1	(1 << 22)
+#define TTBCR_T1SZ	(3 << 16)
+#define TTBCR_SH0	(3 << 12)
+#define TTBCR_ORGN0	(3 << 10)
+#define TTBCR_IRGN0	(3 << 8)
+#define TTBCR_EPD0	(1 << 7)
+#define TTBCR_T0SZ	3
+#define HTCR_MASK	(TTBCR_T0SZ | TTBCR_IRGN0 | TTBCR_ORGN0 | TTBCR_SH0)
+
+
+/* Virtualization Translation Control Register (VTCR) bits */
+#define VTCR_SH0	(3 << 12)
+#define VTCR_ORGN0	(3 << 10)
+#define VTCR_IRGN0	(3 << 8)
+#define VTCR_SL0	(3 << 6)
+#define VTCR_S		(1 << 4)
+#define VTCR_T0SZ	3
+#define VTCR_MASK	(VTCR_SH0 | VTCR_ORGN0 | VTCR_IRGN0 | VTCR_SL0 | \
+			 VTCR_S | VTCR_T0SZ | VTCR_MASK)
+#define VTCR_HTCR_SH	(VTCR_SH0 | VTCR_ORGN0 | VTCR_IRGN0)
+#define VTCR_SL_L2	0		/* Starting-level: 2 */
+#define VTCR_SL_L1	(1 << 6)	/* Starting-level: 1 */
+#define VTCR_GUEST_SL	VTCR_SL_L1
+#define VTCR_GUEST_T0SZ	0
+#if VTCR_GUEST_SL == 0
+#define VTTBR_X		(14 - VTCR_GUEST_T0SZ)
+#else
+#define VTTBR_X		(5 - VTCR_GUEST_T0SZ)
+#endif
+
+
+#endif /* __KVM_ARM_H__ */
arch/arm/include/asm/kvm_asm.h +22 -0
index c3d4458..69afdf3 100644
@@ -24,5 +24,27 @@
 #define ARM_EXCEPTION_DATA_ABORT  4
 #define ARM_EXCEPTION_IRQ	  5
 #define ARM_EXCEPTION_FIQ	  6
+#define ARM_EXCEPTION_HVC	  7
+
+/*
+ * SMC Hypervisor API call number
+ */
+#define SMCHYP_HVBAR_W 0xfffffff0
+
+#ifndef __ASSEMBLY__
+struct kvm_vcpu;
+
+extern char __kvm_hyp_init[];
+extern char __kvm_hyp_init_end[];
+
+extern char __kvm_hyp_vector[];
+
+extern char __kvm_hyp_code_start[];
+extern char __kvm_hyp_code_end[];
+
+extern void __kvm_flush_vm_context(void);
+
+extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
+#endif
 
 #endif /* __ARM_KVM_ASM_H__ */
arch/arm/include/asm/kvm_mmu.h new file +37 -0
new file mode 100644
index 0000000..1aa1af4
@@ -0,0 +1,37 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef __ARM_KVM_MMU_H__
+#define __ARM_KVM_MMU_H__
+
+/*
+ * The architecture supports 40-bit IPA as input to the 2nd stage translations
+ * and PTRS_PER_PGD2 could therefore be 1024.
+ *
+ * To save a bit of memory and to avoid alignment issues we assume 39-bit IPA
+ * for now, but remember that the level-1 table must be aligned to its size.
+ */
+#define PTRS_PER_PGD2	512
+#define PGD2_ORDER	get_order(PTRS_PER_PGD2 * sizeof(pgd_t))
+
+int create_hyp_mappings(void *from, void *to);
+void free_hyp_pmds(void);
+
+int kvm_hyp_pgd_alloc(void);
+pgd_t *kvm_hyp_pgd_get(void);
+void kvm_hyp_pgd_free(void);
+
+#endif /* __ARM_KVM_MMU_H__ */
arch/arm/include/asm/pgtable-3level-hwdef.h +4 -0
index a2d404e..18f5cef 100644
@@ -32,6 +32,9 @@
 #define PMD_TYPE_SECT		(_AT(pmdval_t, 1) << 0)
 #define PMD_BIT4		(_AT(pmdval_t, 0))
 #define PMD_DOMAIN(x)		(_AT(pmdval_t, 0))
+#define PMD_APTABLE_SHIFT	(61)
+#define PMD_APTABLE		(_AT(pgdval_t, 3) << PGD_APTABLE_SHIFT)
+#define PMD_PXNTABLE		(_AT(pgdval_t, 1) << 59)
 
 /*
  *   - section
@@ -41,6 +44,7 @@
 #define PMD_SECT_S		(_AT(pmdval_t, 3) << 8)
 #define PMD_SECT_AF		(_AT(pmdval_t, 1) << 10)
 #define PMD_SECT_nG		(_AT(pmdval_t, 1) << 11)
+#define PMD_SECT_PXN		(_AT(pmdval_t, 1) << 53)
 #define PMD_SECT_XN		(_AT(pmdval_t, 1) << 54)
 #define PMD_SECT_AP_WRITE	(_AT(pmdval_t, 0))
 #define PMD_SECT_AP_READ	(_AT(pmdval_t, 0))
arch/arm/include/asm/pgtable-3level.h +4 -0
index b249035..1169a8a 100644
@@ -107,6 +107,10 @@
 #define pud_none(pud)		(!pud_val(pud))
 #define pud_bad(pud)		(!(pud_val(pud) & 2))
 #define pud_present(pud)	(pud_val(pud))
+#define pmd_table(pmd)		((pmd_val(pmd) & PMD_TYPE_MASK) == \
+						 PMD_TYPE_TABLE)
+#define pmd_sect(pmd)		((pmd_val(pmd) & PMD_TYPE_MASK) == \
+						 PMD_TYPE_SECT)
 
 #define pud_clear(pudp)			\
 	do {				\
arch/arm/include/asm/pgtable.h +1 -0
index c7bd809..4b72287 100644
@@ -82,6 +82,7 @@ extern pgprot_t		pgprot_kernel;
 #define PAGE_READONLY_EXEC	_MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_RDONLY)
 #define PAGE_KERNEL		_MOD_PROT(pgprot_kernel, L_PTE_XN)
 #define PAGE_KERNEL_EXEC	pgprot_kernel
+#define PAGE_HYP		_MOD_PROT(pgprot_kernel, L_PTE_USER)
 
 #define __PAGE_NONE		__pgprot(_L_PTE_DEFAULT | L_PTE_RDONLY | L_PTE_XN)
 #define __PAGE_SHARED		__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN)
arch/arm/kvm/arm.c +137 -0
index 5992d90..4c61d3c 100644
@@ -31,6 +31,12 @@
 #include <asm/uaccess.h>
 #include <asm/ptrace.h>
 #include <asm/mman.h>
+#include <asm/tlbflush.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
+#include <asm/kvm_mmu.h>
+
+static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
 
 int kvm_arch_hardware_enable(void *garbage)
 {
@@ -255,13 +261,174 @@ long kvm_arch_vm_ioctl(struct file *filp,
 	return -EINVAL;
 }
 
+static void cpu_set_vector(void *vector)
+{
+	unsigned long vector_ptr;
+	unsigned long smc_hyp_nr;
+
+	vector_ptr = (unsigned long)vector;
+	smc_hyp_nr = SMCHYP_HVBAR_W;
+
+	/*
+	 * Set the HVBAR
+	 */
+	asm volatile (
+		"mov	r0, %[vector_ptr]\n\t"
+		"mov	r7, %[smc_hyp_nr]\n\t"
+		"smc	#0\n\t" : :
+		[vector_ptr] "r" (vector_ptr),
+		[smc_hyp_nr] "r" (smc_hyp_nr) :
+		"r0", "r7");
+}
+
+static void cpu_init_hyp_mode(void *vector)
+{
+	unsigned long pgd_ptr;
+	unsigned long hyp_stack_ptr;
+	unsigned long stack_page;
+
+	cpu_set_vector(vector);
+
+	pgd_ptr = virt_to_phys(kvm_hyp_pgd_get());
+	stack_page = __get_cpu_var(kvm_arm_hyp_stack_page);
+	hyp_stack_ptr = stack_page + PAGE_SIZE;
+
+	/*
+	 * Call initialization code
+	 */
+	asm volatile (
+		"mov	r0, %[pgd_ptr]\n\t"
+		"mov	r1, %[hyp_stack_ptr]\n\t"
+		"hvc	#0\n\t" : :
+		[pgd_ptr] "r" (pgd_ptr),
+		[hyp_stack_ptr] "r" (hyp_stack_ptr) :
+		"r0", "r1");
+}
+
+/**
+ * Inits Hyp-mode on all online CPUs
+ */
+static int init_hyp_mode(void)
+{
+	phys_addr_t init_phys_addr, init_end_phys_addr;
+	int cpu;
+	int err = 0;
+
+	/*
+	 * Allocate stack pages for Hypervisor-mode
+	 */
+	for_each_possible_cpu(cpu) {
+		unsigned long stack_page;
+
+		stack_page = __get_free_page(GFP_KERNEL);
+		if (!stack_page) {
+			err = -ENOMEM;
+			goto out_free_stack_pages;
+		}
+
+		per_cpu(kvm_arm_hyp_stack_page, cpu) = stack_page;
+	}
+
+	/*
+	 * Allocate Hyp level-1 page table
+	 */
+	err = kvm_hyp_pgd_alloc();
+	if (err)
+		goto out_free_stack_pages;
+
+	init_phys_addr = virt_to_phys(__kvm_hyp_init);
+	init_end_phys_addr = virt_to_phys(__kvm_hyp_init_end);
+	BUG_ON(init_phys_addr & 0x1f);
+
+	/*
+	 * Create identity mapping for the init code.
+	 */
+	hyp_idmap_add(kvm_hyp_pgd_get(),
+		      (unsigned long)init_phys_addr,
+		      (unsigned long)init_end_phys_addr);
+
+	/*
+	 * Execute the init code on each CPU.
+	 *
+	 * Note: The stack is not mapped yet, so don't do anything else than
+	 * initializing the hypervisor mode on each CPU using a local stack
+	 * space for temporary storage.
+	 */
+	for_each_online_cpu(cpu) {
+		smp_call_function_single(cpu, cpu_init_hyp_mode,
+					 (void *)(long)init_phys_addr, 1);
+	}
+
+	/*
+	 * Unmap the identity mapping
+	 */
+	hyp_idmap_del(kvm_hyp_pgd_get(),
+		      (unsigned long)init_phys_addr,
+		      (unsigned long)init_end_phys_addr);
+
+	/*
+	 * Map the Hyp-code called directly from the host
+	 */
+	err = create_hyp_mappings(__kvm_hyp_code_start, __kvm_hyp_code_end);
+	if (err) {
+		kvm_err("Cannot map world-switch code\n");
+		goto out_free_mappings;
+	}
+
+	/*
+	 * Map the Hyp stack pages
+	 */
+	for_each_possible_cpu(cpu) {
+		char *stack_page = (char *)per_cpu(kvm_arm_hyp_stack_page, cpu);
+		err = create_hyp_mappings(stack_page, stack_page + PAGE_SIZE);
+
+		if (err) {
+			kvm_err("Cannot map hyp stack\n");
+			goto out_free_mappings;
+		}
+	}
+
+	/*
+	 * Set the HVBAR to the virtual kernel address
+	 */
+	for_each_online_cpu(cpu)

arch/arm/kvm/arm.c long kvm_arch_vm_ioctl(struct file *filp, · line 404

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+		smp_call_function_single(cpu, cpu_set_vector,
+					 __kvm_hyp_vector, 1);
+
+	return 0;
+out_free_mappings:
+	free_hyp_pmds();
+	kvm_hyp_pgd_free();
+out_free_stack_pages:
+	for_each_possible_cpu(cpu)
+		free_page(per_cpu(kvm_arm_hyp_stack_page, cpu));

should assign per_cpu(kvm_arm_hyp_stack_page, cpu) to NULL.

Is there CPU hotplug support on ARM?

#18 lore raw

Re: [PATCH v8 07/15] ARM: KVM: Hypervisor inititalization

Quoted 446 lines · Marcelo Tosatti
Quoted 445 lines · Christoffer Dall

Sets up the required registers to run code in HYP-mode from the kernel.

By setting the HVBAR the kernel can execute code in Hyp-mode with the MMU disabled. The HVBAR initially points to initialization code, which initializes other Hyp-mode registers and enables the MMU for Hyp-mode. Afterwards, the HVBAR is changed to point to KVM Hyp vectors used to catch guest faults and to switch to Hyp mode to perform a world-switch into a KVM guest.

Also provides memory mapping code to map required code pages and data structures accessed in Hyp mode at the same virtual address as the host kernel virtual addresses, but which conforms to the architectural requirements for translations in Hyp mode. This interface is added in arch/arm/kvm/arm_mmu.c and is comprised of:
 - create_hyp_mappings(hyp_pgd, start, end);
 - free_hyp_pmds(pgd_hyp);

Note: The initialization mechanism currently relies on an SMC #0 call to the secure monitor, which was merely a fast way of getting to the hypervisor. Dave Marting and Rusty Russel have patches out to make the boot-wrapper and the kernel boot in Hyp-mode and setup a generic way for hypervisors to get access to Hyp-mode if the boot-loader allows such access.

---

 arch/arm/include/asm/kvm_arm.h              |  117 +++++++++++++++++++
 arch/arm/include/asm/kvm_asm.h              |   22 +++
 arch/arm/include/asm/kvm_mmu.h              |   37 ++++++

 arch/arm/include/asm/pgtable-3level-hwdef.h |    4 + arch/arm/include/asm/pgtable-3level.h       |    4 + arch/arm/include/asm/pgtable.h              |    1 arch/arm/kvm/arm.c                          |  167 +++++++++++++++++++++++++++ arch/arm/kvm/exports.c                      |   15 ++ arch/arm/kvm/init.S                         |   99 ++++++++++++++++ arch/arm/kvm/interrupts.S                   |   47 +++++++ arch/arm/kvm/mmu.c                          |  170 +++++++++++++++++++++++++++ mm/memory.c                                 |    2

 12 files changed, 685 insertions(+)

 create mode 100644 arch/arm/include/asm/kvm_arm.h create mode 100644 arch/arm/include/asm/kvm_mmu.h

arch/arm/include/asm/kvm_arm.h new file +117 -0
new file mode 100644
index 0000000..7f30cbd
@@ -0,0 +1,117 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef __KVM_ARM_H__
+#define __KVM_ARM_H__
+
+#include <asm/types.h>
+
+/* Hyp Configuration Register (HCR) bits */
+#define HCR_TGE              (1 << 27)
+#define HCR_TVM              (1 << 26)
+#define HCR_TTLB     (1 << 25)
+#define HCR_TPU              (1 << 24)
+#define HCR_TPC              (1 << 23)
+#define HCR_TSW              (1 << 22)
+#define HCR_TAC              (1 << 21)
+#define HCR_TIDCP    (1 << 20)
+#define HCR_TSC              (1 << 19)
+#define HCR_TID3     (1 << 18)
+#define HCR_TID2     (1 << 17)
+#define HCR_TID1     (1 << 16)
+#define HCR_TID0     (1 << 15)
+#define HCR_TWE              (1 << 14)
+#define HCR_TWI              (1 << 13)
+#define HCR_DC               (1 << 12)
+#define HCR_BSU              (3 << 10)
+#define HCR_BSU_IS   (1 << 10)
+#define HCR_FB               (1 << 9)
+#define HCR_VA               (1 << 8)
+#define HCR_VI               (1 << 7)
+#define HCR_VF               (1 << 6)
+#define HCR_AMO              (1 << 5)
+#define HCR_IMO              (1 << 4)
+#define HCR_FMO              (1 << 3)
+#define HCR_PTW              (1 << 2)
+#define HCR_SWIO     (1 << 1)
+#define HCR_VM               1
+
+/*
+ * The bits we set in HCR:
+ * TAC:              Trap ACTLR
+ * TSC:              Trap SMC
+ * TWI:              Trap WFI
+ * BSU_IS:   Upgrade barriers to the inner shareable domain
+ * FB:               Force broadcast of all maintainance operations
+ * AMO:              Override CPSR.A and enable signaling with VA
+ * IMO:              Override CPSR.I and enable signaling with VI
+ * FMO:              Override CPSR.F and enable signaling with VF
+ * SWIO:     Turn set/way invalidates into set/way clean+invalidate
+ */
+#define HCR_GUEST_MASK (HCR_TSC | HCR_TWI | HCR_VM | HCR_BSU_IS | HCR_FB | \
+                     HCR_AMO | HCR_IMO | HCR_FMO | HCR_FMO | HCR_SWIO)
+
+/* Hyp System Control Register (HSCTLR) bits */
+#define HSCTLR_TE    (1 << 30)
+#define HSCTLR_EE    (1 << 25)
+#define HSCTLR_FI    (1 << 21)
+#define HSCTLR_WXN   (1 << 19)
+#define HSCTLR_I     (1 << 12)
+#define HSCTLR_C     (1 << 2)
+#define HSCTLR_A     (1 << 1)
+#define HSCTLR_M     1
+#define HSCTLR_MASK  (HSCTLR_M | HSCTLR_A | HSCTLR_C | HSCTLR_I | \
+                      HSCTLR_WXN | HSCTLR_FI | HSCTLR_EE | HSCTLR_TE)
+
+/* TTBCR and HTCR Registers bits */
+#define TTBCR_EAE    (1 << 31)
+#define TTBCR_IMP    (1 << 30)
+#define TTBCR_SH1    (3 << 28)
+#define TTBCR_ORGN1  (3 << 26)
+#define TTBCR_IRGN1  (3 << 24)
+#define TTBCR_EPD1   (1 << 23)
+#define TTBCR_A1     (1 << 22)
+#define TTBCR_T1SZ   (3 << 16)
+#define TTBCR_SH0    (3 << 12)
+#define TTBCR_ORGN0  (3 << 10)
+#define TTBCR_IRGN0  (3 << 8)
+#define TTBCR_EPD0   (1 << 7)
+#define TTBCR_T0SZ   3
+#define HTCR_MASK    (TTBCR_T0SZ | TTBCR_IRGN0 | TTBCR_ORGN0 | TTBCR_SH0)
+
+
+/* Virtualization Translation Control Register (VTCR) bits */
+#define VTCR_SH0     (3 << 12)
+#define VTCR_ORGN0   (3 << 10)
+#define VTCR_IRGN0   (3 << 8)
+#define VTCR_SL0     (3 << 6)
+#define VTCR_S               (1 << 4)
+#define VTCR_T0SZ    3
+#define VTCR_MASK    (VTCR_SH0 | VTCR_ORGN0 | VTCR_IRGN0 | VTCR_SL0 | \
+                      VTCR_S | VTCR_T0SZ | VTCR_MASK)
+#define VTCR_HTCR_SH (VTCR_SH0 | VTCR_ORGN0 | VTCR_IRGN0)
+#define VTCR_SL_L2   0               /* Starting-level: 2 */
+#define VTCR_SL_L1   (1 << 6)        /* Starting-level: 1 */
+#define VTCR_GUEST_SL        VTCR_SL_L1
+#define VTCR_GUEST_T0SZ      0
+#if VTCR_GUEST_SL == 0
+#define VTTBR_X              (14 - VTCR_GUEST_T0SZ)
+#else
+#define VTTBR_X              (5 - VTCR_GUEST_T0SZ)
+#endif
+
+
+#endif /* __KVM_ARM_H__ */
arch/arm/include/asm/kvm_asm.h +22 -0
index c3d4458..69afdf3 100644
@@ -24,5 +24,27 @@
  #define ARM_EXCEPTION_DATA_ABORT  4
  #define ARM_EXCEPTION_IRQ      5
  #define ARM_EXCEPTION_FIQ      6
+#define ARM_EXCEPTION_HVC      7
+
+/*
+ * SMC Hypervisor API call number
+ */
+#define SMCHYP_HVBAR_W 0xfffffff0
+
+#ifndef __ASSEMBLY__
+struct kvm_vcpu;
+
+extern char __kvm_hyp_init[];
+extern char __kvm_hyp_init_end[];
+
+extern char __kvm_hyp_vector[];
+
+extern char __kvm_hyp_code_start[];
+extern char __kvm_hyp_code_end[];
+
+extern void __kvm_flush_vm_context(void);
+
+extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
+#endif
 
  #endif /* __ARM_KVM_ASM_H__ */
arch/arm/include/asm/kvm_mmu.h new file +37 -0
new file mode 100644
index 0000000..1aa1af4
@@ -0,0 +1,37 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef __ARM_KVM_MMU_H__
+#define __ARM_KVM_MMU_H__
+
+/*
+ * The architecture supports 40-bit IPA as input to the 2nd stage translations
+ * and PTRS_PER_PGD2 could therefore be 1024.
+ *
+ * To save a bit of memory and to avoid alignment issues we assume 39-bit IPA
+ * for now, but remember that the level-1 table must be aligned to its size.
+ */
+#define PTRS_PER_PGD2        512
+#define PGD2_ORDER   get_order(PTRS_PER_PGD2 * sizeof(pgd_t))
+
+int create_hyp_mappings(void *from, void *to);
+void free_hyp_pmds(void);
+
+int kvm_hyp_pgd_alloc(void);
+pgd_t *kvm_hyp_pgd_get(void);
+void kvm_hyp_pgd_free(void);
+
+#endif /* __ARM_KVM_MMU_H__ */
arch/arm/include/asm/pgtable-3level-hwdef.h +4 -0
index a2d404e..18f5cef 100644
@@ -32,6 +32,9 @@
  #define PMD_TYPE_SECT                (_AT(pmdval_t, 1) << 0)
  #define PMD_BIT4             (_AT(pmdval_t, 0))
  #define PMD_DOMAIN(x)                (_AT(pmdval_t, 0))
+#define PMD_APTABLE_SHIFT    (61)
+#define PMD_APTABLE          (_AT(pgdval_t, 3) << PGD_APTABLE_SHIFT)
+#define PMD_PXNTABLE         (_AT(pgdval_t, 1) << 59)
 
  /*
   *   - section
@@ -41,6 +44,7 @@
  #define PMD_SECT_S           (_AT(pmdval_t, 3) << 8)
  #define PMD_SECT_AF          (_AT(pmdval_t, 1) << 10)
  #define PMD_SECT_nG          (_AT(pmdval_t, 1) << 11)
+#define PMD_SECT_PXN         (_AT(pmdval_t, 1) << 53)
  #define PMD_SECT_XN          (_AT(pmdval_t, 1) << 54)
  #define PMD_SECT_AP_WRITE    (_AT(pmdval_t, 0))
  #define PMD_SECT_AP_READ     (_AT(pmdval_t, 0))
arch/arm/include/asm/pgtable-3level.h +4 -0
index b249035..1169a8a 100644
@@ -107,6 +107,10 @@
  #define pud_none(pud)                (!pud_val(pud))
  #define pud_bad(pud)         (!(pud_val(pud) & 2))
  #define pud_present(pud)     (pud_val(pud))
+#define pmd_table(pmd)               ((pmd_val(pmd) & PMD_TYPE_MASK) == \
+                                              PMD_TYPE_TABLE)
+#define pmd_sect(pmd)                ((pmd_val(pmd) & PMD_TYPE_MASK) == \
+                                              PMD_TYPE_SECT)
 
  #define pud_clear(pudp)                      \
       do {                            \
arch/arm/include/asm/pgtable.h +1 -0
index c7bd809..4b72287 100644
@@ -82,6 +82,7 @@ extern pgprot_t             pgprot_kernel;
  #define PAGE_READONLY_EXEC   _MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_RDONLY)
  #define PAGE_KERNEL          _MOD_PROT(pgprot_kernel, L_PTE_XN)
  #define PAGE_KERNEL_EXEC     pgprot_kernel
+#define PAGE_HYP             _MOD_PROT(pgprot_kernel, L_PTE_USER)
 
  #define __PAGE_NONE          __pgprot(_L_PTE_DEFAULT | L_PTE_RDONLY | L_PTE_XN)
  #define __PAGE_SHARED                __pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN)
arch/arm/kvm/arm.c +141 -0
index 5992d90..4c61d3c 100644
@@ -31,6 +31,12 @@
  #include <asm/uaccess.h>
  #include <asm/ptrace.h>
  #include <asm/mman.h>
+#include <asm/tlbflush.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
+#include <asm/kvm_mmu.h>
+
+static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
 
  int kvm_arch_hardware_enable(void *garbage)
  {
@@ -255,13 +261,174 @@ long kvm_arch_vm_ioctl(struct file *filp,
       return -EINVAL;
  }
 
+static void cpu_set_vector(void *vector)
+{
+     unsigned long vector_ptr;
+     unsigned long smc_hyp_nr;
+
+     vector_ptr = (unsigned long)vector;
+     smc_hyp_nr = SMCHYP_HVBAR_W;
+
+     /*
+      * Set the HVBAR
+      */
+     asm volatile (
+             "mov    r0, %[vector_ptr]\n\t"
+             "mov    r7, %[smc_hyp_nr]\n\t"
+             "smc    #0\n\t" : :
+             [vector_ptr] "r" (vector_ptr),
+             [smc_hyp_nr] "r" (smc_hyp_nr) :
+             "r0", "r7");
+}
+
+static void cpu_init_hyp_mode(void *vector)
+{
+     unsigned long pgd_ptr;
+     unsigned long hyp_stack_ptr;
+     unsigned long stack_page;
+
+     cpu_set_vector(vector);
+
+     pgd_ptr = virt_to_phys(kvm_hyp_pgd_get());
+     stack_page = __get_cpu_var(kvm_arm_hyp_stack_page);
+     hyp_stack_ptr = stack_page + PAGE_SIZE;
+
+     /*
+      * Call initialization code
+      */
+     asm volatile (
+             "mov    r0, %[pgd_ptr]\n\t"
+             "mov    r1, %[hyp_stack_ptr]\n\t"
+             "hvc    #0\n\t" : :
+             [pgd_ptr] "r" (pgd_ptr),
+             [hyp_stack_ptr] "r" (hyp_stack_ptr) :
+             "r0", "r1");
+}
+
+/**
+ * Inits Hyp-mode on all online CPUs
+ */
+static int init_hyp_mode(void)
+{
+     phys_addr_t init_phys_addr, init_end_phys_addr;
+     int cpu;
+     int err = 0;
+
+     /*
+      * Allocate stack pages for Hypervisor-mode
+      */
+     for_each_possible_cpu(cpu) {
+             unsigned long stack_page;
+
+             stack_page = __get_free_page(GFP_KERNEL);
+             if (!stack_page) {
+                     err = -ENOMEM;
+                     goto out_free_stack_pages;
+             }
+
+             per_cpu(kvm_arm_hyp_stack_page, cpu) = stack_page;
+     }
+
+     /*
+      * Allocate Hyp level-1 page table
+      */
+     err = kvm_hyp_pgd_alloc();
+     if (err)
+             goto out_free_stack_pages;
+
+     init_phys_addr = virt_to_phys(__kvm_hyp_init);
+     init_end_phys_addr = virt_to_phys(__kvm_hyp_init_end);
+     BUG_ON(init_phys_addr & 0x1f);
+
+     /*
+      * Create identity mapping for the init code.
+      */
+     hyp_idmap_add(kvm_hyp_pgd_get(),
+                   (unsigned long)init_phys_addr,
+                   (unsigned long)init_end_phys_addr);
+
+     /*
+      * Execute the init code on each CPU.
+      *
+      * Note: The stack is not mapped yet, so don't do anything else than
+      * initializing the hypervisor mode on each CPU using a local stack
+      * space for temporary storage.
+      */
+     for_each_online_cpu(cpu) {
+             smp_call_function_single(cpu, cpu_init_hyp_mode,
+                                      (void *)(long)init_phys_addr, 1);
+     }
+
+     /*
+      * Unmap the identity mapping
+      */
+     hyp_idmap_del(kvm_hyp_pgd_get(),
+                   (unsigned long)init_phys_addr,
+                   (unsigned long)init_end_phys_addr);
+
+     /*
+      * Map the Hyp-code called directly from the host
+      */
+     err = create_hyp_mappings(__kvm_hyp_code_start, __kvm_hyp_code_end);
+     if (err) {
+             kvm_err("Cannot map world-switch code\n");
+             goto out_free_mappings;
+     }
+
+     /*
+      * Map the Hyp stack pages
+      */
+     for_each_possible_cpu(cpu) {
+             char *stack_page = (char *)per_cpu(kvm_arm_hyp_stack_page, cpu);
+             err = create_hyp_mappings(stack_page, stack_page + PAGE_SIZE);
+
+             if (err) {
+                     kvm_err("Cannot map hyp stack\n");
+                     goto out_free_mappings;
+             }
+     }
+
+     /*
+      * Set the HVBAR to the virtual kernel address
+      */
+     for_each_online_cpu(cpu)
+             smp_call_function_single(cpu, cpu_set_vector,
+                                      __kvm_hyp_vector, 1);
+
+     return 0;
Quoted 9 lines

arch/arm/kvm/arm.c long kvm_arch_vm_ioctl(struct file *filp, · line 404

Quoted 6 lines
arch/arm/kvm/arm.c +6 -0
+out_free_mappings:
+     free_hyp_pmds();
+     kvm_hyp_pgd_free();
+out_free_stack_pages:
+     for_each_possible_cpu(cpu)
+             free_page(per_cpu(kvm_arm_hyp_stack_page, cpu));

should assign per_cpu(kvm_arm_hyp_stack_page, cpu) to NULL.

why? this is run as part of the init code and thus the only way it could ever run again would be to have the module unloaded in which case the variable would be re-initialized to zero as per the static declaration, no?

Quoted 2 lines

Is there CPU hotplug support on ARM?

I don't think (read: hope) so. ARM people?

-Christoffer

#19 lore raw

Re: [PATCH v8 07/15] ARM: KVM: Hypervisor inititalization

Quoted 7 lines · Christoffer Dall
Quoted 2 lines

should assign per_cpu(kvm_arm_hyp_stack_page, cpu) to NULL.

why? this is run as part of the init code and thus the only way it could ever run again would be to have the module unloaded in which case the variable would be re-initialized to zero as per the static declaration, no?

Right.

#20 lore raw

PATCH v8 8/15 ARM: KVM: Module unloading support

In reply to Christoffer Dall

Current initialization code relies on the MMU-bit and TE-bit of the HSCTLR register to be cleared, so to support re-inserting the KVM module we must clear these bits when unloading the module.

This is going to change in two ways:

First, the init id-map code is going to go away in favor of section-based id-mapping.

Second, we are not going to use the SMC call in the future, but rather an HVC instruction to take control of Hyp mode. We need, however, a method to setup the original init code again to support module unloading. It is useful to add this support at this point since we will (a) remember to support unloading and (b) benefit from shorter debug cycles.

---
 arch/arm/include/asm/kvm_asm.h |    3 ++
 arch/arm/kvm/arm.c             |   50 ++++++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/exports.c         |    3 ++
 arch/arm/kvm/init.S            |   28 ++++++++++++++++++++++
 4 files changed, 84 insertions(+)
arch/arm/include/asm/kvm_asm.h +3 -0
index 69afdf3..c2ec131 100644
@@ -37,6 +37,9 @@ struct kvm_vcpu;
 extern char __kvm_hyp_init[];
 extern char __kvm_hyp_init_end[];
 
+extern char __kvm_hyp_exit[];
+extern char __kvm_hyp_exit_end[];
+
 extern char __kvm_hyp_vector[];
 
 extern char __kvm_hyp_code_start[];
arch/arm/kvm/arm.c +50 -0

60 lines not on this page · raw message

arch/arm/kvm/exports.c +3 -0
index 2631609..9bdaf11 100644
@@ -19,6 +19,9 @@
 EXPORT_SYMBOL_GPL(__kvm_hyp_init);
 EXPORT_SYMBOL_GPL(__kvm_hyp_init_end);
 
+EXPORT_SYMBOL_GPL(__kvm_hyp_exit);
+EXPORT_SYMBOL_GPL(__kvm_hyp_exit_end);
+
 EXPORT_SYMBOL_GPL(__kvm_hyp_vector);
 
 EXPORT_SYMBOL_GPL(__kvm_hyp_code_start);
arch/arm/kvm/init.S +28 -0
index 7800023..c3beb20 100644
@@ -114,3 +114,31 @@ __do_hyp_init:
 	__kvm_init_sp:
 	.globl __kvm_hyp_init_end
 __kvm_hyp_init_end:
+
+
+	.align 12
+__kvm_hyp_exit:
+	.globl __kvm_hyp_exit
+
+	@ Hyp-mode exception vector
+	nop
+	nop
+	nop
+	nop
+	nop
+	b	__do_hyp_exit
+	nop
+	nop
+
+__do_hyp_exit:
+	@ Clear the MMU and TE bits in the HSCR
+	mrc	p15, 4, sp, c1, c0, 0	@ HSCR
+	bic	sp, sp, #((1 << 30) | (1 << 0))
+
+	isb
+	mcr	p15, 4, sp, c1, c0, 0	@ HSCR
+	isb
+	eret
+
+	.globl __kvm_hyp_exit_end
+__kvm_hyp_exit_end:
#21 lore raw

PATCH v8 9/15 ARM: KVM: Memory virtualization setup

In reply to Christoffer Dall

From: Christoffer Dall <[email protected]>

This commit introduces the framework for guest memory management through the use of 2nd stage translation. Each VM has a pointer to a level-1 table (the pgd field in struct kvm_arch) which is used for the 2nd stage translations. Entries are added when handling guest faults (later patch) and the table itself can be allocated and freed through the following functions implemented in arch/arm/kvm/arm_mmu.c:
- kvm_alloc_stage2_pgd(struct kvm *kvm);
- kvm_free_stage2_pgd(struct kvm *kvm);

Further, each entry in TLBs and caches are tagged with a VMID identifier in addition to ASIDs. The VMIDs are assigned consecutively to VMs in the order that VMs are executed, and caches and tlbs are invalidated when the VMID space has been used to allow for more than 255 simultaenously running guests.

The 2nd stage pgd is allocated in kvm_arch_init_vm(). The table is freed in kvm_arch_destroy_vm(). Both functions are called from the main KVM code.

---
 arch/arm/include/asm/kvm_arm.h |    2 -
 arch/arm/include/asm/kvm_mmu.h |    5 ++
 arch/arm/kvm/arm.c             |   65 ++++++++++++++++++++++---
 arch/arm/kvm/mmu.c             |  103 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 166 insertions(+), 9 deletions(-)
arch/arm/include/asm/kvm_arm.h +1 -1
index 7f30cbd..257242f 100644
@@ -62,7 +62,7 @@
  * SWIO:	Turn set/way invalidates into set/way clean+invalidate
  */
 #define HCR_GUEST_MASK (HCR_TSC | HCR_TWI | HCR_VM | HCR_BSU_IS | HCR_FB | \
-			HCR_AMO | HCR_IMO | HCR_FMO | HCR_FMO | HCR_SWIO)
+			HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | HCR_SWIO)
 
 /* Hyp System Control Register (HSCTLR) bits */
 #define HSCTLR_TE	(1 << 30)
arch/arm/include/asm/kvm_mmu.h +5 -0
index 1aa1af4..d95662eb 100644
@@ -34,4 +34,9 @@ int kvm_hyp_pgd_alloc(void);
 pgd_t *kvm_hyp_pgd_get(void);
 void kvm_hyp_pgd_free(void);
 
+int kvm_alloc_stage2_pgd(struct kvm *kvm);
+void kvm_free_stage2_pgd(struct kvm *kvm);
+
+int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run);
+
 #endif /* __ARM_KVM_MMU_H__ */
arch/arm/kvm/arm.c +57 -8

105 lines not on this page · raw message

arch/arm/kvm/mmu.c +103 -0

109 lines not on this page · raw message

#22 lore raw

Re: [PATCH v8 09/15] ARM: KVM: Memory virtualization setup

Quoted 203 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

This commit introduces the framework for guest memory management through the use of 2nd stage translation. Each VM has a pointer to a level-1 table (the pgd field in struct kvm_arch) which is used for the 2nd stage translations. Entries are added when handling guest faults (later patch) and the table itself can be allocated and freed through the following functions implemented in arch/arm/kvm/arm_mmu.c:
- kvm_alloc_stage2_pgd(struct kvm *kvm);
- kvm_free_stage2_pgd(struct kvm *kvm);

Further, each entry in TLBs and caches are tagged with a VMID identifier in addition to ASIDs. The VMIDs are assigned consecutively to VMs in the order that VMs are executed, and caches and tlbs are invalidated when the VMID space has been used to allow for more than 255 simultaenously running guests.

The 2nd stage pgd is allocated in kvm_arch_init_vm(). The table is freed in kvm_arch_destroy_vm(). Both functions are called from the main KVM code.

---
 arch/arm/include/asm/kvm_arm.h |    2 -
 arch/arm/include/asm/kvm_mmu.h |    5 ++
 arch/arm/kvm/arm.c             |   65 ++++++++++++++++++++++---
 arch/arm/kvm/mmu.c             |  103 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 166 insertions(+), 9 deletions(-)
arch/arm/include/asm/kvm_arm.h +1 -1
index 7f30cbd..257242f 100644
@@ -62,7 +62,7 @@
  * SWIO:	Turn set/way invalidates into set/way clean+invalidate
  */
 #define HCR_GUEST_MASK (HCR_TSC | HCR_TWI | HCR_VM | HCR_BSU_IS | HCR_FB | \
-			HCR_AMO | HCR_IMO | HCR_FMO | HCR_FMO | HCR_SWIO)
+			HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | HCR_SWIO)
 
 /* Hyp System Control Register (HSCTLR) bits */
 #define HSCTLR_TE	(1 << 30)
arch/arm/include/asm/kvm_mmu.h +5 -0
index 1aa1af4..d95662eb 100644
@@ -34,4 +34,9 @@ int kvm_hyp_pgd_alloc(void);
 pgd_t *kvm_hyp_pgd_get(void);
 void kvm_hyp_pgd_free(void);
 
+int kvm_alloc_stage2_pgd(struct kvm *kvm);
+void kvm_free_stage2_pgd(struct kvm *kvm);
+
+int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run);
+
 #endif /* __ARM_KVM_MMU_H__ */
arch/arm/kvm/arm.c +57 -8
index efe130c..81babe9 100644
@@ -38,6 +38,13 @@
 
 static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
 
+/* The VMID used in the VTTBR */
+#define VMID_BITS               8
+#define VMID_MASK               ((1 << VMID_BITS) - 1)
+#define VMID_FIRST_GENERATION	(1 << VMID_BITS)
+static u64 next_vmid;		/* The next available VMID in the sequence */
+DEFINE_SPINLOCK(kvm_vmid_lock);
+
 int kvm_arch_hardware_enable(void *garbage)
 {
 	return 0;
@@ -70,14 +77,6 @@ void kvm_arch_sync_events(struct kvm *kvm)
 {
 }
 
-int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
-{
-	if (type)
-		return -EINVAL;
-
-	return 0;
-}
-
 int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
 {
 	return VM_FAULT_SIGBUS;
@@ -93,10 +92,46 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)
 	return 0;
 }
 
+/**
+ * kvm_arch_init_vm - initializes a VM data structure
+ * @kvm:	pointer to the KVM struct
+ */
+int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
+{
+	int ret = 0;
+
+	if (type)
+		return -EINVAL;
+
+	ret = kvm_alloc_stage2_pgd(kvm);
+	if (ret)
+		goto out_fail_alloc;
+	mutex_init(&kvm->arch.pgd_mutex);
+
+	ret = create_hyp_mappings(kvm, kvm + 1);
+	if (ret)
+		goto out_free_stage2_pgd;
+
+	/* Mark the initial VMID invalid */
+	kvm->arch.vmid = 0;
+
+	return ret;
+out_free_stage2_pgd:
+	kvm_free_stage2_pgd(kvm);
+out_fail_alloc:
+	return ret;
+}
+
+/**
+ * kvm_arch_destroy_vm - destroy the VM data structure
+ * @kvm:	pointer to the KVM struct
+ */
 void kvm_arch_destroy_vm(struct kvm *kvm)
 {
 	int i;
 
+	kvm_free_stage2_pgd(kvm);
+
 	for (i = 0; i < KVM_MAX_VCPUS; ++i) {
 		if (kvm->vcpus[i]) {
 			kvm_arch_vcpu_free(kvm->vcpus[i]);
@@ -172,6 +207,10 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
 	if (err)
 		goto free_vcpu;
 
+	err = create_hyp_mappings(vcpu, vcpu + 1);
+	if (err)
+		goto free_vcpu;
+
 	return vcpu;
 free_vcpu:
 	kmem_cache_free(kvm_vcpu_cache, vcpu);
@@ -181,6 +220,7 @@ out:
 
 void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
 {
+	kmem_cache_free(kvm_vcpu_cache, vcpu);
 }
 
 void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
@@ -416,6 +456,15 @@ int kvm_arch_init(void *opaque)
 	if (err)
 		goto out_err;
 
+	/*
+	 * The upper 56 bits of VMIDs are used to identify the generation
+	 * counter, so VMIDs initialized to 0, having generation == 0, will
+	 * never be considered valid and therefor a new VMID must always be
+	 * assigned. Whent he VMID generation rolls over, we start from
+	 * VMID_FIRST_GENERATION again.
+	 */
+	next_vmid = VMID_FIRST_GENERATION;
+
 	return 0;
 out_err:
 	return err;
arch/arm/kvm/mmu.c +23 -0
index a320b56a..b256540 100644
@@ -159,6 +159,109 @@ out:
 	return err;
 }
 
+/**
+ * kvm_alloc_stage2_pgd - allocate level-1 table for stage-2 translation.
+ * @kvm:	The KVM struct pointer for the VM.
+ *
+ * Allocates the 1st level table only of size defined by PGD2_ORDER (can
+ * support either full 40-bit input addresses or limited to 32-bit input
+ * addresses). Clears the allocated pages.
+ */
+int kvm_alloc_stage2_pgd(struct kvm *kvm)
+{
+	pgd_t *pgd;
+
+	if (kvm->arch.pgd != NULL) {
+		kvm_err("kvm_arch already initialized?\n");
+		return -EINVAL;
+	}
+
+	pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, PGD2_ORDER);
+	if (!pgd)
+		return -ENOMEM;
+
+	memset(pgd, 0, PTRS_PER_PGD2 * sizeof(pgd_t));
+	kvm->arch.pgd = pgd;

arch/arm/kvm/mmu.c out: · line 194

Quoted 10 lines
arch/arm/kvm/mmu.c +10 -0
+
+	return 0;
+}
+
+static void free_guest_pages(pte_t *pte, unsigned long addr)
+{
+	unsigned int i;
+	struct page *page;
+
+	for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE) {

Hmm, "addr" is not used.

arch/arm/kvm/mmu.c out: · line 196

Quoted 2 lines
arch/arm/kvm/mmu.c +2 -0
+		if (!pte_present(*pte))
+			goto next_page;

Why goto instead of:

              if(pte_present(*pte)) {

arch/arm/kvm/mmu.c out: · line 198

Quoted 2 lines
arch/arm/kvm/mmu.c +2 -0
+		page = pfn_to_page(pte_pfn(*pte));
+		put_page(page);
              }
Quoted 71 lines
arch/arm/kvm/mmu.c +66 -0
+next_page:
+		pte++;
+	}
+}
 
+
+static void free_stage2_ptes(pmd_t *pmd, unsigned long addr)
+{
+	unsigned int i;
+	pte_t *pte;
+	struct page *page;
+
+	for (i = 0; i < PTRS_PER_PMD; i++, addr += PMD_SIZE) {
+		BUG_ON(pmd_sect(*pmd));
+		if (!pmd_none(*pmd) && pmd_table(*pmd)) {
+			pte = pte_offset_kernel(pmd, addr);
+			free_guest_pages(pte, addr);
+			page = virt_to_page((void *)pte);
+			WARN_ON(atomic_read(&page->_count) != 1);
+			pte_free_kernel(NULL, pte);
+		}
+		pmd++;
+	}
+}
+
+/**
+ * kvm_free_stage2_pgd - free all stage-2 tables
+ * @kvm:	The KVM struct pointer for the VM.
+ *
+ * Walks the level-1 page table pointed to by kvm->arch.pgd and frees all
+ * underlying level-2 and level-3 tables before freeing the actual level-1 table
+ * and setting the struct pointer to NULL.
+ */
+void kvm_free_stage2_pgd(struct kvm *kvm)
+{
+	pgd_t *pgd;
+	pud_t *pud;
+	pmd_t *pmd;
+	unsigned long long i, addr;
+
+	if (kvm->arch.pgd == NULL)
+		return;
+
+	/*
+	 * We do this slightly different than other places, since we need more
+	 * than 32 bits and for instance pgd_addr_end converts to unsigned long.
+	 */
+	addr = 0;
+	for (i = 0; i < PTRS_PER_PGD2; i++) {
+		addr = i * (unsigned long long)PGDIR_SIZE;
+		pgd = kvm->arch.pgd + i;
+		pud = pud_offset(pgd, addr);
+
+		if (pud_none(*pud))
+			continue;
+
+		BUG_ON(pud_bad(*pud));
+
+		pmd = pmd_offset(pud, addr);
+		free_stage2_ptes(pmd, addr);
+		pmd_free(NULL, pmd);
+	}
+
+	free_pages((unsigned long)kvm->arch.pgd, PGD2_ORDER);
+	kvm->arch.pgd = NULL;
+}
+
 int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {

return -EINVAL;

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Gleb.

#23 lore raw

Re: [PATCH v8 09/15] ARM: KVM: Memory virtualization setup

Quoted 207 lines · Gleb Natapov
Quoted 206 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

This commit introduces the framework for guest memory management through the use of 2nd stage translation. Each VM has a pointer to a level-1 table (the pgd field in struct kvm_arch) which is used for the 2nd stage translations. Entries are added when handling guest faults (later patch) and the table itself can be allocated and freed through the following functions implemented in arch/arm/kvm/arm_mmu.c:
 - kvm_alloc_stage2_pgd(struct kvm *kvm);
 - kvm_free_stage2_pgd(struct kvm *kvm);

Further, each entry in TLBs and caches are tagged with a VMID identifier in addition to ASIDs. The VMIDs are assigned consecutively to VMs in the order that VMs are executed, and caches and tlbs are invalidated when the VMID space has been used to allow for more than 255 simultaenously running guests.

The 2nd stage pgd is allocated in kvm_arch_init_vm(). The table is freed in kvm_arch_destroy_vm(). Both functions are called from the main KVM code.

---
 arch/arm/include/asm/kvm_arm.h |    2 - arch/arm/include/asm/kvm_mmu.h |    5 ++
 arch/arm/kvm/arm.c             |   65 ++++++++++++++++++++++--- arch/arm/kvm/mmu.c             |  103 ++++++++++++++++++++++++++++++++++++++++

 4 files changed, 166 insertions(+), 9 deletions(-)
arch/arm/include/asm/kvm_arm.h +1 -1
index 7f30cbd..257242f 100644
@@ -62,7 +62,7 @@
   * SWIO:     Turn set/way invalidates into set/way clean+invalidate
   */
  #define HCR_GUEST_MASK (HCR_TSC | HCR_TWI | HCR_VM | HCR_BSU_IS | HCR_FB | \
-                     HCR_AMO | HCR_IMO | HCR_FMO | HCR_FMO | HCR_SWIO)
+                     HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | HCR_SWIO)
 
  /* Hyp System Control Register (HSCTLR) bits */
  #define HSCTLR_TE    (1 << 30)
arch/arm/include/asm/kvm_mmu.h +5 -0
index 1aa1af4..d95662eb 100644
@@ -34,4 +34,9 @@ int kvm_hyp_pgd_alloc(void);
  pgd_t *kvm_hyp_pgd_get(void);
  void kvm_hyp_pgd_free(void);
 
+int kvm_alloc_stage2_pgd(struct kvm *kvm);
+void kvm_free_stage2_pgd(struct kvm *kvm);
+
+int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run);
+
  #endif /* __ARM_KVM_MMU_H__ */
arch/arm/kvm/arm.c +47 -8
index efe130c..81babe9 100644
@@ -38,6 +38,13 @@
 
  static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
 
+/* The VMID used in the VTTBR */
+#define VMID_BITS               8
+#define VMID_MASK               ((1 << VMID_BITS) - 1)
+#define VMID_FIRST_GENERATION        (1 << VMID_BITS)
+static u64 next_vmid;                /* The next available VMID in the sequence */
+DEFINE_SPINLOCK(kvm_vmid_lock);
+
  int kvm_arch_hardware_enable(void *garbage)
  {
       return 0;
@@ -70,14 +77,6 @@ void kvm_arch_sync_events(struct kvm *kvm)
  {
  }
 
-int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
-{
-     if (type)
-             return -EINVAL;
-
-     return 0;
-}
-
  int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
  {
       return VM_FAULT_SIGBUS;
@@ -93,10 +92,46 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)
       return 0;
  }
 
+/**
+ * kvm_arch_init_vm - initializes a VM data structure
+ * @kvm:     pointer to the KVM struct
+ */
+int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
+{
+     int ret = 0;
+
+     if (type)
+             return -EINVAL;
+
+     ret = kvm_alloc_stage2_pgd(kvm);
+     if (ret)
+             goto out_fail_alloc;
+     mutex_init(&kvm->arch.pgd_mutex);
+
+     ret = create_hyp_mappings(kvm, kvm + 1);
+     if (ret)
+             goto out_free_stage2_pgd;
+
+     /* Mark the initial VMID invalid */
+     kvm->arch.vmid = 0;
+
+     return ret;
+out_free_stage2_pgd:
+     kvm_free_stage2_pgd(kvm);
+out_fail_alloc:
+     return ret;
+}
+
+/**
+ * kvm_arch_destroy_vm - destroy the VM data structure
+ * @kvm:     pointer to the KVM struct
+ */
  void kvm_arch_destroy_vm(struct kvm *kvm)
  {
       int i;
 
+     kvm_free_stage2_pgd(kvm);
+
       for (i = 0; i < KVM_MAX_VCPUS; ++i) {
               if (kvm->vcpus[i]) {
                       kvm_arch_vcpu_free(kvm->vcpus[i]);
@@ -172,6 +207,10 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
       if (err)
               goto free_vcpu;
 
+     err = create_hyp_mappings(vcpu, vcpu + 1);
+     if (err)
+             goto free_vcpu;
+
       return vcpu;

 free_vcpu:
      kmem_cache_free(kvm_vcpu_cache, vcpu);

arch/arm/kvm/arm.c +10 -0
@@ -181,6 +220,7 @@ out:
 
  void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
  {
+     kmem_cache_free(kvm_vcpu_cache, vcpu);
  }
 
  void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
@@ -416,6 +456,15 @@ int kvm_arch_init(void *opaque)
       if (err)
               goto out_err;
 
+     /*
+      * The upper 56 bits of VMIDs are used to identify the generation
+      * counter, so VMIDs initialized to 0, having generation == 0, will
+      * never be considered valid and therefor a new VMID must always be
+      * assigned. Whent he VMID generation rolls over, we start from
+      * VMID_FIRST_GENERATION again.
+      */
+     next_vmid = VMID_FIRST_GENERATION;
+
       return 0;

 out_err:
      return err;

arch/arm/kvm/mmu.c +26 -0
index a320b56a..b256540 100644
@@ -159,6 +159,109 @@ out:
       return err;
  }
 
+/**
+ * kvm_alloc_stage2_pgd - allocate level-1 table for stage-2 translation.
+ * @kvm:     The KVM struct pointer for the VM.
+ *
+ * Allocates the 1st level table only of size defined by PGD2_ORDER (can
+ * support either full 40-bit input addresses or limited to 32-bit input
+ * addresses). Clears the allocated pages.
+ */
+int kvm_alloc_stage2_pgd(struct kvm *kvm)
+{
+     pgd_t *pgd;
+
+     if (kvm->arch.pgd != NULL) {
+             kvm_err("kvm_arch already initialized?\n");
+             return -EINVAL;
+     }
+
+     pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, PGD2_ORDER);
+     if (!pgd)
+             return -ENOMEM;
+
+     memset(pgd, 0, PTRS_PER_PGD2 * sizeof(pgd_t));
+     kvm->arch.pgd = pgd;
+
+     return 0;
+}
Quoted 9 lines

arch/arm/kvm/mmu.c out: · line 194

Quoted 7 lines
arch/arm/kvm/mmu.c +7 -0
+
+static void free_guest_pages(pte_t *pte, unsigned long addr)
+{
+     unsigned int i;
+     struct page *page;
+
+     for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE) {

Hmm, "addr" is not used.

indeed it's not

Quoted 4 lines

arch/arm/kvm/mmu.c out: · line 196

Quoted 2 lines
arch/arm/kvm/mmu.c +2 -0
+             if (!pte_present(*pte))
+                     goto next_page;

Why goto instead of:

historic reasons, thanks.

Quoted 83 lines

             if(pte_present(*pte)) {

arch/arm/kvm/mmu.c out: · line 198

Quoted 2 lines
arch/arm/kvm/mmu.c +2 -0
+             page = pfn_to_page(pte_pfn(*pte));
+             put_page(page);

             }

Quoted 71 lines
arch/arm/kvm/mmu.c +66 -0
+next_page:
+             pte++;
+     }
+}
 
+
+static void free_stage2_ptes(pmd_t *pmd, unsigned long addr)
+{
+     unsigned int i;
+     pte_t *pte;
+     struct page *page;
+
+     for (i = 0; i < PTRS_PER_PMD; i++, addr += PMD_SIZE) {
+             BUG_ON(pmd_sect(*pmd));
+             if (!pmd_none(*pmd) && pmd_table(*pmd)) {
+                     pte = pte_offset_kernel(pmd, addr);
+                     free_guest_pages(pte, addr);
+                     page = virt_to_page((void *)pte);
+                     WARN_ON(atomic_read(&page->_count) != 1);
+                     pte_free_kernel(NULL, pte);
+             }
+             pmd++;
+     }
+}
+
+/**
+ * kvm_free_stage2_pgd - free all stage-2 tables
+ * @kvm:     The KVM struct pointer for the VM.
+ *
+ * Walks the level-1 page table pointed to by kvm->arch.pgd and frees all
+ * underlying level-2 and level-3 tables before freeing the actual level-1 table
+ * and setting the struct pointer to NULL.
+ */
+void kvm_free_stage2_pgd(struct kvm *kvm)
+{
+     pgd_t *pgd;
+     pud_t *pud;
+     pmd_t *pmd;
+     unsigned long long i, addr;
+
+     if (kvm->arch.pgd == NULL)
+             return;
+
+     /*
+      * We do this slightly different than other places, since we need more
+      * than 32 bits and for instance pgd_addr_end converts to unsigned long.
+      */
+     addr = 0;
+     for (i = 0; i < PTRS_PER_PGD2; i++) {
+             addr = i * (unsigned long long)PGDIR_SIZE;
+             pgd = kvm->arch.pgd + i;
+             pud = pud_offset(pgd, addr);
+
+             if (pud_none(*pud))
+                     continue;
+
+             BUG_ON(pud_bad(*pud));
+
+             pmd = pmd_offset(pud, addr);
+             free_stage2_ptes(pmd, addr);
+             pmd_free(NULL, pmd);
+     }
+
+     free_pages((unsigned long)kvm->arch.pgd, PGD2_ORDER);
+     kvm->arch.pgd = NULL;
+}
+
  int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
  {

      return -EINVAL;

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
                       Gleb.

#24 lore raw

Re: [PATCH v8 09/15] ARM: KVM: Memory virtualization setup

In reply to Christoffer Dall
Quoted 18 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

This commit introduces the framework for guest memory management through the use of 2nd stage translation. Each VM has a pointer to a level-1 table (the pgd field in struct kvm_arch) which is used for the 2nd stage translations. Entries are added when handling guest faults (later patch) and the table itself can be allocated and freed through the following functions implemented in arch/arm/kvm/arm_mmu.c:
- kvm_alloc_stage2_pgd(struct kvm *kvm);
- kvm_free_stage2_pgd(struct kvm *kvm);

Further, each entry in TLBs and caches are tagged with a VMID identifier in addition to ASIDs. The VMIDs are assigned consecutively to VMs in the order that VMs are executed, and caches and tlbs are invalidated when the VMID space has been used to allow for more than 255 simultaenously running guests.

Quoted 5 lines

The 2nd stage pgd is allocated in kvm_arch_init_vm(). The table is freed in kvm_arch_destroy_vm(). Both functions are called from the main KVM code.

Can you explain on a high level how the IPA -> PA mappings work?

#25 lore raw

Re: [PATCH v8 09/15] ARM: KVM: Memory virtualization setup

Quoted 19 lines · Marcelo Tosatti
Quoted 18 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

This commit introduces the framework for guest memory management through the use of 2nd stage translation. Each VM has a pointer to a level-1 table (the pgd field in struct kvm_arch) which is used for the 2nd stage translations. Entries are added when handling guest faults (later patch) and the table itself can be allocated and freed through the following functions implemented in arch/arm/kvm/arm_mmu.c:
 - kvm_alloc_stage2_pgd(struct kvm *kvm);
 - kvm_free_stage2_pgd(struct kvm *kvm);

Further, each entry in TLBs and caches are tagged with a VMID identifier in addition to ASIDs. The VMIDs are assigned consecutively to VMs in the order that VMs are executed, and caches and tlbs are invalidated when the VMID space has been used to allow for more than 255 simultaenously running guests.

Quoted 8 lines
Quoted 5 lines

The 2nd stage pgd is allocated in kvm_arch_init_vm(). The table is freed in kvm_arch_destroy_vm(). Both functions are called from the main KVM code.

Can you explain on a high level how the IPA -> PA mappings work?

the memory system on ARM with Virtualization Extensions is separated into two stages: stage 1 and stage 2.

If stage 2 translation is disabled, which it is when we boot the host kernel, only a three-level page table for stage 1 translations are performed by the MMU and the result of the stage 1 translation is used to physically access memory.

If stage 2 translation is enabled, the output of the stage 1 translation (which is a 40-bit intermediate physical address, IPA, a.k.a. gpa_t/gfn in KVM language) is used for a stage 2 translation that takes the 40-bit address as input and uses another set of page tables, in 3 levels, to produce the resulting address.

If a fault happens during stage 1 translation, this fault is taken:
a) directly by the host when the host is running and stage 2
translation is disabled
b) directly by the guest VM

If a fault happens during stage 2 translation, the fault is always taken by the hypervisor, which populates the missing entry in the stage 2 page table (or changes the entry to be a writable entry in the case of a permission fault).

During boot of a VM, the MMU is disabled for the guest Stage 1 translations and the address produced by the CPU is fed directly to the stage 2 translation system.

A nice diagram is shown on page B3-1330 of the ARM arm I referred you to in the other mail.

Let me know if this is the level you had in mind.

-Christoffer

#26 lore raw

PATCH v8 10/15 ARM: KVM: Inject IRQs and FIQs from userspace

In reply to Christoffer Dall

From: Christoffer Dall <[email protected]>

Userspace can inject IRQs and FIQs through the KVM_IRQ_LINE VM ioctl. This ioctl is used since the sematics are in fact two lines that can be either raised or lowered on the VCPU - the IRQ and FIQ lines.

KVM needs to know which VCPU it must operate on and whether the FIQ or IRQ line is raised/lowered. Hence both pieces of information is packed in the kvm_irq_level->irq field. The irq fild value will be:
IRQ: vcpu_index << 1

  FIQ: (vcpu_index << 1) | 1

This is documented in Documentation/kvm/api.txt.

The effect of the ioctl is simply to simply raise/lower the corresponding irq_line field on the VCPU struct, which will cause the world-switch code to raise/lower virtual interrupts when running the guest on next switch. The wait_for_interrupt flag is also cleared for raised IRQs or FIQs causing an idle VCPU to become active again. CPUs in guest mode are kicked to make sure they refresh their interrupt status.

---
 Documentation/virtual/kvm/api.txt |   12 +++++--
 arch/arm/include/asm/kvm.h        |    9 +++++
 arch/arm/include/asm/kvm_arm.h    |    1 +
 arch/arm/kvm/arm.c                |   62 ++++++++++++++++++++++++++++++++++++-
 include/linux/kvm.h               |    1 +
 5 files changed, 80 insertions(+), 5 deletions(-)
Documentation/virtual/kvm/api.txt +8 -4
index 310fe50..79c10fc 100644
@@ -614,15 +614,19 @@ only go to the IOAPIC.  On ia64, a IOSAPIC is created.
 4.25 KVM_IRQ_LINE
 
 Capability: KVM_CAP_IRQCHIP
-Architectures: x86, ia64
+Architectures: x86, ia64, arm
 Type: vm ioctl
 Parameters: struct kvm_irq_level
 Returns: 0 on success, -1 on error
 
 Sets the level of a GSI input to the interrupt controller model in the kernel.
-Requires that an interrupt controller model has been previously created with
-KVM_CREATE_IRQCHIP.  Note that edge-triggered interrupts require the level
-to be set to 1 and then back to 0.
+On some architectures it is required that an interrupt controller model has
+been previously created with KVM_CREATE_IRQCHIP.  Note that edge-triggered
+interrupts require the level to be set to 1 and then back to 0.
+
+ARM uses two types of interrupt lines per CPU: IRQ and FIQ.  The value of the
+irq field should be (vcpu_index << 1) for IRQs and ((vcpu_index << 1) | 1) for
+FIQs. Level is used to raise/lower the line.
 
 struct kvm_irq_level {
 	union {
arch/arm/include/asm/kvm.h +9 -0
index c8466b7..38ff1d6 100644
@@ -20,6 +20,15 @@
 #include <asm/types.h>
 
 #define __KVM_HAVE_GUEST_DEBUG
+#define __KVM_HAVE_IRQ_LINE
+
+/*
+ * KVM_IRQ_LINE macros to set/read IRQ/FIQ for specific VCPU index.
+ */
+enum KVM_ARM_IRQ_LINE_TYPE {
+	KVM_ARM_IRQ_LINE = 0,
+	KVM_ARM_FIQ_LINE = 1,
+};
 
 /*
  * Modes used for short-hand mode determinition in the world-switch code and
arch/arm/include/asm/kvm_arm.h +1 -0
index 257242f..130526c 100644
@@ -63,6 +63,7 @@
  */
 #define HCR_GUEST_MASK (HCR_TSC | HCR_TWI | HCR_VM | HCR_BSU_IS | HCR_FB | \
 			HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | HCR_SWIO)
+#define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
 
 /* Hyp System Control Register (HSCTLR) bits */
 #define HSCTLR_TE	(1 << 30)
arch/arm/kvm/arm.c +61 -1

86 lines not on this page · raw message

include/linux/kvm.h +1 -0
index 2ce09aa..f978447 100644
@@ -111,6 +111,7 @@ struct kvm_irq_level {
 	 * ACPI gsi notion of irq.
 	 * For IA-64 (APIC model) IOAPIC0: irq 0-23; IOAPIC1: irq 24-47..
 	 * For X86 (standard AT mode) PIC0/1: irq 0-15. IOAPIC0: 0-23..
+	 * For ARM: IRQ: irq = (2*vcpu_index). FIQ: irq = (2*vcpu_indx + 1).
 	 */
 	union {
 		__u32 irq;
#27 lore raw

Re: [PATCH v8 10/15] ARM: KVM: Inject IRQs and FIQs from userspace

Quoted 46 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

Userspace can inject IRQs and FIQs through the KVM_IRQ_LINE VM ioctl. This ioctl is used since the sematics are in fact two lines that can be either raised or lowered on the VCPU - the IRQ and FIQ lines.

KVM needs to know which VCPU it must operate on and whether the FIQ or IRQ line is raised/lowered. Hence both pieces of information is packed in the kvm_irq_level->irq field. The irq fild value will be:
IRQ: vcpu_index << 1

  FIQ: (vcpu_index << 1) | 1

This is documented in Documentation/kvm/api.txt.

The effect of the ioctl is simply to simply raise/lower the corresponding irq_line field on the VCPU struct, which will cause the world-switch code to raise/lower virtual interrupts when running the guest on next switch. The wait_for_interrupt flag is also cleared for raised IRQs or FIQs causing an idle VCPU to become active again. CPUs in guest mode are kicked to make sure they refresh their interrupt status.

arch/arm/kvm/arm.c +24 -0
+static int kvm_arch_vm_ioctl_irq_line(struct kvm *kvm,
+				      struct kvm_irq_level *irq_level)
+{
+	int mask;
+	unsigned int vcpu_idx;
+	struct kvm_vcpu *vcpu;
+	unsigned long old, new, *ptr;
+
+	vcpu_idx = irq_level->irq >> 1;
+	if (vcpu_idx >= KVM_MAX_VCPUS)
+		return -EINVAL;
+
+	vcpu = kvm_get_vcpu(kvm, vcpu_idx);
+	if (!vcpu)
+		return -EINVAL;
+
+	if ((irq_level->irq & 1) == KVM_ARM_IRQ_LINE)
+		mask = HCR_VI;
+	else /* KVM_ARM_FIQ_LINE */
+		mask = HCR_VF;
+
+	trace_kvm_set_irq(irq_level->irq, irq_level->level, 0);
+
+	ptr = (unsigned long *)&vcpu->arch.irq_lines;

arch/arm/kvm/arm.c int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) · line 322

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+	do {
+		old = ACCESS_ONCE(*ptr);
+		if (irq_level->level)
+			new = old | mask;
+		else
+			new = old & ~mask;
+
+		if (new == old)
+			return 0; /* no change */
+	} while (cmpxchg(ptr, old, new) != old);

Isn't this a complicated

if (level)

       set_bit()
   else
       clear_bit()

?

arch/arm/kvm/arm.c int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) · line 329

Quoted 7 lines
arch/arm/kvm/arm.c +7 -0
+
+	/*
+	 * The vcpu irq_lines field was updated, wake up sleeping VCPUs and
+	 * trigger a world-switch round on the running physical CPU to set the
+	 * virtual IRQ/FIQ fields in the HCR appropriately.
+	 */
+	kvm_vcpu_kick(vcpu);

No need to wake when the line is asserted so you can make this conditional on level.

Quoted 17 lines
arch/arm/kvm/arm.c +9 -1
+
+	return 0;
+}
+
 long kvm_arch_vcpu_ioctl(struct file *filp,
 			 unsigned int ioctl, unsigned long arg)
 {
@@ -298,7 +345,20 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
 long kvm_arch_vm_ioctl(struct file *filp,
 		       unsigned int ioctl, unsigned long arg)
 {
-	return -EINVAL;
+	struct kvm *kvm = filp->private_data;
+	void __user *argp = (void __user *)arg;
+
+	switch (ioctl) {
+	case KVM_IRQ_LINE: {

arch/arm/kvm/arm.c int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) · line 362

Quoted 10 lines
arch/arm/kvm/arm.c +9 -0
+		struct kvm_irq_level irq_event;
+
+		if (copy_from_user(&irq_event, argp, sizeof irq_event))
+			return -EFAULT;
+		return kvm_arch_vm_ioctl_irq_line(kvm, &irq_event);
+	}
+	default:
+		return -EINVAL;
+	}
 }

Should be in common code guarded by the define introduced previously.

Signature · 1 line
error compiling committee.c: too many arguments to function
#28 lore raw

Re: [PATCH v8 10/15] ARM: KVM: Inject IRQs and FIQs from userspace

Quoted 61 lines · Avi Kivity
Quoted 46 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

Userspace can inject IRQs and FIQs through the KVM_IRQ_LINE VM ioctl. This ioctl is used since the sematics are in fact two lines that can be either raised or lowered on the VCPU - the IRQ and FIQ lines.

KVM needs to know which VCPU it must operate on and whether the FIQ or IRQ line is raised/lowered. Hence both pieces of information is packed in the kvm_irq_level->irq field. The irq fild value will be:
  IRQ: vcpu_index << 1
  FIQ: (vcpu_index << 1) | 1

This is documented in Documentation/kvm/api.txt.

The effect of the ioctl is simply to simply raise/lower the corresponding irq_line field on the VCPU struct, which will cause the world-switch code to raise/lower virtual interrupts when running the guest on next switch. The wait_for_interrupt flag is also cleared for raised IRQs or FIQs causing an idle VCPU to become active again. CPUs in guest mode are kicked to make sure they refresh their interrupt status.

(quoted diff) +24 -0
+static int kvm_arch_vm_ioctl_irq_line(struct kvm *kvm,
+                                   struct kvm_irq_level *irq_level)
+{
+     int mask;
+     unsigned int vcpu_idx;
+     struct kvm_vcpu *vcpu;
+     unsigned long old, new, *ptr;
+
+     vcpu_idx = irq_level->irq >> 1;
+     if (vcpu_idx >= KVM_MAX_VCPUS)
+             return -EINVAL;
+
+     vcpu = kvm_get_vcpu(kvm, vcpu_idx);
+     if (!vcpu)
+             return -EINVAL;
+
+     if ((irq_level->irq & 1) == KVM_ARM_IRQ_LINE)
+             mask = HCR_VI;
+     else /* KVM_ARM_FIQ_LINE */
+             mask = HCR_VF;
+
+     trace_kvm_set_irq(irq_level->irq, irq_level->level, 0);
+
+     ptr = (unsigned long *)&vcpu->arch.irq_lines;

Review

Quoted 10 lines
(quoted diff) +10 -0
+     do {
+             old = ACCESS_ONCE(*ptr);
+             if (irq_level->level)
+                     new = old | mask;
+             else
+                     new = old & ~mask;
+
+             if (new == old)
+                     return 0; /* no change */
+     } while (cmpxchg(ptr, old, new) != old);

Isn't this a complicated

Quoted 7 lines

  if (level)
      set_bit()
  else
      clear_bit()

?

we need to atomically know if we changed either the FIQ/IRQ fields and atomically update without locking. (I think you suggested this approach, in fact).

Quoted 11 lines
Quoted 7 lines

+
+     /*
+      * The vcpu irq_lines field was updated, wake up sleeping VCPUs and
+      * trigger a world-switch round on the running physical CPU to set the
+      * virtual IRQ/FIQ fields in the HCR appropriately.
+      */
+     kvm_vcpu_kick(vcpu);

No need to wake when the line is asserted so you can make this conditional on level.

we need to trigger a world switch to update the virtualized register from the actual running physical CPU if we changed any of the IRQ/FIQ fields. We return in the loop above if we didn't change anything.

Quoted 21 lines
Quoted 21 lines
(quoted diff) +4 -0
+
+     return 0;
+}
+

 long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) {

(quoted diff) +9 -1
@@ -298,7 +345,20 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
  long kvm_arch_vm_ioctl(struct file *filp,
                      unsigned int ioctl, unsigned long arg)
  {
-     return -EINVAL;
+     struct kvm *kvm = filp->private_data;
+     void __user *argp = (void __user *)arg;
+
+     switch (ioctl) {
+     case KVM_IRQ_LINE: {
+             struct kvm_irq_level irq_event;
+
+             if (copy_from_user(&irq_event, argp, sizeof irq_event))
+                     return -EFAULT;
Quoted 10 lines

Review int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) · line 362

Quoted 6 lines
(quoted diff) +5 -0
+             return kvm_arch_vm_ioctl_irq_line(kvm, &irq_event);
+     }
+     default:
+             return -EINVAL;
+     }
  }

Should be in common code guarded by the define introduced previously.

you mean like this: ?

arch/arm/kvm/arm.c +0 -0
index a9f209b..5bf2193 100644
@@ -535,8 +535,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu,

struct kvm_run *run)
return ret;
}

-static int kvm_arch_vm_ioctl_irq_line(struct kvm *kvm,
- struct kvm_irq_level *irq_level)
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
{
int mask;
unsigned int vcpu_idx;
@@ -596,20 +595,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
long kvm_arch_vm_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
- struct kvm *kvm = filp->private_data;
- void __user *argp = (void __user *)arg;
-
- switch (ioctl) {
- case KVM_IRQ_LINE: {
- struct kvm_irq_level irq_event;
-
- if (copy_from_user(&irq_event, argp, sizeof irq_event))
- return -EFAULT;
- return kvm_arch_vm_ioctl_irq_line(kvm, &irq_event);
- }
- default:
- return -EINVAL;
- }
+ return -EINVAL;
}

static void cpu_set_vector(void *vector)

arch/ia64/kvm/kvm-ia64.c +0 -0
index bd77cb5..122a4b2 100644
@@ -924,6 +924,16 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu

*vcpu, struct kvm_regs *regs)
return 0;
}

+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event) +{
+ if (!irqchip_in_kernel(kvm))
+ return -ENXIO;
+
+ irq_event->statusstatus = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
+ irq_event->irq, irq_event->level);
+ return 0;
+}
+
long kvm_arch_vm_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
@@ -963,29 +973,6 @@ long kvm_arch_vm_ioctl(struct file *filp,
goto out;
}
break;
- case KVM_IRQ_LINE_STATUS:
- case KVM_IRQ_LINE: {
- struct kvm_irq_level irq_event;
-
- r = -EFAULT;
- if (copy_from_user(&irq_event, argp, sizeof irq_event))
- goto out;
- r = -ENXIO;
- if (irqchip_in_kernel(kvm)) {
- __s32 status;
- status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
- irq_event.irq, irq_event.level);
- if (ioctl == KVM_IRQ_LINE_STATUS) {
- r = -EFAULT;
- irq_event.status = status;
- if (copy_to_user(argp, &irq_event,
- sizeof irq_event))
- goto out;
- }
- r = 0;
- }
- break;
- }
case KVM_GET_IRQCHIP: {
/* 0: PIC master, 1: PIC slave, 2: IOAPIC */ struct kvm_irqchip chip;

arch/x86/kvm/x86.c +10 -23
index a01a424..c9c4186 100644
@@ -3144,6 +3144,16 @@ out:
 	return r;
 }
 
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event)
+{
+	if (!irqchip_in_kernel(kvm))
+		return -ENXIO;
+
+	irq_event->status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
+					irq_event->irq, irq_event->level);
+	return 0;
+}
+
 long kvm_arch_vm_ioctl(struct file *filp,
 		       unsigned int ioctl, unsigned long arg)
 {
@@ -3250,29 +3260,6 @@ long kvm_arch_vm_ioctl(struct file *filp,
 	create_pit_unlock:
 		mutex_unlock(&kvm->slots_lock);
 		break;
-	case KVM_IRQ_LINE_STATUS:
-	case KVM_IRQ_LINE: {
-		struct kvm_irq_level irq_event;
-
-		r = -EFAULT;
-		if (copy_from_user(&irq_event, argp, sizeof irq_event))
-			goto out;
-		r = -ENXIO;
-		if (irqchip_in_kernel(kvm)) {
-			__s32 status;
-			status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
-					irq_event.irq, irq_event.level);
-			if (ioctl == KVM_IRQ_LINE_STATUS) {
-				r = -EFAULT;
-				irq_event.status = status;
-				if (copy_to_user(argp, &irq_event,
-							sizeof irq_event))
-					goto out;
-			}
-			r = 0;
-		}
-		break;
-	}
 	case KVM_GET_IRQCHIP: {
 		/* 0: PIC master, 1: PIC slave, 2: IOAPIC */
 		struct kvm_irqchip *chip;
include/linux/kvm_host.h +1 -0
index e3c86f8..96aa7fb 100644
@@ -494,6 +494,7 @@ int kvm_vm_ioctl_set_memory_region(struct kvm *kvm,
 				   struct
 				   kvm_userspace_memory_region *mem,
 				   int user_alloc);
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level);
 long kvm_arch_vm_ioctl(struct file *filp,
 		       unsigned int ioctl, unsigned long arg);
 
virt/kvm/kvm_main.c +19 -0
index 636bd08..1d33877 100644
@@ -2093,6 +2093,25 @@ static long kvm_vm_ioctl(struct file *filp,
 		break;
 	}
 #endif
+#ifdef __KVM_HAVE_IRQ_LINE
+	case KVM_IRQ_LINE_STATUS:
+	case KVM_IRQ_LINE: {
+		struct kvm_irq_level irq_event;
+
+		r = -EFAULT;
+		if (copy_from_user(&irq_event, argp, sizeof irq_event))
+			goto out;
+
+		r = kvm_vm_ioctl_irq_line(kvm, &irq_event);
+
+		if (ioctl == KVM_IRQ_LINE_STATUS) {
+			if (copy_to_user(argp, &irq_event, sizeof irq_event))
+				r = -EFAULT;
+		}
+
+		break;
+	}
+#endif
 	default:
 		r = kvm_arch_vm_ioctl(filp, ioctl, arg);
 		if (r == -ENOTTY)

---

Thanks,
Christoffer

#29 lore raw

Re: [PATCH v8 10/15] ARM: KVM: Inject IRQs and FIQs from userspace

Quoted 67 lines · Christoffer Dall
Quoted 66 lines · Avi Kivity
Quoted 46 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

Userspace can inject IRQs and FIQs through the KVM_IRQ_LINE VM ioctl. This ioctl is used since the sematics are in fact two lines that can be either raised or lowered on the VCPU - the IRQ and FIQ lines.

KVM needs to know which VCPU it must operate on and whether the FIQ or IRQ line is raised/lowered. Hence both pieces of information is packed in the kvm_irq_level->irq field. The irq fild value will be:
IRQ: vcpu_index << 1

  FIQ: (vcpu_index << 1) | 1

This is documented in Documentation/kvm/api.txt.

The effect of the ioctl is simply to simply raise/lower the corresponding irq_line field on the VCPU struct, which will cause the world-switch code to raise/lower virtual interrupts when running the guest on next switch. The wait_for_interrupt flag is also cleared for raised IRQs or FIQs causing an idle VCPU to become active again. CPUs in guest mode are kicked to make sure they refresh their interrupt status.

(quoted diff) +24 -0
+static int kvm_arch_vm_ioctl_irq_line(struct kvm *kvm,
+                                   struct kvm_irq_level *irq_level)
+{
+     int mask;
+     unsigned int vcpu_idx;
+     struct kvm_vcpu *vcpu;
+     unsigned long old, new, *ptr;
+
+     vcpu_idx = irq_level->irq >> 1;
+     if (vcpu_idx >= KVM_MAX_VCPUS)
+             return -EINVAL;
+
+     vcpu = kvm_get_vcpu(kvm, vcpu_idx);
+     if (!vcpu)
+             return -EINVAL;
+
+     if ((irq_level->irq & 1) == KVM_ARM_IRQ_LINE)
+             mask = HCR_VI;
+     else /* KVM_ARM_FIQ_LINE */
+             mask = HCR_VF;
+
+     trace_kvm_set_irq(irq_level->irq, irq_level->level, 0);
+
+     ptr = (unsigned long *)&vcpu->arch.irq_lines;

Review

Quoted 10 lines
(quoted diff) +10 -0
+     do {
+             old = ACCESS_ONCE(*ptr);
+             if (irq_level->level)
+                     new = old | mask;
+             else
+                     new = old & ~mask;
+
+             if (new == old)
+                     return 0; /* no change */
+     } while (cmpxchg(ptr, old, new) != old);

Isn't this a complicated

if (level)

      set_bit()
  else
      clear_bit()
Quoted 5 lines
Quoted 2 lines

?

we need to atomically know if we changed either the FIQ/IRQ fields and atomically update without locking.

So use test_and_set_bit()/test_and_clear_bit().

Quoted 2 lines

(I think you suggested this
approach, in fact).

I think so too, but it only makes sense if you need to consider both fields simultaneously (which you don't here). For example, if IRQ was asserted while FIQ was already raised, you don't need to kick. But that's not the case according to the below.

Quoted 9 lines
Quoted 9 lines
Quoted 8 lines
(quoted diff) +7 -0
+
+     /*
+      * The vcpu irq_lines field was updated, wake up sleeping VCPUs and
+      * trigger a world-switch round on the running physical CPU to set the
+      * virtual IRQ/FIQ fields in the HCR appropriately.
+      */
+     kvm_vcpu_kick(vcpu);
Quoted 7 lines
Quoted 3 lines

No need to wake when the line is asserted so you can make this conditional on level.

we need to trigger a world switch to update the virtualized register from the actual running physical CPU if we changed any of the IRQ/FIQ fields. We return in the loop above if we didn't change anything.

Okay.

Quoted 209 lines
Quoted 22 lines
Quoted 22 lines
arch/x86/kvm/x86.c +13 -1
+
+     return 0;
+}
+
 long kvm_arch_vcpu_ioctl(struct file *filp,
                       unsigned int ioctl, unsigned long arg)
 {
@@ -298,7 +345,20 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
 long kvm_arch_vm_ioctl(struct file *filp,
                     unsigned int ioctl, unsigned long arg)
 {
-     return -EINVAL;
+     struct kvm *kvm = filp->private_data;
+     void __user *argp = (void __user *)arg;
+
+     switch (ioctl) {
+     case KVM_IRQ_LINE: {
+             struct kvm_irq_level irq_event;
+
+             if (copy_from_user(&irq_event, argp, sizeof irq_event))
+                     return -EFAULT;
Quoted 10 lines

Review int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) · line 362

Quoted 6 lines
(quoted diff) +5 -0
+             return kvm_arch_vm_ioctl_irq_line(kvm, &irq_event);
+     }
+     default:
+             return -EINVAL;
+     }
 }

Should be in common code guarded by the define introduced previously.

you mean like this: ?

arch/arm/kvm/arm.c +1 -2
index a9f209b..5bf2193 100644
@@ -535,8 +535,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu,
 struct kvm_run *run)
 	return ret;
 }
 
-static int kvm_arch_vm_ioctl_irq_line(struct kvm *kvm,
-				      struct kvm_irq_level *irq_level)
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
 {
 	int mask;
arch/arm/kvm/arm.c +1 -14
 	unsigned int vcpu_idx;
@@ -596,20 +595,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
 struct kvm_dirty_log *log)
 long kvm_arch_vm_ioctl(struct file *filp,
 		       unsigned int ioctl, unsigned long arg)
 {
-	struct kvm *kvm = filp->private_data;
-	void __user *argp = (void __user *)arg;
-
-	switch (ioctl) {
-	case KVM_IRQ_LINE: {
-		struct kvm_irq_level irq_event;
-
-		if (copy_from_user(&irq_event, argp, sizeof irq_event))
-			return -EFAULT;
-		return kvm_arch_vm_ioctl_irq_line(kvm, &irq_event);
-	}
-	default:
-		return -EINVAL;
-	}
+	return -EINVAL;
 }
 

static void cpu_set_vector(void *vector)

arch/ia64/kvm/kvm-ia64.c +10 -0
index bd77cb5..122a4b2 100644
@@ -924,6 +924,16 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu
 *vcpu, struct kvm_regs *regs)
 	return 0;
 }
 
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event)
+{
+	if (!irqchip_in_kernel(kvm))
+		return -ENXIO;
+
+	irq_event->statusstatus = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
+					      irq_event->irq, irq_event->level);
+	return 0;
+}
+
 long kvm_arch_vm_ioctl(struct file *filp,
 		unsigned int ioctl, unsigned long arg)

{

arch/ia64/kvm/kvm-ia64.c +0 -23
@@ -963,29 +973,6 @@ long kvm_arch_vm_ioctl(struct file *filp,
 			goto out;
 		}
 		break;
-	case KVM_IRQ_LINE_STATUS:
-	case KVM_IRQ_LINE: {
-		struct kvm_irq_level irq_event;
-
-		r = -EFAULT;
-		if (copy_from_user(&irq_event, argp, sizeof irq_event))
-			goto out;
-		r = -ENXIO;
-		if (irqchip_in_kernel(kvm)) {
-			__s32 status;
-			status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
-				    irq_event.irq, irq_event.level);
-			if (ioctl == KVM_IRQ_LINE_STATUS) {
-				r = -EFAULT;
-				irq_event.status = status;
-				if (copy_to_user(argp, &irq_event,
-							sizeof irq_event))
-					goto out;
-			}
-			r = 0;
-		}
-		break;
-		}
 	case KVM_GET_IRQCHIP: {
 		/* 0: PIC master, 1: PIC slave, 2: IOAPIC */
 		struct kvm_irqchip chip;
arch/x86/kvm/x86.c +10 -23
index a01a424..c9c4186 100644
@@ -3144,6 +3144,16 @@ out:
 	return r;
 }
 
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event)
+{
+	if (!irqchip_in_kernel(kvm))
+		return -ENXIO;
+
+	irq_event->status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
+					irq_event->irq, irq_event->level);
+	return 0;
+}
+
 long kvm_arch_vm_ioctl(struct file *filp,
 		       unsigned int ioctl, unsigned long arg)
 {
@@ -3250,29 +3260,6 @@ long kvm_arch_vm_ioctl(struct file *filp,
 	create_pit_unlock:
 		mutex_unlock(&kvm->slots_lock);
 		break;
-	case KVM_IRQ_LINE_STATUS:
-	case KVM_IRQ_LINE: {
-		struct kvm_irq_level irq_event;
-
-		r = -EFAULT;
-		if (copy_from_user(&irq_event, argp, sizeof irq_event))
-			goto out;
-		r = -ENXIO;
-		if (irqchip_in_kernel(kvm)) {
-			__s32 status;
-			status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
-					irq_event.irq, irq_event.level);
-			if (ioctl == KVM_IRQ_LINE_STATUS) {
-				r = -EFAULT;
-				irq_event.status = status;
-				if (copy_to_user(argp, &irq_event,
-							sizeof irq_event))
-					goto out;
-			}
-			r = 0;
-		}
-		break;
-	}
 	case KVM_GET_IRQCHIP: {
 		/* 0: PIC master, 1: PIC slave, 2: IOAPIC */
 		struct kvm_irqchip *chip;
include/linux/kvm_host.h +1 -0
index e3c86f8..96aa7fb 100644
@@ -494,6 +494,7 @@ int kvm_vm_ioctl_set_memory_region(struct kvm *kvm,
 				   struct
 				   kvm_userspace_memory_region *mem,
 				   int user_alloc);
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level);
 long kvm_arch_vm_ioctl(struct file *filp,
 		       unsigned int ioctl, unsigned long arg);
 
virt/kvm/kvm_main.c +12 -0
index 636bd08..1d33877 100644
@@ -2093,6 +2093,25 @@ static long kvm_vm_ioctl(struct file *filp,
 		break;
 	}
 #endif
+#ifdef __KVM_HAVE_IRQ_LINE
+	case KVM_IRQ_LINE_STATUS:
+	case KVM_IRQ_LINE: {
+		struct kvm_irq_level irq_event;
+
+		r = -EFAULT;
+		if (copy_from_user(&irq_event, argp, sizeof irq_event))
+			goto out;
+
+		r = kvm_vm_ioctl_irq_line(kvm, &irq_event);
+
+		if (ioctl == KVM_IRQ_LINE_STATUS) {

virt/kvm/kvm_main.c static long kvm_vm_ioctl(struct file *filp, · line 2117

Quoted 10 lines
virt/kvm/kvm_main.c +7 -0
+			if (copy_to_user(argp, &irq_event, sizeof irq_event))
+				r = -EFAULT;
+		}
+
+		break;
+	}
+#endif
 	default:
 		r = kvm_arch_vm_ioctl(filp, ioctl, arg);
 		if (r == -ENOTTY)

Yup. Note it brings in KVM_IRQ_LINE_STATUS for ARM. I think it makes sense if you have guests that do timekeeping by counting interrupts (as opposed to reading a wall clock register).

Signature · 1 line
error compiling committee.c: too many arguments to function
#30 lore raw

Re: [PATCH v8 10/15] ARM: KVM: Inject IRQs and FIQs from userspace

Quoted 76 lines · Avi Kivity
Quoted 67 lines · Christoffer Dall
Quoted 66 lines · Avi Kivity
Quoted 46 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

Userspace can inject IRQs and FIQs through the KVM_IRQ_LINE VM ioctl. This ioctl is used since the sematics are in fact two lines that can be either raised or lowered on the VCPU - the IRQ and FIQ lines.

KVM needs to know which VCPU it must operate on and whether the FIQ or IRQ line is raised/lowered. Hence both pieces of information is packed in the kvm_irq_level->irq field. The irq fild value will be:
  IRQ: vcpu_index << 1
  FIQ: (vcpu_index << 1) | 1

This is documented in Documentation/kvm/api.txt.

The effect of the ioctl is simply to simply raise/lower the corresponding irq_line field on the VCPU struct, which will cause the world-switch code to raise/lower virtual interrupts when running the guest on next switch. The wait_for_interrupt flag is also cleared for raised IRQs or FIQs causing an idle VCPU to become active again. CPUs in guest mode are kicked to make sure they refresh their interrupt status.

(quoted diff) +24 -0
+static int kvm_arch_vm_ioctl_irq_line(struct kvm *kvm,
+                                   struct kvm_irq_level *irq_level)
+{
+     int mask;
+     unsigned int vcpu_idx;
+     struct kvm_vcpu *vcpu;
+     unsigned long old, new, *ptr;
+
+     vcpu_idx = irq_level->irq >> 1;
+     if (vcpu_idx >= KVM_MAX_VCPUS)
+             return -EINVAL;
+
+     vcpu = kvm_get_vcpu(kvm, vcpu_idx);
+     if (!vcpu)
+             return -EINVAL;
+
+     if ((irq_level->irq & 1) == KVM_ARM_IRQ_LINE)
+             mask = HCR_VI;
+     else /* KVM_ARM_FIQ_LINE */
+             mask = HCR_VF;
+
+     trace_kvm_set_irq(irq_level->irq, irq_level->level, 0);
+
+     ptr = (unsigned long *)&vcpu->arch.irq_lines;

Review

Quoted 10 lines
(quoted diff) +10 -0
+     do {
+             old = ACCESS_ONCE(*ptr);
+             if (irq_level->level)
+                     new = old | mask;
+             else
+                     new = old & ~mask;
+
+             if (new == old)
+                     return 0; /* no change */
+     } while (cmpxchg(ptr, old, new) != old);

Isn't this a complicated

  if (level)
      set_bit()
  else
      clear_bit()

Quoted 5 lines
Quoted 2 lines

?

we need to atomically know if we changed either the FIQ/IRQ fields and atomically update without locking.

So use test_and_set_bit()/test_and_clear_bit().

Quoted 8 lines
Quoted 2 lines

(I think you suggested this
approach, in fact).

I think so too, but it only makes sense if you need to consider both fields simultaneously (which you don't here).  For example, if IRQ was asserted while FIQ was already raised, you don't need to kick.  But that's not the case according to the below.

ok, fair enough. I simplified it a bit.

Quoted 248 lines
Quoted 9 lines
Quoted 9 lines
Quoted 8 lines

+
+     /*
+      * The vcpu irq_lines field was updated, wake up sleeping VCPUs and
+      * trigger a world-switch round on the running physical CPU to set the
+      * virtual IRQ/FIQ fields in the HCR appropriately.
+      */
+     kvm_vcpu_kick(vcpu);

Quoted 7 lines
Quoted 3 lines

No need to wake when the line is asserted so you can make this conditional on level.

we need to trigger a world switch to update the virtualized register from the actual running physical CPU if we changed any of the IRQ/FIQ fields. We return in the loop above if we didn't change anything.

Okay.

Quoted 216 lines
Quoted 22 lines
Quoted 22 lines
arch/x86/kvm/x86.c +4 -0
+
+     return 0;
+}
+

 long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) {

(quoted diff) +9 -1
@@ -298,7 +345,20 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
  long kvm_arch_vm_ioctl(struct file *filp,
                      unsigned int ioctl, unsigned long arg)
  {
-     return -EINVAL;
+     struct kvm *kvm = filp->private_data;
+     void __user *argp = (void __user *)arg;
+
+     switch (ioctl) {
+     case KVM_IRQ_LINE: {
+             struct kvm_irq_level irq_event;
+
+             if (copy_from_user(&irq_event, argp, sizeof irq_event))
+                     return -EFAULT;
Quoted 10 lines

Review int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) · line 362

Quoted 6 lines
(quoted diff) +5 -0
+             return kvm_arch_vm_ioctl_irq_line(kvm, &irq_event);
+     }
+     default:
+             return -EINVAL;
+     }
  }

Should be in common code guarded by the define introduced previously.

you mean like this: ?

arch/arm/kvm/arm.c +1 -2
index a9f209b..5bf2193 100644
@@ -535,8 +535,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu,
 struct kvm_run *run)
       return ret;
  }
 
-static int kvm_arch_vm_ioctl_irq_line(struct kvm *kvm,
-                                   struct kvm_irq_level *irq_level)
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
  {
       int mask;

      unsigned int vcpu_idx;

arch/arm/kvm/arm.c +1 -14
@@ -596,20 +595,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
 struct kvm_dirty_log *log)
  long kvm_arch_vm_ioctl(struct file *filp,
                      unsigned int ioctl, unsigned long arg)
  {
-     struct kvm *kvm = filp->private_data;
-     void __user *argp = (void __user *)arg;
-
-     switch (ioctl) {
-     case KVM_IRQ_LINE: {
-             struct kvm_irq_level irq_event;
-
-             if (copy_from_user(&irq_event, argp, sizeof irq_event))
-                     return -EFAULT;
-             return kvm_arch_vm_ioctl_irq_line(kvm, &irq_event);
-     }
-     default:
-             return -EINVAL;
-     }
+     return -EINVAL;
  }
 

 static void cpu_set_vector(void *vector)

arch/ia64/kvm/kvm-ia64.c +10 -0
index bd77cb5..122a4b2 100644
@@ -924,6 +924,16 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu
 *vcpu, struct kvm_regs *regs)
       return 0;
  }
 
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event)
+{
+     if (!irqchip_in_kernel(kvm))
+             return -ENXIO;
+
+     irq_event->statusstatus = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
+                                           irq_event->irq, irq_event->level);
+     return 0;
+}
+
  long kvm_arch_vm_ioctl(struct file *filp,
               unsigned int ioctl, unsigned long arg)

 {

arch/ia64/kvm/kvm-ia64.c +0 -23
@@ -963,29 +973,6 @@ long kvm_arch_vm_ioctl(struct file *filp,
                       goto out;
               }
               break;
-     case KVM_IRQ_LINE_STATUS:
-     case KVM_IRQ_LINE: {
-             struct kvm_irq_level irq_event;
-
-             r = -EFAULT;
-             if (copy_from_user(&irq_event, argp, sizeof irq_event))
-                     goto out;
-             r = -ENXIO;
-             if (irqchip_in_kernel(kvm)) {
-                     __s32 status;
-                     status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
-                                 irq_event.irq, irq_event.level);
-                     if (ioctl == KVM_IRQ_LINE_STATUS) {
-                             r = -EFAULT;
-                             irq_event.status = status;
-                             if (copy_to_user(argp, &irq_event,
-                                                     sizeof irq_event))
-                                     goto out;
-                     }
-                     r = 0;
-             }
-             break;
-             }
       case KVM_GET_IRQCHIP: {
               /* 0: PIC master, 1: PIC slave, 2: IOAPIC */
               struct kvm_irqchip chip;
arch/x86/kvm/x86.c +10 -0
index a01a424..c9c4186 100644
@@ -3144,6 +3144,16 @@ out:
       return r;
  }
 
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event)
+{
+     if (!irqchip_in_kernel(kvm))
+             return -ENXIO;
+
+     irq_event->status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
+                                     irq_event->irq, irq_event->level);
+     return 0;
+}
+
  long kvm_arch_vm_ioctl(struct file *filp,
                      unsigned int ioctl, unsigned long arg)
  {
@@ -3250,29 +3260,6 @@ long kvm_arch_vm_ioctl(struct file *filp,

      create_pit_unlock:
              mutex_unlock(&kvm->slots_lock);
              break;
-     case KVM_IRQ_LINE_STATUS:
-     case KVM_IRQ_LINE: {
-             struct kvm_irq_level irq_event;
-
-             r = -EFAULT;
-             if (copy_from_user(&irq_event, argp, sizeof irq_event))
-                     goto out;
-             r = -ENXIO;
-             if (irqchip_in_kernel(kvm)) {
-                     __s32 status;
-                     status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
-                                     irq_event.irq, irq_event.level);
-                     if (ioctl == KVM_IRQ_LINE_STATUS) {
-                             r = -EFAULT;
-                             irq_event.status = status;
-                             if (copy_to_user(argp, &irq_event,
-                                                     sizeof irq_event))
-                                     goto out;
-                     }
-                     r = 0;
-             }
-             break;
-     }
      case KVM_GET_IRQCHIP: {
              /* 0: PIC master, 1: PIC slave, 2: IOAPIC */ struct kvm_irqchip *chip;

include/linux/kvm_host.h +0 -0
index e3c86f8..96aa7fb 100644
@@ -494,6 +494,7 @@ int kvm_vm_ioctl_set_memory_region(struct kvm *kvm,

                                 struct kvm_userspace_memory_region *mem, int user_alloc);
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level); long kvm_arch_vm_ioctl(struct file *filp,
                     unsigned int ioctl, unsigned long arg);

virt/kvm/kvm_main.c +19 -0
index 636bd08..1d33877 100644
@@ -2093,6 +2093,25 @@ static long kvm_vm_ioctl(struct file *filp,
               break;
       }
  #endif
+#ifdef __KVM_HAVE_IRQ_LINE
+     case KVM_IRQ_LINE_STATUS:
+     case KVM_IRQ_LINE: {
+             struct kvm_irq_level irq_event;
+
+             r = -EFAULT;
+             if (copy_from_user(&irq_event, argp, sizeof irq_event))
+                     goto out;
+
+             r = kvm_vm_ioctl_irq_line(kvm, &irq_event);
+
+             if (ioctl == KVM_IRQ_LINE_STATUS) {
+                     if (copy_to_user(argp, &irq_event, sizeof irq_event))
+                             r = -EFAULT;
+             }
+
+             break;
+     }
+#endif
Quoted 3 lines

      default:
              r = kvm_arch_vm_ioctl(filp, ioctl, arg); if (r == -ENOTTY)

Yup.  Note it brings in KVM_IRQ_LINE_STATUS for ARM.  I think it makes sense if you have guests that do timekeeping by counting interrupts (as opposed to reading a wall clock register).

--
error compiling committee.c: too many arguments to function

#31 lore raw

PATCH v8 11/15 ARM: KVM: World-switch implementation

In reply to Christoffer Dall

Provides complete world-switch implementation to switch to other guests running in non-secure modes. Includes Hyp exception handlers that capture necessary exception information and stores the information on the VCPU and KVM structures.

Switching to Hyp mode is done through a simple HVC instructions. The exception vector code will check that the HVC comes from VMID==0 and if so will store the necessary state on the Hyp stack, which will look like this (growing downwards, see the hyp_hvc handler):
...
Hyp_Sp + 4: spsr (Host-SVC cpsr)
Hyp_Sp : lr_usr

When returning from Hyp mode to SVC mode, another HVC instruction is executed from Hyp mode, which is taken in the Hyp_Svc handler. The Hyp stack pointer should be where it was left from the above initial call, since the values on the stack will be used to restore state (see hyp_svc).

Otherwise, the world-switch is pretty straight-forward. All state that can be modified by the guest is first backed up on the Hyp stack and the VCPU values is loaded onto the hardware. State, which is not loaded, but theoretically modifiable by the guest is protected through the virtualiation features to generate a trap and cause software emulation. Upon guest returns, all state is restored from hardware onto the VCPU struct and the original state is restored from the Hyp-stack onto the hardware.

One controversy may be the back-door call to __irq_svc (the host kernel's own physical IRQ handler) which is called when a physical IRQ exception is taken in Hyp mode while running in the guest.

SMP support using the VMPIDR calculated on the basis of the host MPIDR and overriding the low bits with KVM vcpu_id contributed by Marc Zyngier.

Reuse of VMIDs has been implemented by Antonios Motakis and adapated from a separate patch into the appropriate patches introducing the functionality. Note that the VMIDs are stored per VM as required by the ARM architecture reference manual.

---
 arch/arm/include/asm/kvm_arm.h  |   26 ++
 arch/arm/include/asm/kvm_host.h |    3 
 arch/arm/kernel/armksyms.c      |    7 +
 arch/arm/kernel/asm-offsets.c   |   35 +++
 arch/arm/kernel/entry-armv.S    |    1 
 arch/arm/kvm/arm.c              |  136 ++++++++++-
 arch/arm/kvm/interrupts.S       |  499 +++++++++++++++++++++++++++++++++++++++
 7 files changed, 703 insertions(+), 4 deletions(-)
arch/arm/include/asm/kvm_arm.h +26 -0
index 130526c..d76e8eb37 100644
@@ -114,5 +114,31 @@
 #define VTTBR_X		(5 - VTCR_GUEST_T0SZ)
 #endif
 
+/* Hyp Syndrome Register (HSR) bits */
+#define HSR_EC_SHIFT	(26)
+#define HSR_EC		(0x3fU << HSR_EC_SHIFT)
+#define HSR_IL		(1U << 25)
+#define HSR_ISS		(HSR_IL - 1)
+#define HSR_ISV_SHIFT	(24)
+#define HSR_ISV		(1U << HSR_ISV_SHIFT)
+
+#define HSR_EC_UNKNOWN	(0x00)
+#define HSR_EC_WFI	(0x01)
+#define HSR_EC_CP15_32	(0x03)
+#define HSR_EC_CP15_64	(0x04)
+#define HSR_EC_CP14_MR	(0x05)
+#define HSR_EC_CP14_LS	(0x06)
+#define HSR_EC_CP_0_13	(0x07)
+#define HSR_EC_CP10_ID	(0x08)
+#define HSR_EC_JAZELLE	(0x09)
+#define HSR_EC_BXJ	(0x0A)
+#define HSR_EC_CP14_64	(0x0C)
+#define HSR_EC_SVC_HYP	(0x11)
+#define HSR_EC_HVC	(0x12)
+#define HSR_EC_SMC	(0x13)
+#define HSR_EC_IABT	(0x20)
+#define HSR_EC_IABT_HYP	(0x21)
+#define HSR_EC_DABT	(0x24)
+#define HSR_EC_DABT_HYP	(0x25)
 
 #endif /* __KVM_ARM_H__ */
arch/arm/include/asm/kvm_host.h +3 -0
index 2d611df..ed4144b 100644
@@ -96,6 +96,9 @@ struct kvm_vcpu_arch {
 	u32 hdfar;		/* Hyp Data Fault Address Register */
 	u32 hifar;		/* Hyp Inst. Fault Address Register */
 	u32 hpfar;		/* Hyp IPA Fault Address Register */
+	u64 pc_ipa;		/* IPA for the current PC (VA to PA result) */
+	u64 pc_ipa2;		/* same as above, but for non-aligned wide thumb
+				   instructions */
 
 	/* IO related fields */
 	u32 mmio_rd;
arch/arm/kernel/armksyms.c +7 -0
index b57c75e..38d3a12 100644
@@ -48,6 +48,13 @@ extern void __aeabi_ulcmp(void);
 
 extern void fpundefinstr(void);
 
+#ifdef CONFIG_KVM_ARM_HOST
+/* This is needed for KVM */
+extern void __irq_svc(void);
+
+EXPORT_SYMBOL_GPL(__irq_svc);
+#endif
+
 	/* platform dependent support */
 EXPORT_SYMBOL(__udelay);
 EXPORT_SYMBOL(__const_udelay);
arch/arm/kernel/asm-offsets.c +35 -0
index 1429d89..6ef0cdc 100644
@@ -13,6 +13,7 @@
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/dma-mapping.h>
+#include <linux/kvm_host.h>
 #include <asm/cacheflush.h>
 #include <asm/glue-df.h>
 #include <asm/glue-pf.h>
@@ -144,5 +145,39 @@ int main(void)
   DEFINE(DMA_BIDIRECTIONAL,	DMA_BIDIRECTIONAL);
   DEFINE(DMA_TO_DEVICE,		DMA_TO_DEVICE);
   DEFINE(DMA_FROM_DEVICE,	DMA_FROM_DEVICE);
+#ifdef CONFIG_KVM_ARM_HOST
+  DEFINE(VCPU_KVM,		offsetof(struct kvm_vcpu, kvm));
+  DEFINE(VCPU_MIDR,		offsetof(struct kvm_vcpu, arch.cp15[c0_MIDR]));
+  DEFINE(VCPU_MPIDR,		offsetof(struct kvm_vcpu, arch.cp15[c0_MPIDR]));
+  DEFINE(VCPU_SCTLR,		offsetof(struct kvm_vcpu, arch.cp15[c1_SCTLR]));
+  DEFINE(VCPU_CPACR,		offsetof(struct kvm_vcpu, arch.cp15[c1_CPACR]));
+  DEFINE(VCPU_TTBR0,		offsetof(struct kvm_vcpu, arch.cp15[c2_TTBR0]));
+  DEFINE(VCPU_TTBR1,		offsetof(struct kvm_vcpu, arch.cp15[c2_TTBR1]));
+  DEFINE(VCPU_TTBCR,		offsetof(struct kvm_vcpu, arch.cp15[c2_TTBCR]));
+  DEFINE(VCPU_DACR,		offsetof(struct kvm_vcpu, arch.cp15[c3_DACR]));
+  DEFINE(VCPU_PRRR,		offsetof(struct kvm_vcpu, arch.cp15[c10_PRRR]));
+  DEFINE(VCPU_NMRR,		offsetof(struct kvm_vcpu, arch.cp15[c10_NMRR]));
+  DEFINE(VCPU_CID,		offsetof(struct kvm_vcpu, arch.cp15[c13_CID]));
+  DEFINE(VCPU_TID_URW,		offsetof(struct kvm_vcpu, arch.cp15[c13_TID_URW]));
+  DEFINE(VCPU_TID_URO,		offsetof(struct kvm_vcpu, arch.cp15[c13_TID_URO]));
+  DEFINE(VCPU_TID_PRIV,		offsetof(struct kvm_vcpu, arch.cp15[c13_TID_PRIV]));
+  DEFINE(VCPU_REGS,		offsetof(struct kvm_vcpu, arch.regs));
+  DEFINE(VCPU_USR_REGS,		offsetof(struct kvm_vcpu, arch.regs.usr_regs));
+  DEFINE(VCPU_SVC_REGS,		offsetof(struct kvm_vcpu, arch.regs.svc_regs));
+  DEFINE(VCPU_ABT_REGS,		offsetof(struct kvm_vcpu, arch.regs.abt_regs));
+  DEFINE(VCPU_UND_REGS,		offsetof(struct kvm_vcpu, arch.regs.und_regs));
+  DEFINE(VCPU_IRQ_REGS,		offsetof(struct kvm_vcpu, arch.regs.irq_regs));
+  DEFINE(VCPU_FIQ_REGS,		offsetof(struct kvm_vcpu, arch.regs.fiq_regs));
+  DEFINE(VCPU_PC,		offsetof(struct kvm_vcpu, arch.regs.pc));
+  DEFINE(VCPU_CPSR,		offsetof(struct kvm_vcpu, arch.regs.cpsr));
+  DEFINE(VCPU_IRQ_LINES,	offsetof(struct kvm_vcpu, arch.irq_lines));
+  DEFINE(VCPU_HSR,		offsetof(struct kvm_vcpu, arch.hsr));
+  DEFINE(VCPU_HDFAR,		offsetof(struct kvm_vcpu, arch.hdfar));
+  DEFINE(VCPU_HIFAR,		offsetof(struct kvm_vcpu, arch.hifar));
+  DEFINE(VCPU_HPFAR,		offsetof(struct kvm_vcpu, arch.hpfar));
+  DEFINE(VCPU_PC_IPA,		offsetof(struct kvm_vcpu, arch.pc_ipa));
+  DEFINE(VCPU_PC_IPA2,		offsetof(struct kvm_vcpu, arch.pc_ipa2));
+  DEFINE(KVM_VTTBR,		offsetof(struct kvm, arch.vttbr));
+#endif
   return 0; 
 }
arch/arm/kernel/entry-armv.S +1 -0
index 437f0c4..db029bb 100644
@@ -209,6 +209,7 @@ __dabt_svc:
 ENDPROC(__dabt_svc)
 
 	.align	5
+	.globl __irq_svc
 __irq_svc:
 	svc_entry
 	irq_handler
arch/arm/kvm/arm.c +134 -2

158 lines not on this page · raw message

arch/arm/kvm/interrupts.S +497 -2

527 lines not on this page · raw message

#32 lore raw

Re: [PATCH v8 11/15] ARM: KVM: World-switch implementation

Quoted 44 lines · Christoffer Dall

Provides complete world-switch implementation to switch to other guests running in non-secure modes. Includes Hyp exception handlers that capture necessary exception information and stores the information on the VCPU and KVM structures.

Switching to Hyp mode is done through a simple HVC instructions. The exception vector code will check that the HVC comes from VMID==0 and if so will store the necessary state on the Hyp stack, which will look like this (growing downwards, see the hyp_hvc handler):
...
Hyp_Sp + 4: spsr (Host-SVC cpsr)
Hyp_Sp : lr_usr

When returning from Hyp mode to SVC mode, another HVC instruction is executed from Hyp mode, which is taken in the Hyp_Svc handler. The Hyp stack pointer should be where it was left from the above initial call, since the values on the stack will be used to restore state (see hyp_svc).

Otherwise, the world-switch is pretty straight-forward. All state that can be modified by the guest is first backed up on the Hyp stack and the VCPU values is loaded onto the hardware. State, which is not loaded, but theoretically modifiable by the guest is protected through the virtualiation features to generate a trap and cause software emulation. Upon guest returns, all state is restored from hardware onto the VCPU struct and the original state is restored from the Hyp-stack onto the hardware.

One controversy may be the back-door call to __irq_svc (the host kernel's own physical IRQ handler) which is called when a physical IRQ exception is taken in Hyp mode while running in the guest.

SMP support using the VMPIDR calculated on the basis of the host MPIDR and overriding the low bits with KVM vcpu_id contributed by Marc Zyngier.

Reuse of VMIDs has been implemented by Antonios Motakis and adapated from a separate patch into the appropriate patches introducing the functionality. Note that the VMIDs are stored per VM as required by the ARM architecture reference manual.

arch/arm/kvm/arm.c +4 -0
+
+/**
+ * update_vttbr - Update the VTTBR with a valid VMID before the guest runs
+ * @kvm	The guest that we are about to run

arch/arm/kvm/arm.c int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) · line 320

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+ *
+ * Called from kvm_arch_vcpu_ioctl_run before entering the guest to ensure the
+ * VM has a valid VMID, otherwise assigns a new one and flushes corresponding
+ * caches and TLBs.
+ */
+static void update_vttbr(struct kvm *kvm)
+{
+	phys_addr_t pgd_phys;
+
+	spin_lock(&kvm_vmid_lock);

Premature, but this is sad. I suggest you split vmid generation from next available vmid. This allows you to make the generation counter atomic so it may be read outside the lock.

You can do

    if (likely(kvm->arch.vmd_gen) == atomic_read(&kvm_vmid_gen)))
           return;

    spin_lock(...

arch/arm/kvm/arm.c int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) · line 332

Quoted 12 lines
arch/arm/kvm/arm.c +12 -0
+
+	/*
+	 *  Check that the VMID is still valid.
+	 *  (The hardware supports only 256 values with the value zero
+	 *   reserved for the host, so we check if an assigned value has rolled
+	 *   over a sequence, which requires us to assign a new value and flush
+	 *   necessary caches and TLBs on all CPUs.)
+	 */
+	if (unlikely((kvm->arch.vmid ^ next_vmid) >> VMID_BITS)) {
+		/* Check for a new VMID generation */
+		if (unlikely((next_vmid & VMID_MASK) == 0)) {
+			/* Check for the (very unlikely) 64-bit wrap around */

Unlikely? it's impossible.

Quoted 13 lines
arch/arm/kvm/arm.c +12 -0
+
+/**
+ * kvm_arch_vcpu_ioctl_run - the main VCPU run function to execute guest code
+ * @vcpu:	The VCPU pointer
+ * @run:	The kvm_run structure pointer used for userspace state exchange
+ *
+ * This function is called through the VCPU_RUN ioctl called from user space. It
+ * will execute VM code in a loop until the time slice for the process is used
+ * or some emulation is needed from user space in which case the function will
+ * return with return value 0 and with the kvm_run structure filled in with the
+ * required data for the requested emulation.
+ */
 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)

arch/arm/kvm/arm.c int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) · line 382

Quoted 10 lines
arch/arm/kvm/arm.c +8 -1
 {
-	return -EINVAL;
+	int ret = 0;
+	sigset_t sigsaved;
+
+	if (vcpu->sigset_active)
+		sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
+
+	run->exit_reason = KVM_EXIT_UNKNOWN;
+	while (run->exit_reason == KVM_EXIT_UNKNOWN) {

It's not a good idea to read stuff from run unless it's part of the ABI, since userspace can play with it while you're reading it. It's harmless here but in other places it can lead to a vulnerability.

arch/arm/kvm/arm.c int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) · line 387

Quoted 5 lines
arch/arm/kvm/arm.c +5 -0
+		/*
+		 * Check conditions before entering the guest
+		 */
+		if (need_resched())
+			kvm_resched(vcpu);

I think cond_resched() is all that's needed these days (kvm_resched() predates preempt notifiers).

Quoted 33 lines
arch/arm/kvm/arm.c +31 -0
+
+		if (signal_pending(current)) {
+			ret = -EINTR;
+			run->exit_reason = KVM_EXIT_INTR;
+			break;
+		}
+
+		/*
+		 * Enter the guest
+		 */
+		trace_kvm_entry(vcpu->arch.regs.pc);
+
+		update_vttbr(vcpu->kvm);
+
+		local_irq_disable();
+		kvm_guest_enter();
+		vcpu->mode = IN_GUEST_MODE;
+
+		ret = __kvm_vcpu_run(vcpu);
+
+		vcpu->mode = OUTSIDE_GUEST_MODE;
+		kvm_guest_exit();
+		local_irq_enable();
+
+		trace_kvm_exit(vcpu->arch.regs.pc);
+	}
+
+	if (vcpu->sigset_active)
+		sigprocmask(SIG_SETMASK, &sigsaved, NULL);
+
+	return ret;
 }
Signature · 1 line
error compiling committee.c: too many arguments to function
#33 lore raw

Re: [PATCH v8 11/15] ARM: KVM: World-switch implementation

Quoted 60 lines · Avi Kivity
Quoted 44 lines · Christoffer Dall

Provides complete world-switch implementation to switch to other guests running in non-secure modes. Includes Hyp exception handlers that capture necessary exception information and stores the information on the VCPU and KVM structures.

Switching to Hyp mode is done through a simple HVC instructions. The exception vector code will check that the HVC comes from VMID==0 and if so will store the necessary state on the Hyp stack, which will look like this (growing downwards, see the hyp_hvc handler):
  ...
  Hyp_Sp + 4: spsr (Host-SVC cpsr)
  Hyp_Sp    : lr_usr

When returning from Hyp mode to SVC mode, another HVC instruction is executed from Hyp mode, which is taken in the Hyp_Svc handler. The Hyp stack pointer should be where it was left from the above initial call, since the values on the stack will be used to restore state (see hyp_svc).

Otherwise, the world-switch is pretty straight-forward. All state that can be modified by the guest is first backed up on the Hyp stack and the VCPU values is loaded onto the hardware. State, which is not loaded, but theoretically modifiable by the guest is protected through the virtualiation features to generate a trap and cause software emulation. Upon guest returns, all state is restored from hardware onto the VCPU struct and the original state is restored from the Hyp-stack onto the hardware.

One controversy may be the back-door call to __irq_svc (the host kernel's own physical IRQ handler) which is called when a physical IRQ exception is taken in Hyp mode while running in the guest.

SMP support using the VMPIDR calculated on the basis of the host MPIDR and overriding the low bits with KVM vcpu_id contributed by Marc Zyngier.

Reuse of VMIDs has been implemented by Antonios Motakis and adapated from a separate patch into the appropriate patches introducing the functionality. Note that the VMIDs are stored per VM as required by the ARM architecture reference manual.

arch/arm/kvm/arm.c +4 -0
+
+/**
+ * update_vttbr - Update the VTTBR with a valid VMID before the guest runs
+ * @kvm      The guest that we are about to run

arch/arm/kvm/arm.c int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) · line 320

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+ *
+ * Called from kvm_arch_vcpu_ioctl_run before entering the guest to ensure the
+ * VM has a valid VMID, otherwise assigns a new one and flushes corresponding
+ * caches and TLBs.
+ */
+static void update_vttbr(struct kvm *kvm)
+{
+     phys_addr_t pgd_phys;
+
+     spin_lock(&kvm_vmid_lock);

Premature, but this is sad.  I suggest you split vmid generation from next available vmid.  This allows you to make the generation counter atomic so it may be read outside the lock.

Quoted 7 lines

You can do

   if (likely(kvm->arch.vmd_gen) == atomic_read(&kvm_vmid_gen))) return;

   spin_lock(...

I knew you were going to say something here :), please take a look at this and see if you agree:

+struct kvm_arch {
+ /* The VMID generation used for the virt. memory system */
+ u64 vmid_gen;
+ u32 vmid;
+
+ /* 1-level 2nd stage table and lock */
+ struct mutex pgd_mutex;
+ pgd_t *pgd;
+
+ /* VTTBR value associated with above pgd and vmid */
+ u64 vttbr;
+};

[snip]

+/* The VMID used in the VTTBR */
+static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1); +static u8 kvm_next_vmid; +DEFINE_SPINLOCK(kvm_vmid_lock);

[snip]

+/**
+ * update_vttbr - Update the VTTBR with a valid VMID before the guest runs
+ * @kvm The guest that we are about to run
+ *
+ * Called from kvm_arch_vcpu_ioctl_run before entering the guest to ensure the
+ * VM has a valid VMID, otherwise assigns a new one and flushes corresponding
+ * caches and TLBs.
+ */
+static void update_vttbr(struct kvm *kvm)
+{
+ phys_addr_t pgd_phys;
+
+ /*
+ * Check that the VMID is still valid.
+ * (The hardware supports only 256 values with the value zero

+	 *   reserved for the host, so we check if an assigned value belongs to
+	 *   a previous generation, which which requires us to assign a new
+	 *   value. If we're the first to use a VMID for the new generation,
+	 *   we must flush necessary caches and TLBs on all CPUs.)

+ */
+ if (likely(kvm->arch.vmid_gen == atomic64_read(&kvm_vmid_gen)))
+ return;
+
+ spin_lock(&kvm_vmid_lock);
+
+ /* First user of a new VMID generation? */
+ if (unlikely(kvm_next_vmid == 0)) {
+ atomic64_inc(&kvm_vmid_gen);
+ kvm_next_vmid = 1;
+
+ /* This does nothing on UP */
+ smp_call_function(reset_vm_context, NULL, 1);
+
+ /*
+ * On SMP we know no other CPUs can use this CPU's or
+ * each other's VMID since the kvm_vmid_lock blocks
+ * them from reentry to the guest.
+ */
+
+ reset_vm_context(NULL);
+ }
+
+ kvm->arch.vmid = kvm_next_vmid;
+ kvm_next_vmid++;
+
+ /* update vttbr to be used with the new vmid */
+ pgd_phys = virt_to_phys(kvm->arch.pgd);
+ kvm->arch.vttbr = pgd_phys & ((1LLU << 40) - 1)
+ & ~((2 << VTTBR_X) - 1);
+ kvm->arch.vttbr |= (u64)(kvm->arch.vmid) << 48;
+
+ spin_unlock(&kvm_vmid_lock);
+}

Quoted 13 lines
Quoted 4 lines

+
+     /*
+      *  Check that the VMID is still valid.
+      *  (The hardware supports only 256 values with the value zero

Quoted 8 lines

+      *   reserved for the host, so we check if an assigned value has rolled
+      *   over a sequence, which requires us to assign a new value and flush
+      *   necessary caches and TLBs on all CPUs.)
+      */
+     if (unlikely((kvm->arch.vmid ^ next_vmid) >> VMID_BITS)) {
+             /* Check for a new VMID generation */
+             if (unlikely((next_vmid & VMID_MASK) == 0)) {
+                     /* Check for the (very unlikely) 64-bit wrap around */

Quoted 2 lines

Unlikely?  it's impossible.

well, if it happens every microsecond on a really fast processor, it would occur a little less than every 500,000 years :) Ok, I'll remove the check.

Quoted 19 lines
Quoted 19 lines
arch/arm/kvm/arm.c +12 -0
+
+/**
+ * kvm_arch_vcpu_ioctl_run - the main VCPU run function to execute guest code
+ * @vcpu:    The VCPU pointer
+ * @run:     The kvm_run structure pointer used for userspace state exchange
+ *
+ * This function is called through the VCPU_RUN ioctl called from user space. It
+ * will execute VM code in a loop until the time slice for the process is used
+ * or some emulation is needed from user space in which case the function will
+ * return with return value 0 and with the kvm_run structure filled in with the
+ * required data for the requested emulation.
+ */

 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) {
-     return -EINVAL;
+     int ret = 0;
+     sigset_t sigsaved;
+
+     if (vcpu->sigset_active)

Quoted 9 lines
Quoted 4 lines

+             sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); +
+     run->exit_reason = KVM_EXIT_UNKNOWN;
+     while (run->exit_reason == KVM_EXIT_UNKNOWN) {

It's not a good idea to read stuff from run unless it's part of the ABI, since userspace can play with it while you're reading it.  It's harmless here but in other places it can lead to a vulnerability.

ok, so in this case, userspace can 'suppress' an MMIO or interrupt window operation.

I can change to keep some local variable to maintain the state and have some API convention for emulation functions, if you feel strongly about it, but otherwise it feels to me like the code will be more complicated. Do you have other ideas?

Quoted 9 lines
Quoted 5 lines

+             /*
+              * Check conditions before entering the guest
+              */
+             if (need_resched())
+                     kvm_resched(vcpu);

I think cond_resched() is all that's needed these days (kvm_resched() predates preempt notifiers).

ok

Quoted 25 lines
Quoted 25 lines
arch/arm/kvm/arm.c +25 -0
+
+             if (signal_pending(current)) {
+                     ret = -EINTR;
+                     run->exit_reason = KVM_EXIT_INTR;
+                     break;
+             }
+
+             /*
+              * Enter the guest
+              */
+             trace_kvm_entry(vcpu->arch.regs.pc);
+
+             update_vttbr(vcpu->kvm);
+
+             local_irq_disable();
+             kvm_guest_enter();
+             vcpu->mode = IN_GUEST_MODE;
+
+             ret = __kvm_vcpu_run(vcpu);
+
+             vcpu->mode = OUTSIDE_GUEST_MODE;
+             kvm_guest_exit();
+             local_irq_enable();
+
+             trace_kvm_exit(vcpu->arch.regs.pc);
Quoted 8 lines
Quoted 8 lines

+     }

arch/arm/kvm/arm.c +5 -0
+
+     if (vcpu->sigset_active)
+             sigprocmask(SIG_SETMASK, &sigsaved, NULL);
+
+     return ret;

 }

Thanks,
Christoffer

#34 lore raw

Re: [PATCH v8 11/15] ARM: KVM: World-switch implementation

Quoted 6 lines · Christoffer Dall
Quoted 6 lines

Premature, but this is sad. I suggest you split vmid generation from next available vmid. This allows you to make the generation counter atomic so it may be read outside the lock.

You can do

Quoted 8 lines
Quoted 5 lines
   if (likely(kvm->arch.vmd_gen) == atomic_read(&kvm_vmid_gen)))
          return;

   spin_lock(...

I knew you were going to say something here :), please take a look at this and see if you agree:

It looks reasonable wrt locking.

arch/arm/kvm/arm.c int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) · line 339

Quoted 8 lines
arch/arm/kvm/arm.c +8 -0
+
+	/* First user of a new VMID generation? */
+	if (unlikely(kvm_next_vmid == 0)) {
+		atomic64_inc(&kvm_vmid_gen);
+		kvm_next_vmid = 1;
+
+		/* This does nothing on UP */
+		smp_call_function(reset_vm_context, NULL, 1);

Does this imply a memory barrier? If not, smp_mb__after_atomic_inc().

arch/arm/kvm/arm.c int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) · line 347

Quoted 8 lines
arch/arm/kvm/arm.c +8 -0
+
+		/*
+		 * On SMP we know no other CPUs can use this CPU's or
+		 * each other's VMID since the kvm_vmid_lock blocks
+		 * them from reentry to the guest.
+		 */
+
+		reset_vm_context(NULL);

These two lines can be folded as on_each_cpu().

Don't you have a race here where you can increment the generation just before guest entry?

cpu0 cpu1
(vmid=0, gen=1) (gen=0)
-------------------------- ----------------------
gen == global_gen, return

                           gen != global_gen
                           increment global_gen
                           smp_call_function
reset_vm_context
                           vmid=0

enter with vmid=0 enter with vmid=0

You must recheck gen after disabling interrupts to ensure global_gen didn't bump after update_vttbr but before entry. x86 has a lot of this, see vcpu_enter_guest() and vcpu->requests (doesn't apply directly to your case but may come in useful later).

Quoted 30 lines
Quoted 20 lines
Quoted 20 lines
arch/arm/kvm/arm.c +16 -1
+
+/**
+ * kvm_arch_vcpu_ioctl_run - the main VCPU run function to execute guest code
+ * @vcpu:    The VCPU pointer
+ * @run:     The kvm_run structure pointer used for userspace state exchange
+ *
+ * This function is called through the VCPU_RUN ioctl called from user space. It
+ * will execute VM code in a loop until the time slice for the process is used
+ * or some emulation is needed from user space in which case the function will
+ * return with return value 0 and with the kvm_run structure filled in with the
+ * required data for the requested emulation.
+ */
 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
-     return -EINVAL;
+     int ret = 0;
+     sigset_t sigsaved;
+
+     if (vcpu->sigset_active)
Quoted 9 lines

arch/arm/kvm/arm.c int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) · line 382

Quoted 4 lines
arch/arm/kvm/arm.c +4 -0
+             sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
+
+     run->exit_reason = KVM_EXIT_UNKNOWN;
+     while (run->exit_reason == KVM_EXIT_UNKNOWN) {

It's not a good idea to read stuff from run unless it's part of the ABI, since userspace can play with it while you're reading it. It's harmless here but in other places it can lead to a vulnerability.

Quoted 7 lines

ok, so in this case, userspace can 'suppress' an MMIO or interrupt window operation.

I can change to keep some local variable to maintain the state and have some API convention for emulation functions, if you feel strongly about it, but otherwise it feels to me like the code will be more complicated. Do you have other ideas?

x86 uses:

0 - return to userspace (run prepared)
1 - return to guest (run untouched)
-ESOMETHING - return to userspace

as return values from handlers and for locals (usually named 'r').

Signature · 1 line
error compiling committee.c: too many arguments to function
#35 lore raw

Re: [PATCH v8 11/15] ARM: KVM: World-switch implementation

Quoted 18 lines · Avi Kivity
Quoted 6 lines · Christoffer Dall
Quoted 6 lines

Premature, but this is sad.  I suggest you split vmid generation from next available vmid.  This allows you to make the generation counter atomic so it may be read outside the lock.

You can do

Quoted 8 lines
Quoted 5 lines

   if (likely(kvm->arch.vmd_gen) == atomic_read(&kvm_vmid_gen))) return;

   spin_lock(...

I knew you were going to say something here :), please take a look at this and see if you agree:

It looks reasonable wrt locking.

Quoted 11 lines

arch/arm/kvm/arm.c int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) · line 339

Quoted 8 lines
arch/arm/kvm/arm.c +8 -0
+
+     /* First user of a new VMID generation? */
+     if (unlikely(kvm_next_vmid == 0)) {
+             atomic64_inc(&kvm_vmid_gen);
+             kvm_next_vmid = 1;
+
+             /* This does nothing on UP */
+             smp_call_function(reset_vm_context, NULL, 1);

Does this imply a memory barrier?  If not, smp_mb__after_atomic_inc().

yes, it implies a memory barrier.

Quoted 11 lines

arch/arm/kvm/arm.c int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) · line 347

Quoted 8 lines
arch/arm/kvm/arm.c +8 -0
+
+             /*
+              * On SMP we know no other CPUs can use this CPU's or
+              * each other's VMID since the kvm_vmid_lock blocks
+              * them from reentry to the guest.
+              */
+
+             reset_vm_context(NULL);

These two lines can be folded as on_each_cpu().

Quoted 2 lines

Don't you have a race here where you can increment the generation just before guest entry?

I don't think I do.

Quoted 6 lines

cpu0                       cpu1
(vmid=0, gen=1)            (gen=0)
-------------------------- ----------------------
gen == global_gen, return

Quoted 7 lines

                          gen != global_gen
                          increment global_gen
                          smp_call_function
reset_vm_context
                          vmid=0

enter with vmid=0          enter with vmid=0

I can't see how the scenario above can happen. First, no-one can run with vmid 0 - it is reserved for the host. If we bump global_gen on cpuN, then since we do smp_call_function(x, x, wait=1) we are now sure that after this call, all cpus(N-1) potentially being inside a VM will have exited, called reset_vm_context, but before they can re-enter into the guest, they will call update_vttbr, and if their generation counter differs from global_gen, they will try to grab that spinlock and everything should happen in order.

Quoted 44 lines

You must recheck gen after disabling interrupts to ensure global_gen didn't bump after update_vttbr but before entry.  x86 has a lot of this, see vcpu_enter_guest() and vcpu->requests (doesn't apply directly to your case but may come in useful later).

Quoted 30 lines
Quoted 20 lines
Quoted 20 lines
arch/arm/kvm/arm.c +12 -0
+
+/**
+ * kvm_arch_vcpu_ioctl_run - the main VCPU run function to execute guest code
+ * @vcpu:    The VCPU pointer
+ * @run:     The kvm_run structure pointer used for userspace state exchange
+ *
+ * This function is called through the VCPU_RUN ioctl called from user space. It
+ * will execute VM code in a loop until the time slice for the process is used
+ * or some emulation is needed from user space in which case the function will
+ * return with return value 0 and with the kvm_run structure filled in with the
+ * required data for the requested emulation.
+ */

 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) {
-     return -EINVAL;
+     int ret = 0;
+     sigset_t sigsaved;
+
+     if (vcpu->sigset_active)

Quoted 9 lines
Quoted 4 lines

+             sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); +
+     run->exit_reason = KVM_EXIT_UNKNOWN;
+     while (run->exit_reason == KVM_EXIT_UNKNOWN) {

It's not a good idea to read stuff from run unless it's part of the ABI, since userspace can play with it while you're reading it.  It's harmless here but in other places it can lead to a vulnerability.

Quoted 7 lines

ok, so in this case, userspace can 'suppress' an MMIO or interrupt window operation.

I can change to keep some local variable to maintain the state and have some API convention for emulation functions, if you feel strongly about it, but otherwise it feels to me like the code will be more complicated. Do you have other ideas?

Quoted 6 lines

x86 uses:

 0 - return to userspace (run prepared)
 1 - return to guest (run untouched)
 -ESOMETHING - return to userspace

yeah, we can do that I guess, that's fair.

Quoted 7 lines

as return values from handlers and for locals (usually named 'r').

--
error compiling committee.c: too many arguments to function

#36 lore raw

Re: [PATCH v8 11/15] ARM: KVM: World-switch implementation

Quoted 44 lines · Christoffer Dall
Quoted 18 lines · Avi Kivity
Quoted 6 lines · Christoffer Dall
Quoted 6 lines

Premature, but this is sad.  I suggest you split vmid generation from next available vmid.  This allows you to make the generation counter atomic so it may be read outside the lock.

You can do

Quoted 8 lines
Quoted 5 lines

   if (likely(kvm->arch.vmd_gen) == atomic_read(&kvm_vmid_gen))) return;

   spin_lock(...

I knew you were going to say something here :), please take a look at this and see if you agree:

It looks reasonable wrt locking.

Quoted 11 lines

Review

Quoted 8 lines
(quoted diff) +8 -0
+
+     /* First user of a new VMID generation? */
+     if (unlikely(kvm_next_vmid == 0)) {
+             atomic64_inc(&kvm_vmid_gen);
+             kvm_next_vmid = 1;
+
+             /* This does nothing on UP */
+             smp_call_function(reset_vm_context, NULL, 1);

Does this imply a memory barrier?  If not, smp_mb__after_atomic_inc().

yes, it implies a memory barrier.

Quoted 11 lines

Review

Quoted 8 lines
(quoted diff) +8 -0
+
+             /*
+              * On SMP we know no other CPUs can use this CPU's or
+              * each other's VMID since the kvm_vmid_lock blocks
+              * them from reentry to the guest.
+              */
+
+             reset_vm_context(NULL);

These two lines can be folded as on_each_cpu().

Quoted 5 lines
Quoted 2 lines

Don't you have a race here where you can increment the generation just before guest entry?

I don't think I do.

uh, strike that, I do.

Quoted 14 lines
Quoted 6 lines

cpu0                       cpu1
(vmid=0, gen=1)            (gen=0)
-------------------------- ----------------------
gen == global_gen, return

Quoted 7 lines

                          gen != global_gen
                          increment global_gen
                          smp_call_function
reset_vm_context
                          vmid=0

enter with vmid=0          enter with vmid=0

Quoted 9 lines

I can't see how the scenario above can happen. First, no-one can run with vmid 0 - it is reserved for the host. If we bump global_gen on cpuN, then since we do smp_call_function(x, x, wait=1) we are now sure that after this call, all cpus(N-1) potentially being inside a VM will have exited, called reset_vm_context, but before they can re-enter into the guest, they will call update_vttbr, and if their generation counter differs from global_gen, they will try to grab that spinlock and everything should happen in order.

the whole vmid=0 confused me a bit. The point is since we moved the generation check outside the spin_lock we have to re-check, I see:

arch/arm/kvm/arm.c +19 -9
index 19fe3b0..74760af 100644
@@ -313,6 +313,24 @@ static void reset_vm_context(void *info)
 }
 
 /**
+ * check_new_vmid_gen - check that the VMID is still valid
+ * @kvm: The VM's VMID to checkt
+ *
+ * return true if there is a new generation of VMIDs being used
+ *
+ * The hardware supports only 256 values with the value zero reserved for the
+ * host, so we check if an assigned value belongs to a previous generation,
+ * which which requires us to assign a new value. If we're the first to use a
+ * VMID for the new generation, we must flush necessary caches and TLBs on all
+ * CPUs.
+ */
+static bool check_new_vmid_gen(struct kvm *kvm)
+{
+	if (likely(kvm->arch.vmid_gen == atomic64_read(&kvm_vmid_gen)))
+		return;
+}
+
+/**
  * update_vttbr - Update the VTTBR with a valid VMID before the guest runs
  * @kvm	The guest that we are about to run
  *
@@ -324,15 +342,7 @@ static void update_vttbr(struct kvm *kvm)
 {
 	phys_addr_t pgd_phys;
 
-	/*
-	 *  Check that the VMID is still valid.
-	 *  (The hardware supports only 256 values with the value zero
-	 *   reserved for the host, so we check if an assigned value belongs to
-	 *   a previous generation, which which requires us to assign a new
-	 *   value. If we're the first to use a VMID for the new generation,
-	 *   we must flush necessary caches and TLBs on all CPUs.)
-	 */
-	if (likely(kvm->arch.vmid_gen == atomic64_read(&kvm_vmid_gen)))
+	if (!check_new_vmid_gen(kvm))
 		return;
 
 	spin_lock(&kvm_vmid_lock);
@@ -504,6 +514,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu

*vcpu, struct kvm_run *run)
*/
preempt_disable();
local_irq_disable();
+
+ if (check_new_vmid_gen(kvm)) {
+ local_irq_enable();
+ preempt_enable();
+ continue;
+ }
+
kvm_guest_enter();
vcpu->mode = IN_GUEST_MODE;

Quoted 60 lines
Quoted 44 lines

You must recheck gen after disabling interrupts to ensure global_gen didn't bump after update_vttbr but before entry.  x86 has a lot of this, see vcpu_enter_guest() and vcpu->requests (doesn't apply directly to your case but may come in useful later).

Quoted 30 lines
Quoted 20 lines
Quoted 20 lines
(quoted diff) +12 -0
+
+/**
+ * kvm_arch_vcpu_ioctl_run - the main VCPU run function to execute guest code
+ * @vcpu:    The VCPU pointer
+ * @run:     The kvm_run structure pointer used for userspace state exchange
+ *
+ * This function is called through the VCPU_RUN ioctl called from user space. It
+ * will execute VM code in a loop until the time slice for the process is used
+ * or some emulation is needed from user space in which case the function will
+ * return with return value 0 and with the kvm_run structure filled in with the
+ * required data for the requested emulation.
+ */

 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) {
-     return -EINVAL;
+     int ret = 0;
+     sigset_t sigsaved;
+
+     if (vcpu->sigset_active)

Quoted 9 lines
Quoted 4 lines

+             sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); +
+     run->exit_reason = KVM_EXIT_UNKNOWN;
+     while (run->exit_reason == KVM_EXIT_UNKNOWN) {

It's not a good idea to read stuff from run unless it's part of the ABI, since userspace can play with it while you're reading it.  It's harmless here but in other places it can lead to a vulnerability.

Quoted 7 lines

ok, so in this case, userspace can 'suppress' an MMIO or interrupt window operation.

I can change to keep some local variable to maintain the state and have some API convention for emulation functions, if you feel strongly about it, but otherwise it feels to me like the code will be more complicated. Do you have other ideas?

Quoted 6 lines

x86 uses:

 0 - return to userspace (run prepared)
 1 - return to guest (run untouched)
 -ESOMETHING - return to userspace

yeah, we can do that I guess, that's fair.

Quoted 7 lines

as return values from handlers and for locals (usually named 'r').

--
error compiling committee.c: too many arguments to function

#37 lore raw

Re: [PATCH v8 11/15] ARM: KVM: World-switch implementation

Quoted 25 lines · Christoffer Dall
Quoted 15 lines
Quoted 7 lines

cpu0 cpu1
(vmid=0, gen=1) (gen=0)
-------------------------- ----------------------
gen == global_gen, return

Quoted 7 lines
                          gen != global_gen
                          increment global_gen
                          smp_call_function
reset_vm_context
                          vmid=0

enter with vmid=0 enter with vmid=0

Quoted 9 lines

I can't see how the scenario above can happen. First, no-one can run with vmid 0 - it is reserved for the host. If we bump global_gen on cpuN, then since we do smp_call_function(x, x, wait=1) we are now sure that after this call, all cpus(N-1) potentially being inside a VM will have exited, called reset_vm_context, but before they can re-enter into the guest, they will call update_vttbr, and if their generation counter differs from global_gen, they will try to grab that spinlock and everything should happen in order.

Quoted 2 lines

the whole vmid=0 confused me a bit. The point is since we moved the generation check outside the spin_lock we have to re-check, I see:

In fact I think the problem occured with the original code as well. The problem is that the check is not atomic wrt guest entry, so

spin_lock
check/update
spin_unlock

enter guest

has a hole between spin_unlock and guest entry.

Quoted 42 lines

/**
+ * check_new_vmid_gen - check that the VMID is still valid
+ * @kvm: The VM's VMID to checkt
+ *
+ * return true if there is a new generation of VMIDs being used
+ *
+ * The hardware supports only 256 values with the value zero reserved for the
+ * host, so we check if an assigned value belongs to a previous generation,
+ * which which requires us to assign a new value. If we're the first to use a
+ * VMID for the new generation, we must flush necessary caches and TLBs on all
+ * CPUs.
+ */

arch/arm/kvm/arm.c +8 -9
+static bool check_new_vmid_gen(struct kvm *kvm)
+{
+	if (likely(kvm->arch.vmid_gen == atomic64_read(&kvm_vmid_gen)))
+		return;
+}
+
+/**
  * update_vttbr - Update the VTTBR with a valid VMID before the guest runs
  * @kvm	The guest that we are about to run
  *
@@ -324,15 +342,7 @@ static void update_vttbr(struct kvm *kvm)
 {
 	phys_addr_t pgd_phys;
 
-	/*
-	 *  Check that the VMID is still valid.
-	 *  (The hardware supports only 256 values with the value zero
-	 *   reserved for the host, so we check if an assigned value belongs to
-	 *   a previous generation, which which requires us to assign a new
-	 *   value. If we're the first to use a VMID for the new generation,
-	 *   we must flush necessary caches and TLBs on all CPUs.)
-	 */
-	if (likely(kvm->arch.vmid_gen == atomic64_read(&kvm_vmid_gen)))
+	if (!check_new_vmid_gen(kvm))
 		return;
 
 	spin_lock(&kvm_vmid_lock);
@@ -504,6 +514,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu
 *vcpu, struct kvm_run *run)

arch/arm/kvm/arm.c int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu · line 524

Quoted 10 lines
arch/arm/kvm/arm.c +7 -0
 		 */
 		preempt_disable();
 		local_irq_disable();
+
+		if (check_new_vmid_gen(kvm)) {
+			local_irq_enable();
+			preempt_enable();
+			continue;
+		}
+

I see the same race with signals. Your signal_pending() check needs to be after the local_irq_disable(), otherwise we can enter a guest with a pending signal.

Better place the signal check before the vmid_gen check, to avoid the possibility of a a signal being held up by other guests.

Signature · 1 line
error compiling committee.c: too many arguments to function
#38 lore raw

Re: [PATCH v8 11/15] ARM: KVM: World-switch implementation

Quoted 32 lines · Avi Kivity
Quoted 25 lines · Christoffer Dall
Quoted 15 lines
Quoted 7 lines

cpu0                       cpu1
(vmid=0, gen=1)            (gen=0)
-------------------------- ----------------------
gen == global_gen, return

Quoted 7 lines

                          gen != global_gen
                          increment global_gen
                          smp_call_function
reset_vm_context
                          vmid=0

enter with vmid=0          enter with vmid=0

Quoted 9 lines

I can't see how the scenario above can happen. First, no-one can run with vmid 0 - it is reserved for the host. If we bump global_gen on cpuN, then since we do smp_call_function(x, x, wait=1) we are now sure that after this call, all cpus(N-1) potentially being inside a VM will have exited, called reset_vm_context, but before they can re-enter into the guest, they will call update_vttbr, and if their generation counter differs from global_gen, they will try to grab that spinlock and everything should happen in order.

Quoted 2 lines

the whole vmid=0 confused me a bit. The point is since we moved the generation check outside the spin_lock we have to re-check, I see:

In fact I think the problem occured with the original code as well.  The problem is that the check is not atomic wrt guest entry, so

Quoted 8 lines

 spin_lock
 check/update
 spin_unlock

 enter guest

has a hole between spin_unlock and guest entry.

you are absolutely right.

Quoted 48 lines
Quoted 48 lines

 /**
+ * check_new_vmid_gen - check that the VMID is still valid
+ * @kvm: The VM's VMID to checkt
+ *
+ * return true if there is a new generation of VMIDs being used
+ *
+ * The hardware supports only 256 values with the value zero reserved for the
+ * host, so we check if an assigned value belongs to a previous generation,
+ * which which requires us to assign a new value. If we're the first to use a
+ * VMID for the new generation, we must flush necessary caches and TLBs on all
+ * CPUs.
+ */

arch/arm/kvm/arm.c +7 -0
+static bool check_new_vmid_gen(struct kvm *kvm)
+{
+     if (likely(kvm->arch.vmid_gen == atomic64_read(&kvm_vmid_gen)))
+             return;
+}
+
+/**

  * update_vttbr - Update the VTTBR with a valid VMID before the guest runs
  * @kvm      The guest that we are about to run
  *

arch/arm/kvm/arm.c +4 -9
@@ -324,15 +342,7 @@ static void update_vttbr(struct kvm *kvm)
  {
       phys_addr_t pgd_phys;
 
-     /*
-      *  Check that the VMID is still valid.
-      *  (The hardware supports only 256 values with the value zero
-      *   reserved for the host, so we check if an assigned value belongs to
-      *   a previous generation, which which requires us to assign a new
-      *   value. If we're the first to use a VMID for the new generation,
-      *   we must flush necessary caches and TLBs on all CPUs.)
-      */
-     if (likely(kvm->arch.vmid_gen == atomic64_read(&kvm_vmid_gen)))
+     if (!check_new_vmid_gen(kvm))
               return;
 
       spin_lock(&kvm_vmid_lock);
@@ -504,6 +514,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu
 *vcpu, struct kvm_run *run)
                */
               preempt_disable();
               local_irq_disable();
+
+             if (check_new_vmid_gen(kvm)) {
+                     local_irq_enable();
Quoted 9 lines

arch/arm/kvm/arm.c static void reset_vm_context(void *info) · line 332

Quoted 4 lines
arch/arm/kvm/arm.c +4 -0
+                     preempt_enable();
+                     continue;
+             }
+

I see the same race with signals.  Your signal_pending() check needs to be after the local_irq_disable(), otherwise we can enter a guest with a pending signal.

that's not functionally incorrect though is it? It may simply increase the latency for the signal delivery as far as I can see, but I definitely don't mind changing this path in any case.

Quoted 4 lines

Better place the signal check before the vmid_gen check, to avoid the possibility of a a signal being held up by other guests.

nice ;)

#39 lore raw

Re: [PATCH v8 11/15] ARM: KVM: World-switch implementation

Quoted 18 lines · Christoffer Dall
Quoted 8 lines
Quoted 8 lines
(quoted diff) +3 -0
@@ -504,6 +514,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu
 *vcpu, struct kvm_run *run)
               */
              preempt_disable();
              local_irq_disable();
+
+             if (check_new_vmid_gen(kvm)) {
+                     local_irq_enable();
Quoted 9 lines

arch/arm/kvm/arm.c static void reset_vm_context(void *info) · line 332

Quoted 4 lines
arch/arm/kvm/arm.c +4 -0
+                     preempt_enable();
+                     continue;
+             }
+

I see the same race with signals. Your signal_pending() check needs to be after the local_irq_disable(), otherwise we can enter a guest with a pending signal.

Quoted 3 lines

that's not functionally incorrect though is it? It may simply increase the latency for the signal delivery as far as I can see, but I definitely don't mind changing this path in any case.

Nothing guarantees that there will be a next exit. I think we still run the timer tick on guest entry, so we'll exit after a few milliseconds, but there are patches to disable the timer tick if only one task is queued.

Signature · 1 line
error compiling committee.c: too many arguments to function
#40 lore raw

PATCH v8 12/15 ARM: KVM: Emulation framework and CP15 emulation

In reply to Christoffer Dall

From: Christoffer Dall <[email protected]>

Adds a new important function in the main KVM/ARM code called handle_exit() which is called from kvm_arch_vcpu_ioctl_run() on returns from guest execution. This function examines the Hyp-Syndrome-Register (HSR), which contains information telling KVM what caused the exit from the guest.

Some of the reasons for an exit are CP15 accesses, which are not allowed from the guest and this commit handles these exits by emulating the intended operation in software and skip the guest instruction.

---
 arch/arm/include/asm/kvm_emulate.h |    9 +
 arch/arm/kvm/arm.c                 |   87 +++++++++
 arch/arm/kvm/emulate.c             |  330 ++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/trace.h               |   28 +++
 4 files changed, 454 insertions(+)
arch/arm/include/asm/kvm_emulate.h +9 -0
index 7ab696d..4b49168 100644
@@ -40,6 +40,15 @@ static inline enum vcpu_mode vcpu_mode(struct kvm_vcpu *vcpu)
 	return modes_table[vcpu->arch.regs.cpsr & 0xf];
 }
 
+int kvm_handle_cp10_id(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp_0_13_access(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run);
+void kvm_adjust_itstate(struct kvm_vcpu *vcpu);
+
 /*
  * Return the SPSR for the specified mode of the virtual CPU.
  */
arch/arm/kvm/arm.c +87 -0

105 lines not on this page · raw message

arch/arm/kvm/emulate.c +330 -0

340 lines not on this page · raw message

arch/arm/kvm/trace.h +28 -0
index f8869c1..e474a0a 100644
@@ -39,7 +39,35 @@ TRACE_EVENT(kvm_exit,
 	TP_printk("PC: 0x%08lx", __entry->vcpu_pc)
 );
 
+/* Architecturally implementation defined CP15 register access */
+TRACE_EVENT(kvm_emulate_cp15_imp,
+	TP_PROTO(unsigned long Op1, unsigned long Rt1, unsigned long CRn,
+		 unsigned long CRm, unsigned long Op2, bool is_write),
+	TP_ARGS(Op1, Rt1, CRn, CRm, Op2, is_write),
 
+	TP_STRUCT__entry(
+		__field(	unsigned int,	Op1		)
+		__field(	unsigned int,	Rt1		)
+		__field(	unsigned int,	CRn		)
+		__field(	unsigned int,	CRm		)
+		__field(	unsigned int,	Op2		)
+		__field(	bool,		is_write	)
+	),
+
+	TP_fast_assign(
+		__entry->is_write		= is_write;
+		__entry->Op1			= Op1;
+		__entry->Rt1			= Rt1;
+		__entry->CRn			= CRn;
+		__entry->CRm			= CRm;
+		__entry->Op2			= Op2;
+	),
+
+	TP_printk("Implementation defined CP15: %s\tp15, %u, r%u, c%u, c%u, %u",
+			(__entry->is_write) ? "mcr" : "mrc",
+			__entry->Op1, __entry->Rt1, __entry->CRn,
+			__entry->CRm, __entry->Op2)
+);
 
 #endif /* _TRACE_KVM_H */
 
#41 lore raw

PATCH v8 13/15 ARM: KVM: Handle guest faults in KVM

In reply to Christoffer Dall

From: Christoffer Dall <[email protected]>

Handles the guest faults in KVM by mapping in corresponding user pages in the 2nd stage page tables.

Introduces new ARM-specific kernel memory types, PAGE_KVM_GUEST and pgprot_guest variables used to map 2nd stage memory for KVM guests.

Leverages MMU notifiers on KVM/ARM by supporting the kvm_unmap_hva() operation, where we remove the HVA from the 2nd stage translation. All other KVM MMU notifierhooks are NOPs.

---
 arch/arm/include/asm/kvm_asm.h        |    3 +
 arch/arm/include/asm/kvm_host.h       |   20 ++++
 arch/arm/include/asm/pgtable-3level.h |    8 ++
 arch/arm/include/asm/pgtable.h        |    4 +
 arch/arm/kvm/Kconfig                  |    1 
 arch/arm/kvm/exports.c                |    1 
 arch/arm/kvm/interrupts.S             |   37 +++++++
 arch/arm/kvm/mmu.c                    |  164 +++++++++++++++++++++++++++++++++
 arch/arm/mm/mmu.c                     |    3 +
 9 files changed, 240 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_asm.h +3 -0
index c2ec131..c4f40f3 100644
@@ -32,6 +32,7 @@
 #define SMCHYP_HVBAR_W 0xfffffff0
 
 #ifndef __ASSEMBLY__
+struct kvm;
 struct kvm_vcpu;
 
 extern char __kvm_hyp_init[];
@@ -45,6 +46,8 @@ extern char __kvm_hyp_vector[];
 extern char __kvm_hyp_code_start[];
 extern char __kvm_hyp_code_end[];
 
+extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
+
 extern void __kvm_flush_vm_context(void);
 
 extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
arch/arm/include/asm/kvm_host.h +20 -0
index ed4144b..21712c5 100644
@@ -116,4 +116,24 @@ struct kvm_vcpu_stat {
 	u32 halt_wakeup;
 };
 
+#define KVM_ARCH_WANT_MMU_NOTIFIER
+struct kvm;
+int kvm_unmap_hva(struct kvm *kvm, unsigned long hva);
+
+/* We do not have shadow page tables, hence the empty hooks */
+static inline int kvm_age_hva(struct kvm *kvm, unsigned long hva)
+{
+	return 0;
+}
+
+static inline int kvm_test_age_hva(struct kvm *kvm, unsigned long hva)
+{
+	return 0;
+}
+
+static inline void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva,
+				    pte_t pte)
+{
+}
+
 #endif /* __ARM_KVM_HOST_H__ */
arch/arm/include/asm/pgtable-3level.h +8 -0
index 1169a8a..e10bb5e 100644
@@ -102,6 +102,14 @@
  */
 #define L_PGD_SWAPPER		(_AT(pgdval_t, 1) << 55)	/* swapper_pg_dir entry */
 
+/*
+ * 2-nd stage PTE definitions for LPAE.
+ */
+#define L_PTE2_READ		(_AT(pteval_t, 1) << 6)	/* HAP[0] */
+#define L_PTE2_WRITE		(_AT(pteval_t, 1) << 7)	/* HAP[1] */
+#define L_PTE2_NORM_WB		(_AT(pteval_t, 3) << 4)	/* MemAttr[3:2] */
+#define L_PTE2_INNER_WB		(_AT(pteval_t, 3) << 2)	/* MemAttr[1:0] */
+
 #ifndef __ASSEMBLY__
 
 #define pud_none(pud)		(!pud_val(pud))
arch/arm/include/asm/pgtable.h +4 -0
index 4b72287..2561a8b 100644
@@ -70,6 +70,7 @@ extern void __pgd_error(const char *file, int line, pgd_t);
 
 extern pgprot_t		pgprot_user;
 extern pgprot_t		pgprot_kernel;
+extern pgprot_t		pgprot_guest;
 
 #define _MOD_PROT(p, b)	__pgprot(pgprot_val(p) | (b))
 
@@ -83,6 +84,9 @@ extern pgprot_t		pgprot_kernel;
 #define PAGE_KERNEL		_MOD_PROT(pgprot_kernel, L_PTE_XN)
 #define PAGE_KERNEL_EXEC	pgprot_kernel
 #define PAGE_HYP		_MOD_PROT(pgprot_kernel, L_PTE_USER)
+#define PAGE_KVM_GUEST		_MOD_PROT(pgprot_guest, L_PTE2_READ | \
+					  L_PTE2_WRITE | L_PTE2_NORM_WB | \
+					  L_PTE2_INNER_WB)
 
 #define __PAGE_NONE		__pgprot(_L_PTE_DEFAULT | L_PTE_RDONLY | L_PTE_XN)
 #define __PAGE_SHARED		__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN)
arch/arm/kvm/Kconfig +1 -0
index 83abbe0..7fa50d3 100644
@@ -36,6 +36,7 @@ config KVM_ARM_HOST
 	depends on KVM
 	depends on MMU
 	depends on CPU_V7 && ARM_VIRT_EXT
+	select	MMU_NOTIFIER
 	---help---
 	  Provides host support for ARM processors.
 
arch/arm/kvm/exports.c +1 -0
index 9bdaf11..ec6e7c4 100644
@@ -30,5 +30,6 @@ EXPORT_SYMBOL_GPL(__kvm_hyp_code_end);
 EXPORT_SYMBOL_GPL(__kvm_vcpu_run);
 
 EXPORT_SYMBOL_GPL(__kvm_flush_vm_context);
+EXPORT_SYMBOL_GPL(__kvm_tlb_flush_vmid);
 
 EXPORT_SYMBOL_GPL(smp_send_reschedule);
arch/arm/kvm/interrupts.S +37 -0
index 13b7b85..4a70250 100644
@@ -34,9 +34,46 @@ __kvm_hyp_code_start:
 	.globl __kvm_hyp_code_start
 
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@  Flush per-VMID TLBs
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+/*
+ * void __kvm_tlb_flush_vmid(struct kvm *kvm);
+ *
+ * We rely on the hardware to broadcast the TLB invalidation to all CPUs
+ * inside the inner-shareable domain (which is the case for all v7
+ * implementations).  If we come across a non-IS SMP implementation, we'll
+ * have to use an IPI based mechanism. Until then, we stick to the simple
+ * hardware assisted version.
+ */
+ENTRY(__kvm_tlb_flush_vmid)
+	hvc	#0			@ Switch to Hyp mode
+	push	{r2, r3}
+
+	add	r0, r0, #KVM_VTTBR
+	ldrd	r2, r3, [r0]
+	mcrr	p15, 6, r2, r3, c2	@ Write VTTBR
+	isb
+	mcr     p15, 0, r0, c8, c3, 0	@ TLBIALLIS (rt ignored)
+	dsb
+	isb
+	mov	r2, #0
+	mov	r3, #0
+	mcrr	p15, 6, r2, r3, c2	@ Back to VMID #0
+	isb
+
+	pop	{r2, r3}
+	hvc	#0			@ Back to SVC
+	bx	lr
+ENDPROC(__kvm_tlb_flush_vmid)
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 @  Flush TLBs and instruction caches of current CPU for all VMIDs
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
+/*
+ * void __kvm_flush_vm_context(void);
+ */
 ENTRY(__kvm_flush_vm_context)
 	hvc	#0			@ switch to hyp-mode
 
arch/arm/kvm/mmu.c +163 -1

190 lines not on this page · raw message

arch/arm/mm/mmu.c +3 -0
index f7439e7..7dd4b54 100644
@@ -56,9 +56,11 @@ static unsigned int cachepolicy __initdata = CPOLICY_WRITEBACK;
 static unsigned int ecc_mask __initdata = 0;
 pgprot_t pgprot_user;
 pgprot_t pgprot_kernel;
+pgprot_t pgprot_guest;
 
 EXPORT_SYMBOL(pgprot_user);
 EXPORT_SYMBOL(pgprot_kernel);
+EXPORT_SYMBOL(pgprot_guest);
 
 struct cachepolicy {
 	const char	policy[16];
@@ -520,6 +522,7 @@ static void __init build_mem_type_table(void)
 	pgprot_user   = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | user_pgprot);
 	pgprot_kernel = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG |
 				 L_PTE_DIRTY | kern_pgprot);
+	pgprot_guest  = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG);
 
 	mem_types[MT_LOW_VECTORS].prot_l1 |= ecc_mask;
 	mem_types[MT_HIGH_VECTORS].prot_l1 |= ecc_mask;
#42 lore raw

Re: [PATCH v8 13/15] ARM: KVM: Handle guest faults in KVM

Quoted 11 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

Handles the guest faults in KVM by mapping in corresponding user pages in the 2nd stage page tables.

Introduces new ARM-specific kernel memory types, PAGE_KVM_GUEST and pgprot_guest variables used to map 2nd stage memory for KVM guests.

Leverages MMU notifiers on KVM/ARM by supporting the kvm_unmap_hva() operation, where we remove the HVA from the 2nd stage translation. All other KVM MMU notifierhooks are NOPs.

I think you must at least support change_pte (possibly by unmapping). Andrea?

Signature · 1 line
error compiling committee.c: too many arguments to function
#43 lore raw

Re: [PATCH v8 13/15] ARM: KVM: Handle guest faults in KVM

Quoted 9 lines · Avi Kivity
Quoted 8 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

Handles the guest faults in KVM by mapping in corresponding user pages in the 2nd stage page tables.

Introduces new ARM-specific kernel memory types, PAGE_KVM_GUEST and pgprot_guest variables used to map 2nd stage memory for KVM guests.

Quoted 7 lines
Quoted 3 lines

Leverages MMU notifiers on KVM/ARM by supporting the kvm_unmap_hva() operation, where we remove the HVA from the 2nd stage translation. All other KVM MMU notifierhooks are NOPs.

I think you must at least support change_pte (possibly by unmapping). Andrea?

hmmm, at least for KSM support we need to support change_pte (are there other callers for this type of memory?)

It's not trivial I guess, since we would need to support COW and thereby stage-2 permission faults... Marc, right?

-Christoffer

#44 lore raw

Re: [PATCH v8 13/15] ARM: KVM: Handle guest faults in KVM

Quoted 14 lines · Christoffer Dall
Quoted 13 lines · Avi Kivity
Quoted 11 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

Handles the guest faults in KVM by mapping in corresponding user pages in the 2nd stage page tables.

Introduces new ARM-specific kernel memory types, PAGE_KVM_GUEST and pgprot_guest variables used to map 2nd stage memory for KVM guests.

Leverages MMU notifiers on KVM/ARM by supporting the kvm_unmap_hva() operation, where we remove the HVA from the 2nd stage translation. All other KVM MMU notifierhooks are NOPs.

Quoted 8 lines
Quoted 3 lines

I think you must at least support change_pte (possibly by unmapping). Andrea?

hmmm, at least for KSM support we need to support change_pte (are there other callers for this type of memory?)

It's not trivial I guess, since we would need to support COW and thereby stage-2 permission faults... Marc, right?

As I mentioned, you can support change_pte by unmapping. This will cause ksm to be ineffective (pages will only be shared if the guest doesn't touch them at all), but it's enough to get started.

Signature · 1 line
error compiling committee.c: too many arguments to function
#45 lore raw

Re: [PATCH v8 13/15] ARM: KVM: Handle guest faults in KVM

Quoted 21 lines · Avi Kivity
Quoted 20 lines · Christoffer Dall
Quoted 9 lines · Avi Kivity
Quoted 8 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

Handles the guest faults in KVM by mapping in corresponding user pages in the 2nd stage page tables.

Introduces new ARM-specific kernel memory types, PAGE_KVM_GUEST and pgprot_guest variables used to map 2nd stage memory for KVM guests.

Quoted 7 lines
Quoted 3 lines

Leverages MMU notifiers on KVM/ARM by supporting the kvm_unmap_hva() operation, where we remove the HVA from the 2nd stage translation. All other KVM MMU notifierhooks are NOPs.

I think you must at least support change_pte (possibly by unmapping). Andrea?

hmmm, at least for KSM support we need to support change_pte (are there other callers for this type of memory?)

Quoted 6 lines
Quoted 2 lines

It's not trivial I guess, since we would need to support COW and thereby stage-2 permission faults... Marc, right?

As I mentioned, you can support change_pte by unmapping. This will cause ksm to be ineffective (pages will only be shared if the guest doesn't touch them at all), but it's enough to get started.

The main reason change_pte initially was required for KSM to be effective was because gup_fast was called with write=1 unconditionally. change_pte was also responsible to set the spte readonly. But that should have been fixed now on x86, so KSM should be effective even despite lack of change_pte on x86.

If the KVM page fault is calling gfn_to_pfn_async(write=0/1) depending if the vmexit was caused by a write or read access (instead of gfn_to_pfn which still has the unconditional write=1), and in turn it's forced to sete the spte readonly after calling gfn_to_pfn_async(write=0), change_pte is still useful but it's only a worthwhile optimization to avoid a spte read fault after every KSM page merged, it's not strictly required for KSM effectiveness anymore.

In short if ARM does the right thing with regard of KVM read faults passed to gup_fast(write=0) and setting the spte readonly, all should work good with KSM (even if not as optimal as with change_pte).

#46 lore raw

Re: [PATCH v8 13/15] ARM: KVM: Handle guest faults in KVM

Quoted 43 lines · Andrea Arcangeli
Quoted 21 lines · Avi Kivity
Quoted 20 lines · Christoffer Dall
Quoted 9 lines · Avi Kivity
Quoted 8 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

Handles the guest faults in KVM by mapping in corresponding user pages in the 2nd stage page tables.

Introduces new ARM-specific kernel memory types, PAGE_KVM_GUEST and pgprot_guest variables used to map 2nd stage memory for KVM guests.

Quoted 7 lines
Quoted 3 lines

Leverages MMU notifiers on KVM/ARM by supporting the kvm_unmap_hva() operation, where we remove the HVA from the 2nd stage translation. All other KVM MMU notifierhooks are NOPs.

I think you must at least support change_pte (possibly by unmapping). Andrea?

hmmm, at least for KSM support we need to support change_pte (are there other callers for this type of memory?)

Quoted 6 lines
Quoted 2 lines

It's not trivial I guess, since we would need to support COW and thereby stage-2 permission faults... Marc, right?

As I mentioned, you can support change_pte by unmapping.  This will cause ksm to be ineffective (pages will only be shared if the guest doesn't touch them at all), but it's enough to get started.

The main reason change_pte initially was required for KSM to be effective was because gup_fast was called with write=1 unconditionally. change_pte was also responsible to set the spte readonly. But that should have been fixed now on x86, so KSM should be effective even despite lack of change_pte on x86.

If the KVM page fault is calling gfn_to_pfn_async(write=0/1) depending if the vmexit was caused by a write or read access (instead of gfn_to_pfn which still has the unconditional write=1), and in turn it's forced to sete the spte readonly after calling gfn_to_pfn_async(write=0), change_pte is still useful but it's only a worthwhile optimization to avoid a spte read fault after every KSM page merged, it's not strictly required for KSM effectiveness anymore.

Quoted 3 lines

In short if ARM does the right thing with regard of KVM read faults passed to gup_fast(write=0) and setting the spte readonly, all should work good with KSM (even if not as optimal as with change_pte).

ah, we don't do things right, we use gfn_to_pfn() flat out and will always break the COW :)

I guess now, when change_pte is a nop, it's outright incorrect if anyone runs KSM.

This has just been added to my todo-list.

-Christoffer

#47 lore raw

Re: [PATCH v8 13/15] ARM: KVM: Handle guest faults in KVM

Quoted 7 lines · Christoffer Dall

ah, we don't do things right, we use gfn_to_pfn() flat out and will always break the COW :)

I guess now, when change_pte is a nop, it's outright incorrect if anyone runs KSM.

This has just been added to my todo-list.

Great.

Either implement change_pte, or use gfn_to_pfn_async(write_fault=0) for read faults.

#48 lore raw

PATCH v8 14/15 ARM: KVM: Handle I/O aborts

In reply to Christoffer Dall

When the guest accesses I/O memory this will create data abort exceptions and they are handled by decoding the HSR information (physical address, read/write, length, register) and forwarding reads and writes to QEMU which performs the device emulation.

Certain classes of load/store operations do not support the syndrome information provided in the HSR and we therefore must be able to fetch the offending instruction from guest memory and decode it manually.

This requires changing the general flow somewhat since new calls to run the VCPU must check if there's a pending MMIO load and perform the write after userspace has made the data available.

---
 arch/arm/include/asm/kvm_emulate.h |    2 
 arch/arm/include/asm/kvm_host.h    |    1 
 arch/arm/include/asm/kvm_mmu.h     |    1 
 arch/arm/kvm/arm.c                 |    6 +
 arch/arm/kvm/emulate.c             |  282 ++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/mmu.c                 |  162 ++++++++++++++++++++-
 arch/arm/kvm/trace.h               |   21 +++
 7 files changed, 472 insertions(+), 3 deletions(-)
arch/arm/include/asm/kvm_emulate.h +2 -0
index 4b49168..1f93e71 100644
@@ -47,6 +47,8 @@ int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run);
 int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run);
 int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run);
 int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_emulate_mmio_ls(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
+			unsigned long instr);
 void kvm_adjust_itstate(struct kvm_vcpu *vcpu);
 
 /*
arch/arm/include/asm/kvm_host.h +1 -0
index 21712c5..df62754 100644
@@ -101,6 +101,7 @@ struct kvm_vcpu_arch {
 				   instructions */
 
 	/* IO related fields */
+	bool mmio_sign_extend;	/* for byte/halfword loads */
 	u32 mmio_rd;
 
 	/* Interrupt related fields */
arch/arm/include/asm/kvm_mmu.h +1 -0
index d95662eb..0ce51b0 100644
@@ -37,6 +37,7 @@ void kvm_hyp_pgd_free(void);
 int kvm_alloc_stage2_pgd(struct kvm *kvm);
 void kvm_free_stage2_pgd(struct kvm *kvm);
 
+int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run);
 int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run);
 
 #endif /* __ARM_KVM_MMU_H__ */
arch/arm/kvm/arm.c +6 -0
index 24de3b7..eedf171 100644
@@ -456,6 +456,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 	int ret = 0;
 	sigset_t sigsaved;
 
+	if (run->exit_reason == KVM_EXIT_MMIO) {
+		ret = kvm_handle_mmio_return(vcpu, vcpu->run);
+		if (ret)
+			return ret;
+	}
+
 	if (vcpu->sigset_active)
 		sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
 
arch/arm/kvm/emulate.c +281 -1

295 lines not on this page · raw message

arch/arm/kvm/mmu.c +160 -2

184 lines not on this page · raw message

arch/arm/kvm/trace.h +21 -0
index e474a0a..bd3a6cc 100644
@@ -39,6 +39,27 @@ TRACE_EVENT(kvm_exit,
 	TP_printk("PC: 0x%08lx", __entry->vcpu_pc)
 );
 
+TRACE_EVENT(kvm_mmio_emulate,
+	TP_PROTO(unsigned long vcpu_pc, unsigned long instr,
+		 unsigned long cpsr),
+	TP_ARGS(vcpu_pc, instr, cpsr),
+
+	TP_STRUCT__entry(
+		__field(	unsigned long,	vcpu_pc		)
+		__field(	unsigned long,	instr		)
+		__field(	unsigned long,	cpsr		)
+	),
+
+	TP_fast_assign(
+		__entry->vcpu_pc		= vcpu_pc;
+		__entry->vcpu_pc		= instr;
+		__entry->vcpu_pc		= cpsr;
+	),
+
+	TP_printk("Emulate MMIO at: 0x%08lx (instr: %08lx, cpsr: %08lx)",
+		  __entry->vcpu_pc, __entry->instr, __entry->cpsr)
+);
+
 /* Architecturally implementation defined CP15 register access */
 TRACE_EVENT(kvm_emulate_cp15_imp,
 	TP_PROTO(unsigned long Op1, unsigned long Rt1, unsigned long CRn,
#49 lore raw

Re: [PATCH v8 14/15] ARM: KVM: Handle I/O aborts

Quoted 28 lines · Christoffer Dall

When the guest accesses I/O memory this will create data abort exceptions and they are handled by decoding the HSR information (physical address, read/write, length, register) and forwarding reads and writes to QEMU which performs the device emulation.

Certain classes of load/store operations do not support the syndrome information provided in the HSR and we therefore must be able to fetch the offending instruction from guest memory and decode it manually.

This requires changing the general flow somewhat since new calls to run the VCPU must check if there's a pending MMIO load and perform the write after userspace has made the data available.

arch/arm/kvm/mmu.c +0 -0
 	memslot = gfn_to_memslot(vcpu->kvm, gfn);
arch/arm/kvm/trace.h +5 -0
index e474a0a..bd3a6cc 100644
@@ -39,6 +39,27 @@ TRACE_EVENT(kvm_exit,
 	TP_printk("PC: 0x%08lx", __entry->vcpu_pc)
 );
 
+TRACE_EVENT(kvm_mmio_emulate,
+	TP_PROTO(unsigned long vcpu_pc, unsigned long instr,
+		 unsigned long cpsr),
+	TP_ARGS(vcpu_pc, instr, cpsr),
+

arch/arm/kvm/trace.h TRACE_EVENT(kvm_exit, · line 56

Quoted 10 lines
arch/arm/kvm/trace.h +10 -0
+	TP_STRUCT__entry(
+		__field(	unsigned long,	vcpu_pc		)
+		__field(	unsigned long,	instr		)
+		__field(	unsigned long,	cpsr		)
+	),
+
+	TP_fast_assign(
+		__entry->vcpu_pc		= vcpu_pc;
+		__entry->vcpu_pc		= instr;
+		__entry->vcpu_pc		= cpsr;

-ECUTANDPASTE

Quoted 6 lines
arch/arm/kvm/trace.h +6 -0
+	),
+
+	TP_printk("Emulate MMIO at: 0x%08lx (instr: %08lx, cpsr: %08lx)",
+		  __entry->vcpu_pc, __entry->instr, __entry->cpsr)
+);
+
Signature · 1 line
error compiling committee.c: too many arguments to function
#50 lore raw

Re: [PATCH v8 14/15] ARM: KVM: Handle I/O aborts

Quoted 33 lines · Avi Kivity
Quoted 32 lines · Christoffer Dall

When the guest accesses I/O memory this will create data abort exceptions and they are handled by decoding the HSR information (physical address, read/write, length, register) and forwarding reads and writes to QEMU which performs the device emulation.

Certain classes of load/store operations do not support the syndrome information provided in the HSR and we therefore must be able to fetch the offending instruction from guest memory and decode it manually.

This requires changing the general flow somewhat since new calls to run the VCPU must check if there's a pending MMIO load and perform the write after userspace has made the data available.

      memslot = gfn_to_memslot(vcpu->kvm, gfn);

arch/arm/kvm/trace.h +9 -0
index e474a0a..bd3a6cc 100644
@@ -39,6 +39,27 @@ TRACE_EVENT(kvm_exit,
       TP_printk("PC: 0x%08lx", __entry->vcpu_pc)
  );
 
+TRACE_EVENT(kvm_mmio_emulate,
+     TP_PROTO(unsigned long vcpu_pc, unsigned long instr,
+              unsigned long cpsr),
+     TP_ARGS(vcpu_pc, instr, cpsr),
+
+     TP_STRUCT__entry(
+             __field(        unsigned long,  vcpu_pc         )
+             __field(        unsigned long,  instr           )
+             __field(        unsigned long,  cpsr            )
Quoted 9 lines

arch/arm/kvm/trace.h TRACE_EVENT(kvm_exit, · line 56

Quoted 6 lines
arch/arm/kvm/trace.h +6 -0
+     ),
+
+     TP_fast_assign(
+             __entry->vcpu_pc                = vcpu_pc;
+             __entry->vcpu_pc                = instr;
+             __entry->vcpu_pc                = cpsr;

-ECUTANDPASTE

hehe, nice, thanks.

-Christoffer

#51 lore raw

PATCH v8 15/15 ARM: KVM: Guest wait-for-interrupts (WFI) support

In reply to Christoffer Dall

From: Christoffer Dall <[email protected]>

When the guest executes a WFI instruction the operation is trapped to KVM, which emulates the instruction in software. There is no correlation between a guest executing a WFI instruction and actually putting the hardware into a low-power mode, since a KVM guest is essentially a process and the WFI instruction can be seen as 'sleep' call from this process. Therefore, we flag the VCPU to be in wait_for_interrupts mode and call the main KVM function kvm_vcpu_block() function. This function will put the thread on a wait-queue and call schedule.

When an interrupt comes in through KVM_IRQ_LINE (see previous patch) we signal the VCPU thread and unflag the VCPU to no longer wait for interrupts. All calls to kvm_arch_vcpu_ioctl_run() result in a call to kvm_vcpu_block() as long as the VCPU is in wfi-mode.

---
 arch/arm/kvm/arm.c     |   15 ++++++++++++++-
 arch/arm/kvm/emulate.c |   12 ++++++++++++
 arch/arm/kvm/trace.h   |   16 ++++++++++++++++
 3 files changed, 42 insertions(+), 1 deletion(-)
arch/arm/kvm/arm.c +14 -1
index eedf171..e4b659b 100644
@@ -291,9 +291,17 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
 	return -EINVAL;
 }
 
+/**
+ * kvm_arch_vcpu_runnable - determine if the vcpu can be scheduled
+ * @v:		The VCPU pointer
+ *
+ * If the guest CPU is not waiting for interrupts (or waiting and
+ * an interrupt is pending) then it is by definition runnable.
+ */
 int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
 {
-	return 0;
+	return !!v->arch.irq_lines ||
+		!v->arch.wait_for_interrupts;
 }
 
 int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v)
@@ -479,6 +487,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 			break;
 		}
 
+		if (vcpu->arch.wait_for_interrupts)
+			kvm_vcpu_block(vcpu);
+
 		/*
 		 * Enter the guest
 		 */
@@ -551,6 +562,8 @@ static int kvm_arch_vm_ioctl_irq_line(struct kvm *kvm,
 	 * trigger a world-switch round on the running physical CPU to set the
 	 * virtual IRQ/FIQ fields in the HCR appropriately.
 	 */
+	if (irq_level->level)
+		vcpu->arch.wait_for_interrupts = 0;
 	kvm_vcpu_kick(vcpu);
 
 	return 0;
arch/arm/kvm/emulate.c +12 -0
index 47aa04c..914b17f 100644
@@ -433,8 +433,20 @@ int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
 	return emulate_cp15(vcpu, &params);
 }
 
+/**
+ * kvm_handle_wfi - handle a wait-for-interrupts instruction executed by a guest
+ * @vcpu:	the vcpu pointer
+ * @run:	the kvm_run structure pointer
+ *
+ * Simply sets the wait_for_interrupts flag on the vcpu structure, which will
+ * halt execution of world-switches and schedule other host processes until
+ * there is an incoming IRQ or FIQ to the VM.
+ */
 int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
+	trace_kvm_wfi(vcpu->arch.regs.pc);
+	if (!vcpu->arch.irq_lines)
+		vcpu->arch.wait_for_interrupts = 1;
 	return 0;
 }
 
arch/arm/kvm/trace.h +16 -0
index bd3a6cc..fc68394 100644
@@ -90,6 +90,22 @@ TRACE_EVENT(kvm_emulate_cp15_imp,
 			__entry->CRm, __entry->Op2)
 );
 
+TRACE_EVENT(kvm_wfi,
+	TP_PROTO(unsigned long vcpu_pc),
+	TP_ARGS(vcpu_pc),
+
+	TP_STRUCT__entry(
+		__field(	unsigned long,	vcpu_pc		)
+	),
+
+	TP_fast_assign(
+		__entry->vcpu_pc		= vcpu_pc;
+	),
+
+	TP_printk("guest executed wfi at: 0x%08lx", __entry->vcpu_pc)
+);
+
+
 #endif /* _TRACE_KVM_H */
 
 #undef TRACE_INCLUDE_PATH
#52 lore raw
In reply to Christoffer Dall
Quoted 51 lines · Christoffer Dall

The following series implements KVM support for ARM processors, specifically on the Cortex A-15 platform. Work is done in collaboration between Columbia University, Virtual Open Systems and ARM/Linaro.

The patch series applies to kvm/next, specifically commit:
25e531a988ea5a64bd97a72dc9d3c65ad5850120

This is Version 8 of the patch series, but the first two versions were reviewed outside of the KVM mailing list. Changes can also be pulled from:
git://github.com/virtualopensystems/linux-kvm-arm.git kvm-a15-v8

A non-flattened edition of the patch series can be found at:
git://github.com/virtualopensystems/linux-kvm-arm.git kvm-a15-v8-stage

The implementation is broken up into a logical set of patches, the first four are preparatory patches:
1. Add mem_type prot_pte accessor (ARM community)
2. Use KVM_CAP_IRQ_ROUTING to protect routing code (KVM community)
3. Introduce __KVM_HAVE_IRQ_LINE (KVM community)
4. Guard code with CONFIG_MMU_NOTIFIER (KVM community)

KVM guys, please consider pulling the KVM generic patches as early as possible. Thanks.

The main implementation is broken up into separate patches, the first containing a skeleton of files, makefile changes, the basic user space interface and KVM architecture specific stubs. Subsequent patches implement parts of the system as listed:

 1.  Preparatory patch introducing __KVM_HAVE_IRQ_LINE
 2.  Preparatory patch guarding mmu_notifier code with CONFIG_MMU_NOTIFIER
 3.  Skeleton
 4.  Identity Mapping for Hyp mode
 5.  Hypervisor initialization
 6.  Hypervisor module unloading
 7.  Memory virtualization setup (hyp mode mappings and 2nd stage)
 8.  Inject IRQs and FIQs from userspace
 9.  World-switch implementation and Hyp exception vectors

10. Emulation framework and CP15 emulation
11. Handle guest user memory aborts
12. Handle guest MMIO aborts
13. Support guest wait-for-interrupt instructions

Testing:
Limited testing, but have run GCC inside guest, which compiled a small hello-world program, which was successfully run. For v8 both ARM/Thumb-2 kernels were tested as both host/guest and both a compiled-in version and a kernel module version of KVM was tested. Hardware still unavailable to me, so all testing has been done on ARM Fast Models.

Quoted 6 lines

For a guide on how to set up a testing environment and try out these patches, see:
http://www.virtualopensystems.com/media/pdf/kvm-arm-guide.pdf

There is an issue list available using the issue tracker on:
https://github.com/virtualopensystems/linux-kvm-arm

Is there public documentation for "hyp-mode" available?

#53 lore raw
Quoted 3 lines

Is there public documentation for "hyp-mode" available?

yes, you have to register on the ARM website
(http://infocenter.arm.com) but there you can download the ARM v7 architecture reference manual.