KVM/ARM Implementation
Subsystems (7)
v10 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:
dbcb4e798072d114fe68813f39a9efd239ab99c0
This is Version 10 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-v10
A non-flattened edition of the patch series can be found at:
git://github.com/virtualopensystems/linux-kvm-arm.git kvm-a15-v10-stage
WARNING: This patch series release breaks compatibility with QEMU as it worked with kvm-a15-v9 due to the new reset and set target API. Please use the latest Linaro master branch (or the mirror from here):
git://github.com/virtualopensystems/qemu.git kvm-a15-v10
The implementation is broken up into a logical set of patches, the first are preparatory patches:
1. ARM: Add mem_type prot_pte accessor
2. ARM: ARM_VIRT_EXT config option
3. ARM: Section based HYP idmaps
4. ARM: Expose PMNC bitfields for KVM use
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:
5. Skeleton and reset hooks
6. Hypervisor initialization
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 coproc emulation
11. Coproc user space API
12. Handle guest user memory aborts
13. Handle guest MMIO aborts
14. 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 v10 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
Additionally a few major milestones are coming up shortly:
- Support Thumb MMIO emulation and test MMIO emulation code (under way)
- 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. (review of patches from Marc Zyngier underway)
Changes since v9:
- Addressed reviewer comments (see mailing list archive)
- Limit the user of .arch_extensiion sec/virt for compilers that need them
- VFP/Neon Support (Antonios Motakis)
- Run exit handling under preemption and still handle guest cache ops
- Add support for IO mapping at Hyp level (VGIC prep)
- Add support for IO mapping at Guest level (VGIC prep)
- Remove backdoor call to irq_svc
- Complete rework of CP15 handling and register reset (Rusty Russell)
- Don't use HSTR for anything else than CR 15
- New ioctl to set emulation target core (only A15 supported for now)
- Support KVM_GET_MSRS / KVM_SET_MSRS
- Add page accounting and page table eviction
- Change pgd lock to spinlock and fix sleeping in atomic bugs
- Check kvm_condition_valid for HVC traps of undefs
- Added a naive implementation of kvm_unmap_hva_range
Changes since v8:
- Support cache maintenance on SMP through set/way
- Hyp mode idmaps are now section based and happen at kernel init
- Handle aborts in Hyp mode
- Inject undefined exceptions into the guest on error
- Kernel-side reset of all crucial registers
- Specifically state which target CPU is being virtualized
- Exit statistics in debugfs
- Some L2CTLR cp15 emulation cleanups
- Support spte_hva for MMU notifiers and take write faults
- FIX: Race condition in VMID generation
- BUG: Run exit handling code with disabled preemption
- Save/Restore abort fault register during world switch
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):
ARM: Add config option ARM_VIRT_EXT
ARM: Section based HYP idmap
KVM: ARM: Initial skeleton to compile KVM support
KVM: ARM: Hypervisor inititalization
KVM: ARM: Memory virtualization setup
KVM: ARM: Inject IRQs and FIQs from userspace
KVM: ARM: World-switch implementation
KVM: ARM: Emulation framework and CP15 emulation
KVM: ARM: User space API for getting/setting co-proc registers
KVM: ARM: Handle guest faults in KVM
KVM: ARM: Handle I/O aborts
KVM: ARM: Guest wait-for-interrupts (WFI) support
Marc Zyngier (1):
ARM: add mem_type prot_pte accessor
Rusty Russell (1):
ARM: Expose PMNC bitfields for KVM use
Documentation/virtual/kvm/api.txt | 85 ++ arch/arm/Kconfig | 2 arch/arm/Makefile | 1 arch/arm/include/asm/idmap.h | 7 arch/arm/include/asm/kvm.h | 119 +++ arch/arm/include/asm/kvm_arm.h | 197 +++++ arch/arm/include/asm/kvm_asm.h | 59 ++ arch/arm/include/asm/kvm_coproc.h | 38 + arch/arm/include/asm/kvm_emulate.h | 115 +++ arch/arm/include/asm/kvm_host.h | 193 +++++ arch/arm/include/asm/kvm_mmu.h | 46 + arch/arm/include/asm/mach/map.h | 1 arch/arm/include/asm/perf_bits.h | 56 ++ arch/arm/include/asm/pgtable-3level-hwdef.h | 5 arch/arm/include/asm/pgtable-3level.h | 13 arch/arm/include/asm/pgtable.h | 5 arch/arm/kernel/asm-offsets.c | 45 + arch/arm/kernel/perf_event_v7.c | 51 - arch/arm/kernel/vmlinux.lds.S | 6 arch/arm/kvm/Kconfig | 45 + arch/arm/kvm/Makefile | 23 + arch/arm/kvm/arm.c | 991 +++++++++++++++++++++++++++ arch/arm/kvm/coproc.c | 962 ++++++++++++++++++++++++++ arch/arm/kvm/emulate.c | 531 ++++++++++++++ arch/arm/kvm/exports.c | 38 + arch/arm/kvm/guest.c | 163 ++++ arch/arm/kvm/init.S | 149 ++++ arch/arm/kvm/interrupts.S | 782 +++++++++++++++++++++ arch/arm/kvm/mmu.c | 837 +++++++++++++++++++++++ arch/arm/kvm/reset.c | 74 ++ arch/arm/kvm/trace.h | 117 +++ arch/arm/mm/Kconfig | 10 arch/arm/mm/idmap.c | 88 ++ arch/arm/mm/mmu.c | 9 include/linux/kvm.h | 3 mm/memory.c | 2 36 files changed, 5798 insertions(+), 70 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_coproc.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/include/asm/perf_bits.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/coproc.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/reset.c create mode 100644 arch/arm/kvm/trace.h
PATCH v10 1/14 ARM: add mem_type prot_pte accessor
From: Marc Zyngier <[email protected]>
The KVM hypervisor mmu code 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
@@ -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
@@ -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. */
PATCH v10 2/14 ARM: Add config option ARM_VIRT_EXT
In reply to Christoffer DallSelect this option for ARM processors equipped with hardware Virtualization Extensions.
--- arch/arm/mm/Kconfig | 10 ++++++++++ 1 file changed, 10 insertions(+)
arch/arm/mm/Kconfig +10 -0
@@ -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
PATCH v10 3/14 ARM: Section based HYP idmap
In reply to Christoffer DallAdd a HYP pgd to the core code (so it can benefit all Linux hypervisors).
Populate this pgd with an identity mapping of the code contained in the .hyp.idmap.text section
Offer a method to drop the this identity mapping through hyp_idmap_teardown and re-create it through hyp_idmap_setup.
Make all the above depend on CONFIG_ARM_VIRT_EXT
--- arch/arm/include/asm/idmap.h | 7 ++ arch/arm/include/asm/pgtable-3level-hwdef.h | 1 arch/arm/kernel/vmlinux.lds.S | 6 ++ arch/arm/mm/idmap.c | 88 +++++++++++++++++++++++---- 4 files changed, 89 insertions(+), 13 deletions(-)
arch/arm/include/asm/idmap.h +7 -0
@@ -11,4 +11,11 @@ extern pgd_t *idmap_pgd;void setup_mm_for_reboot (void ); +#ifdef CONFIG_ARM_VIRT_EXT +extern pgd_t *hyp_pgd; + +void hyp_idmap_teardown (void ); +void hyp_idmap_setup (void ); +#endif +#endif /* __ASM_IDMAP_H */
arch/arm/include/asm/pgtable-3level-hwdef.h +1 -0
@@ -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/kernel/vmlinux.lds.S +5 -1
@@ -19,7 +19,11 @@ ALIGN_FUNCTION(); \ VMLINUX_SYMBOL(__idmap_text_start) = .; \ *(.idmap.text) \ - VMLINUX_SYMBOL(__idmap_text_end) = .; + VMLINUX_SYMBOL(__idmap_text_end) = .; \ + ALIGN_FUNCTION(); \ + VMLINUX_SYMBOL(__hyp_idmap_text_start) = .; \ + *(.hyp.idmap.text) \ + VMLINUX_SYMBOL(__hyp_idmap_text_end) = .; #ifdef CONFIG_HOTPLUG_CPU #define ARM_CPU_DISCARD(x)
arch/arm/mm/idmap.c +76 -12
115 lines not on this page · raw message
PATCH v10 4/14 ARM: Expose PMNC bitfields for KVM use
In reply to Christoffer DallFrom: Rusty Russell <[email protected]>
We want some of these for use in KVM, so pull them out of arch/arm/kernel/perf_event_v7.c into their own asm/perf_bits.h.
--- arch/arm/include/asm/perf_bits.h | 56 ++++++++++++++++++++++++++++++++++++++ arch/arm/kernel/perf_event_v7.c | 51 +---------------------------------- 2 files changed, 57 insertions(+), 50 deletions(-) create mode 100644 arch/arm/include/asm/perf_bits.h
arch/arm/include/asm/perf_bits.h new file +56 -0
@@ -0,0 +1,56 @@ +#ifndef __ARM_PERF_BITS_H__ +#define __ARM_PERF_BITS_H__ + +/* +* ARMv7 low level PMNC access +*/ + +/* +* Per-CPU PMNC: config reg +*/ +#define ARMV7_PMNC_E (1 << 0)/* Enable all counters */ +#define ARMV7_PMNC_P (1 << 1)/* Reset all counters */ +#define ARMV7_PMNC_C (1 << 2)/* Cycle counter reset */ +#define ARMV7_PMNC_D (1 << 3)/* CCNT counts every 64th cpu cycle */ +#define ARMV7_PMNC_X (1 << 4)/* Export to ETM */ +#define ARMV7_PMNC_DP (1 << 5)/* Disable CCNT if non-invasive debug*/ +#define ARMV7_PMNC_N_SHIFT 11/* Number of counters supported */ +#define ARMV7_PMNC_N_MASK 0x1f +#define ARMV7_PMNC_MASK 0x3f/* Mask for writable bits */ + +/* +* FLAG: counters overflow flag status reg +*/ +#define ARMV7_FLAG_MASK 0xffffffff/* Mask for writable bits */ +#define ARMV7_OVERFLOWED_MASK ARMV7_FLAG_MASK + +/* +* PMXEVTYPER: Event selection reg +*/ +#define ARMV7_EVTYPE_MASK 0xc00000ff/* Mask for writable bits */ +#define ARMV7_EVTYPE_EVENT 0xff/* Mask for EVENT bits */ + +/* +* Event filters for PMUv2 +*/ +#define ARMV7_EXCLUDE_PL1 (1 << 31) +#define ARMV7_EXCLUDE_USER (1 << 30) +#define ARMV7_INCLUDE_HYP (1 << 27) + +#ifndef __ASSEMBLY__ +static inline u32 armv7_pmnc_read (void ) +{ +u32 val; + asmvolatile ("mrc p15, 0, %0, c9, c12, 0" :"=r" (val)); +return val; +} + +static inline void armv7_pmnc_write (u32 val) +{ + val &=ARMV7_PMNC_MASK ; +isb (); + asmvolatile ("mcr p15, 0, %0, c9, c12, 0" : :"r" (val)); +} +#endif + +#endif /* __ARM_PERF_BITS_H__ */
arch/arm/kernel/perf_event_v7.c +1 -50
68 lines not on this page · raw message
PATCH v10 5/14 KVM: ARM: Initial skeleton to compile KVM support
In reply to Christoffer DallTargets KVM support for Cortex A-15 processors.
Contains all the framework components, make files, header files and some tracing functionality.
Only supported core is Cortex-A15 for now.
Contains minor reset hook driven from kvm_vcpu_set_target, which will eventually be a custom ARM ioctl to set the core we are emulating.
Most functionality is in arch/arm/kvm/* or arch/arm/include/asm/kvm_*.h.
“Nothing to see here. Move along, move along..."
--- arch/arm/Kconfig | 2 arch/arm/Makefile | 1 arch/arm/include/asm/kvm.h | 79 +++++++++ arch/arm/include/asm/kvm_arm.h | 28 +++ arch/arm/include/asm/kvm_asm.h | 30 +++ arch/arm/include/asm/kvm_coproc.h | 24 +++ arch/arm/include/asm/kvm_emulate.h | 108 ++++++++++++ arch/arm/include/asm/kvm_host.h | 160 ++++++++++++++++++ arch/arm/kvm/Kconfig | 44 +++++ arch/arm/kvm/Makefile | 23 +++ arch/arm/kvm/arm.c | 317 ++++++++++++++++++++++++++++++++++++ arch/arm/kvm/coproc.c | 22 ++ arch/arm/kvm/emulate.c | 127 ++++++++++++++ arch/arm/kvm/exports.c | 21 ++ arch/arm/kvm/guest.c | 163 +++++++++++++++++++ arch/arm/kvm/init.S | 19 ++ arch/arm/kvm/interrupts.S | 19 ++ arch/arm/kvm/mmu.c | 17 ++ arch/arm/kvm/reset.c | 74 ++++++++ arch/arm/kvm/trace.h | 52 ++++++ include/linux/kvm.h | 1 21 files changed, 1331 insertions(+) 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_coproc.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/coproc.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/reset.c create mode 100644 arch/arm/kvm/trace.h
arch/arm/Kconfig +2 -0
@@ -2341,3 +2341,5 @@ source "security/Kconfig" source "crypto/Kconfig" source "lib/Kconfig" + +source "arch/arm/kvm/Kconfig"
arch/arm/Makefile +1 -0
@@ -255,6 +255,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 +79 -0
79 lines not on this page · raw message
arch/arm/include/asm/kvm_arm.h new file +28 -0
@@ -0,0 +1,28 @@ +/* +* Copyright (C) 2012 - Virtual Open Systems and Columbia University +* Author: Christoffer Dall <[email protected]> +* +* 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_ARM_H__ +#define __ARM_KVM_ARM_H__ + +/* Supported Processor Types */ +#define CORTEX_A15 (0xC0F) + +/* Multiprocessor Affinity Register */ +#define MPIDR_CPUID (0x3 << 0) + +#endif /* __ARM_KVM_ARM_H__ */
arch/arm/include/asm/kvm_asm.h new file +30 -0
@@ -0,0 +1,30 @@ +/* +* Copyright (C) 2012 - Virtual Open Systems and Columbia University +* Author: Christoffer Dall <[email protected]> +* +* 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_coproc.h new file +24 -0
@@ -0,0 +1,24 @@ +/* +* Copyright (C) 2012 Rusty Russell IBM Corporation +* +* 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_COPROC_H__ +#define __ARM_KVM_COPROC_H__ +#include <linux/kvm_host.h> + +void kvm_reset_coprocs (struct kvm_vcpu *vcpu); + +#endif /* __ARM_KVM_COPROC_H__ */
arch/arm/include/asm/kvm_emulate.h new file +108 -0
108 lines not on this page · raw message
arch/arm/include/asm/kvm_host.h new file +160 -0
160 lines not on this page · raw message
arch/arm/kvm/Kconfig new file +44 -0
44 lines not on this page · raw message
arch/arm/kvm/Makefile new file +23 -0
@@ -0,0 +1,23 @@ +# +# Makefile for Kernel-based Virtual Machine module +# + +plus_virt := $(call as-instr,.arch_extension virt,+virt) +ifeq ($(plus_virt),+virt) + plus_virt_def := -DREQUIRES_VIRT=1 +endif + +ccflags-y += -Ivirt/kvm -Iarch/arm/kvm +CFLAGS_arm.o := -I. $(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) $(plus_virt_def) +CFLAGS_mmu.o := -I. + +AFLAGS_init.o := -Wa,-march=armv7-a$(plus_virt) +AFLAGS_interrupts.o := -Wa,-march=armv7-a$(plus_virt) + +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 reset.o coproc.o + +obj-$(CONFIG_KVM) += kvm-arm.o
arch/arm/kvm/arm.c new file +317 -0
317 lines not on this page · raw message
arch/arm/kvm/coproc.c new file +22 -0
@@ -0,0 +1,22 @@ +/* +* Copyright (C) 2012 - Virtual Open Systems and Columbia University +* Author: Christoffer Dall <[email protected]> +* +* 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/kvm_host.h> + +void kvm_reset_coprocs (struct kvm_vcpu *vcpu) +{ +}
arch/arm/kvm/emulate.c new file +127 -0
127 lines not on this page · raw message
arch/arm/kvm/exports.c new file +21 -0
@@ -0,0 +1,21 @@ +/* +* Copyright (C) 2012 - Virtual Open Systems and Columbia University +* Author: Christoffer Dall <[email protected]> +* +* 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> + +EXPORT_SYMBOL_GPL (smp_send_reschedule);
arch/arm/kvm/guest.c new file +163 -0
163 lines not on this page · raw message
arch/arm/kvm/init.S new file +19 -0
@@ -0,0 +1,19 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall <[email protected]> + * + * 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 +19 -0
@@ -0,0 +1,19 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall <[email protected]> + * + * 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 +17 -0
@@ -0,0 +1,17 @@ +/* +* Copyright (C) 2012 - Virtual Open Systems and Columbia University +* Author: Christoffer Dall <[email protected]> +* +* 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/reset.c new file +74 -0
74 lines not on this page · raw message
arch/arm/kvm/trace.h new file +52 -0
52 lines not on this page · raw message
include/linux/kvm.h +1 -0
@@ -904,6 +904,7 @@ struct kvm_s390_ucas_mapping {#define KVM_SET_ONE_REG _IOW(KVMIO, 0xac, struct kvm_one_reg)/* VM is being stopped by host */ #define KVM_KVMCLOCK_CTRL _IO(KVMIO, 0xad) +#define KVM_ARM_VCPU_INIT _IOW(KVMIO, 0xae, struct kvm_vcpu_init)#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)
PATCH v10 6/14 KVM: ARM: Hypervisor inititalization
In reply to Christoffer DallSets 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, data structures, and I/O regions 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(from, to);
- create_hyp_io_mappings(from, to, phys_addr);
- free_hyp_pmds();
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. We are working on supporting Hyp mode boot of the kernel and control of Hyp mode through a local kernel mechanism.
--- arch/arm/include/asm/kvm_arm.h | 109 +++++++++++++ arch/arm/include/asm/kvm_asm.h | 25 +++ arch/arm/include/asm/kvm_mmu.h | 36 ++++ 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 | 224 +++++++++++++++++++++++++++ arch/arm/kvm/exports.c | 16 ++ arch/arm/kvm/init.S | 130 ++++++++++++++++ arch/arm/kvm/interrupts.S | 48 ++++++ arch/arm/kvm/mmu.c | 189 +++++++++++++++++++++++ mm/memory.c | 2 12 files changed, 788 insertions(+) create mode 100644 arch/arm/include/asm/kvm_mmu.h
arch/arm/include/asm/kvm_arm.h +109 -0
@@ -19,10 +19,119 @@#ifndef __ARM_KVM_ARM_H__#define __ARM_KVM_ARM_H__ +#include <asm/types.h> +/* Supported Processor Types */ #define CORTEX_A15 (0xC0F)/* Multiprocessor Affinity Register */ #define MPIDR_CPUID (0x3 << 0) +/* 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 +* TSW: Trap cache operations by set/way +* TWI: Trap WFI +* TIDCP: Trap L2CTLR/L2ECTLR +* 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_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \ + HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \ + HCR_SWIO | HCR_TIDCP) + +/* 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) + +/* Hyp Debug Configuration Register bits */ +#define HDCR_TDRA (1 << 11) +#define HDCR_TDOSA (1 << 10) +#define HDCR_TDA (1 << 9) +#define HDCR_TDE (1 << 8) +#define HDCR_HPME (1 << 7) +#define HDCR_TPM (1 << 6) +#define HDCR_TPMCR (1 << 5) +#define HDCR_HPMN_MASK (0x1F) + +/* 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 /* __ARM_KVM_ARM_H__ */
arch/arm/include/asm/kvm_asm.h +25 -0
@@ -26,5 +26,30 @@#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_exit[]; +extern char __kvm_hyp_exit_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 +36 -0
@@ -0,0 +1,36 @@ +/* +* Copyright (C) 2012 - Virtual Open Systems and Columbia University +* Author: Christoffer Dall <[email protected]> +* +* 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); +int create_hyp_io_mappings (void *from,void *to,phys_addr_t ); +void free_hyp_pmds (void ); + +#endif /* __ARM_KVM_MMU_H__ */
arch/arm/include/asm/pgtable-3level-hwdef.h +4 -0
@@ -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
@@ -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
@@ -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 +224 -0
249 lines not on this page · raw message
arch/arm/kvm/exports.c +16 -0
@@ -17,5 +17,21 @@*/ #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_exit); +EXPORT_SYMBOL_GPL (__kvm_hyp_exit_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 +130 -0
135 lines not on this page · raw message
arch/arm/kvm/interrupts.S +48 -0
53 lines not on this page · raw message
arch/arm/kvm/mmu.c +189 -0
192 lines not on this page · raw message
mm/memory.c +2 -0
@@ -383,12 +383,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) {
Re: [kvmarm] [PATCH v10 06/14] KVM: ARM: Hypervisor inititalization
Hi Christoffer,
[snip]
Quoted 35 lines
arch/arm/kvm/init.S +25
-0@@ -15,5 +15,135 @@ * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <linux/linkage.h> +#include <asm/unified.h> #include <asm/asm-offsets.h> #include <asm/kvm_asm.h> +#include <asm/kvm_arm.h> + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Hypervisor initialization +@ - should be called with: +@ r0 = Hypervisor pgd pointer +@ r1 = top of Hyp stack (kernel VA) +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + .text + .arm + .pushsection .hyp.idmap.text,"ax" + .align 12 +__kvm_hyp_init: + .globl __kvm_hyp_init + + @ Hyp-mode exception vector + nop + nop + nop + nop + nop + b __do_hyp_init
arch/arm/kvm/init.S line 54
Quoted 10 lines
arch/arm/kvm/init.S +10
-0+ nop + nop + +__do_hyp_init: + @ Set the sp to end of this page and push data for later use + mov sp, pc + bic sp, sp, #0x0ff + bic sp, sp, #0xf00 + add sp, sp, #0x1000 + push {r0, r1, r2, r12}
Is safe to use the stack here? Since our HYP memory mapping is gotten valid after the following HTTBR being set as I think.
arch/arm/kvm/init.S line 60
Quoted 6 lines
arch/arm/kvm/init.S +6
-0+ + @ Set the HTTBR to point to the hypervisor PGD pointer passed to + @ function and set the upper bits equal to the kernel PGD. + mrrc p15, 1, r1, r2, c2 + mcrr p15, 4, r0, r2, c2 +
[snip]
Thanks,
Lei
Re: [kvmarm] [PATCH v10 06/14] KVM: ARM: Hypervisor inititalization
Quoted 44 lines · Lei Wen
Hi Christoffer,
[snip]
Quoted 40 lines
arch/arm/kvm/init.S +30
-0@@ -15,5 +15,135 @@ * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <linux/linkage.h> +#include <asm/unified.h> #include <asm/asm-offsets.h> #include <asm/kvm_asm.h> +#include <asm/kvm_arm.h> + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Hypervisor initialization +@ - should be called with: +@ r0 = Hypervisor pgd pointer +@ r1 = top of Hyp stack (kernel VA) +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + .text + .arm + .pushsection .hyp.idmap.text,"ax" + .align 12 +__kvm_hyp_init: + .globl __kvm_hyp_init + + @ Hyp-mode exception vector + nop + nop + nop + nop + nop + b __do_hyp_init + nop + nop + +__do_hyp_init: + @ Set the sp to end of this page and push data for later use
Quoted 9 lines
arch/arm/kvm/init.S line 54
Quoted 5 lines
arch/arm/kvm/init.S +5
-0+ mov sp, pc + bic sp, sp, #0x0ff + bic sp, sp, #0xf00 + add sp, sp, #0x1000 + push {r0, r1, r2, r12}Is safe to use the stack here? Since our HYP memory mapping is gotten valid after the following HTTBR being set as I think.
yes, as you can see in the end of this block we have a .align 12 before __kvm_hyp_init_end giving us the stack space we need in this page (as long as this init code doesn't grow beyond ~3K
Quoted 11 lines
arch/arm/kvm/init.S line 60
Quoted 6 lines
arch/arm/kvm/init.S +6
-0+ + @ Set the HTTBR to point to the hypervisor PGD pointer passed to + @ function and set the upper bits equal to the kernel PGD. + mrrc p15, 1, r1, r2, c2 + mcrr p15, 4, r0, r2, c2 +[snip]
Thanks,
Lei
Re: [kvmarm] [PATCH v10 06/14] KVM: ARM: Hypervisor inititalization
Quoted 55 lines · Christoffer Dall
Quoted 44 lines · Lei Wen
Hi Christoffer,
[snip]
Quoted 40 lines
arch/arm/kvm/init.S +30
-0@@ -15,5 +15,135 @@ * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <linux/linkage.h> +#include <asm/unified.h> #include <asm/asm-offsets.h> #include <asm/kvm_asm.h> +#include <asm/kvm_arm.h> + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Hypervisor initialization +@ - should be called with: +@ r0 = Hypervisor pgd pointer +@ r1 = top of Hyp stack (kernel VA) +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + .text + .arm + .pushsection .hyp.idmap.text,"ax" + .align 12 +__kvm_hyp_init: + .globl __kvm_hyp_init + + @ Hyp-mode exception vector + nop + nop + nop + nop + nop + b __do_hyp_init + nop + nop + +__do_hyp_init: + @ Set the sp to end of this page and push data for later useQuoted 9 lines
arch/arm/kvm/init.S line 54
Quoted 5 lines
arch/arm/kvm/init.S +5
-0+ mov sp, pc + bic sp, sp, #0x0ff + bic sp, sp, #0xf00 + add sp, sp, #0x1000 + push {r0, r1, r2, r12}Is safe to use the stack here? Since our HYP memory mapping is gotten valid after the following HTTBR being set as I think.
Quoted 3 lines
yes, as you can see in the end of this block we have a .align 12 before
__kvm_hyp_init_endgiving us the stack space we need in this page (as long as this init code doesn't grow beyond ~3K
I know this area is already being mapped in hyp_init_static_idmap, however, due to the HTTBR is not being set up yet in the below code. So how the memory get mapped in the hyp mode? I mean what I could understand the hyp virtual address only could be seen after HTTBR being set up.
Quoted 11 lines
Quoted 11 lines
Quoted 9 lines
arch/arm/kvm/init.S +6
-0+ + @ Set the HTTBR to point to the hypervisor PGD pointer passed to + @ function and set the upper bits equal to the kernel PGD. + mrrc p15, 1, r1, r2, c2 + mcrr p15, 4, r0, r2, c2 +[snip]
Thanks,
Lei
Re: [kvmarm] [PATCH v10 06/14] KVM: ARM: Hypervisor inititalization
Quoted 3 lines · Lei Wen
Quoted 1 line · Christoffer Dall
On Thu, Aug 23, 2012 at 11:08 AM, Lei Wen <[email protected]>
Quoted 13 lines · wrote:
Quoted 13 lines
Quoted 13 lines
Hi Christoffer,
[snip]
Quoted 9 lines
arch/arm/kvm/init.S +0
-0@@ -15,5 +15,135 @@ * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
arch/arm/kvm/init.S line 24
Quoted 8 lines
arch/arm/kvm/init.S +5
-0+ +#include <linux/linkage.h> +#include <asm/unified.h> #include <asm/asm-offsets.h> #include <asm/kvm_asm.h> +#include <asm/kvm_arm.h> +
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
+@ Hypervisor initialization
+@ - should be called with:
+@ r0 = Hypervisor pgd pointer
+@ r1 = top of Hyp stack (kernel VA)
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Quoted 33 lines
Quoted 29 lines
Quoted 19 lines
Quoted 19 lines
arch/arm/kvm/init.S +19
-0+ .text + .arm + .pushsection .hyp.idmap.text,"ax" + .align 12 +__kvm_hyp_init: + .globl __kvm_hyp_init + + @ Hyp-mode exception vector + nop + nop + nop + nop + nop + b __do_hyp_init + nop + nop + +__do_hyp_init: + @ Set the sp to end of this page and push data for later useQuoted 9 lines
arch/arm/kvm/init.S line 54
Quoted 5 lines
arch/arm/kvm/init.S +5
-0+ mov sp, pc + bic sp, sp, #0x0ff + bic sp, sp, #0xf00 + add sp, sp, #0x1000 + push {r0, r1, r2, r12}Is safe to use the stack here? Since our HYP memory mapping is gotten valid after the following HTTBR being set as I think.
Quoted 3 lines
yes, as you can see in the end of this block we have a .align 12 before
__kvm_hyp_init_endgiving us the stack space we need in this page (as long as this init code doesn't grow beyond ~3K
Quoted 5 lines
I know this area is already being mapped in
hyp_init_static_idmap, however, due to the HTTBR is not being set up yet in the below code. So how the memory get mapped in the hyp mode? I mean what I could understand the hyp virtual address only could be seen after HTTBR being set up.
Hint: look at HSCTLR, and when the M bit gets set. Until then, the words "virtual address" have no meaning.
M.
Signature · 1 line
Fast, cheap, reliable. Pick two.
Re: [kvmarm] [PATCH v10 06/14] KVM: ARM: Hypervisor inititalization
Hi Marc,
Quoted 66 lines · Marc Zyngier
Quoted 3 lines · Lei Wen
Quoted 1 line · Christoffer Dall
On Thu, Aug 23, 2012 at 11:08 AM, Lei Wen <[email protected]>
Quoted 13 lines · wrote:
Quoted 13 lines
Quoted 13 lines
Hi Christoffer,
[snip]
Quoted 9 lines
arch/arm/kvm/init.S +0
-0@@ -15,5 +15,135 @@ * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
arch/arm/kvm/init.S line 24
Quoted 8 lines
arch/arm/kvm/init.S +5
-0+ +#include <linux/linkage.h> +#include <asm/unified.h> #include <asm/asm-offsets.h> #include <asm/kvm_asm.h> +#include <asm/kvm_arm.h> ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
+@ Hypervisor initialization
+@ - should be called with:
+@ r0 = Hypervisor pgd pointer
+@ r1 = top of Hyp stack (kernel VA)+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Quoted 33 lines
Quoted 32 lines
Quoted 19 lines
Quoted 19 lines
arch/arm/kvm/init.S +19
-0+ .text + .arm + .pushsection .hyp.idmap.text,"ax" + .align 12 +__kvm_hyp_init: + .globl __kvm_hyp_init + + @ Hyp-mode exception vector + nop + nop + nop + nop + nop + b __do_hyp_init + nop + nop + +__do_hyp_init: + @ Set the sp to end of this page and push data for later useQuoted 9 lines
arch/arm/kvm/init.S line 54
Quoted 5 lines
arch/arm/kvm/init.S +5
-0+ mov sp, pc + bic sp, sp, #0x0ff + bic sp, sp, #0xf00 + add sp, sp, #0x1000 + push {r0, r1, r2, r12}Is safe to use the stack here? Since our HYP memory mapping is gotten valid after the following HTTBR being set as I think.
yes, as you can see in the end of this block we have a .align 12 before
__kvm_hyp_init_endgiving us the stack space we need in this page (as long as this init code doesn't grow beyond ~3K
Quoted 8 lines
Quoted 5 lines
I know this area is already being mapped in
hyp_init_static_idmap, however, due to the HTTBR is not being set up yet in the below code. So how the memory get mapped in the hyp mode? I mean what I could understand the hyp virtual address only could be seen after HTTBR being set up.Hint: look at HSCTLR, and when the M bit gets set. Until then, the words "virtual address" have no meaning.
I see. You means that when at the first push operation, it directly deal with physical memory, mapping is meaningless at that moment. After MMU on, for that piece of code being mapped as identical one, the sp operation also get no problem.
Yep, that solve my confusion.
Thanks,
Lei
PATCH v10 7/14 KVM: ARM: Memory virtualization setup
In reply to Christoffer DallThis 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);
Introduces new ARM-specific kernel memory types, PAGE_KVM_GUEST and pgprot_guest variables used to map 2nd stage memory for KVM guests.
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.
We pre-allocate page table memory to be able to synchronize using a spinlock and be called under rcu_read_lock from the MMU notifiers. We steal the mmu_memory_cache implementation from x86 and adapt for our specific usage.
We support MMU notifiers (thanks to Marc Zyngier) through kvm_unmap_hva and kvm_set_spte_hva.
Finally, define kvm_phys_addr_ioremap() to map a device at a guest IPA, which is used by VGIC support to map the virtual CPU interface registers to the guest. This support is added by Marc Zyngier.
--- arch/arm/include/asm/kvm_asm.h | 2 arch/arm/include/asm/kvm_host.h | 18 ++ arch/arm/include/asm/kvm_mmu.h | 9 + arch/arm/include/asm/pgtable-3level.h | 9 + arch/arm/include/asm/pgtable.h | 4 arch/arm/kvm/Kconfig | 1 arch/arm/kvm/arm.c | 38 +++ arch/arm/kvm/exports.c | 1 arch/arm/kvm/interrupts.S | 8 + arch/arm/kvm/mmu.c | 373 +++++++++++++++++++++++++++++++++ arch/arm/mm/mmu.c | 3 11 files changed, 465 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_asm.h +2 -0
@@ -34,6 +34,7 @@#define SMCHYP_HVBAR_W 0xfffffff0#ifndef __ASSEMBLY__ +struct kvm ;struct kvm_vcpu ;extern char __kvm_hyp_init[];
@@ -48,6 +49,7 @@ extern char __kvm_hyp_code_start[];extern char __kvm_hyp_code_end[];extern void __kvm_flush_vm_context (void ); +extern void __kvm_tlb_flush_vmid (struct kvm *kvm);extern int __kvm_vcpu_run (struct kvm_vcpu *vcpu);#endif
arch/arm/include/asm/kvm_host.h +18 -0
@@ -157,4 +157,22 @@ struct kvm_vcpu_stat {struct kvm_vcpu_init ;int kvm_vcpu_set_target (struct kvm_vcpu *vcpu,const struct kvm_vcpu_init *init); + +#define KVM_ARCH_WANT_MMU_NOTIFIER +struct kvm ; +int kvm_unmap_hva (struct kvm *kvm,unsigned long hva); +int kvm_unmap_hva_range (struct kvm *kvm, +unsigned long start,unsigned long end); +void kvm_set_spte_hva (struct kvm *kvm,unsigned long hva,pte_t pte); + +/* 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 ; +}#endif /* __ARM_KVM_HOST_H__ */
arch/arm/include/asm/kvm_mmu.h +9 -0
@@ -33,4 +33,13 @@ int create_hyp_mappings(void *from, void *to);int create_hyp_io_mappings (void *from,void *to,phys_addr_t );void free_hyp_pmds (void ); +int kvm_alloc_stage2_pgd (struct kvm *kvm); +void kvm_free_stage2_pgd (struct kvm *kvm); +int kvm_phys_addr_ioremap (struct kvm *kvm,phys_addr_t guest_ipa, +phys_addr_t pa,unsigned long size); + +int kvm_handle_guest_abort (struct kvm_vcpu *vcpu,struct kvm_run *run); + +void kvm_mmu_free_memory_caches (struct kvm_vcpu *vcpu); +#endif /* __ARM_KVM_MMU_H__ */
arch/arm/include/asm/pgtable-3level.h +9 -0
@@ -102,6 +102,15 @@*/ #define L_PGD_SWAPPER (_AT(pgdval_t, 1) << 55)/* swapper_pg_dir entry */ +/* +* 2-nd stage PTE definitions for LPAE. +*/ +#define L_PTE2_SHARED L_PTE_SHARED +#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
@@ -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_NORM_WB | L_PTE2_INNER_WB | \ + L_PTE2_SHARED)#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
@@ -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/arm.c +37 -1
@@ -82,12 +82,34 @@ void kvm_arch_sync_events(struct kvm *kvm) { } +/** +* 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 ; -return 0 ; + ret =kvm_alloc_stage2_pgd (kvm); +if (ret) + goto out_fail_alloc; +spin_lock_init (&kvm->arch.pgd_lock); + + ret =create_hyp_mappings (kvm, kvm +1 ); +if (ret) + goto out_free_stage2_pgd; + +/* Mark the initial VMID generation invalid */ + kvm->arch.vmid_gen =0 ; + +return ret; +out_free_stage2_pgd: +kvm_free_stage2_pgd (kvm); +out_fail_alloc: +return ret; }int kvm_arch_vcpu_fault (struct kvm_vcpu *vcpu,struct vm_fault *vmf)
@@ -105,10 +127,16 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)return 0 ; } +/** +* 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]);
@@ -184,7 +212,13 @@ 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 vcpu_uninit; +return vcpu; +vcpu_uninit: +kvm_vcpu_uninit (vcpu); free_vcpu:kmem_cache_free (kvm_vcpu_cache, vcpu); out:
@@ -193,6 +227,8 @@ out:void kvm_arch_vcpu_free (struct kvm_vcpu *vcpu) { +kvm_mmu_free_memory_caches (vcpu); +kmem_cache_free (kvm_vcpu_cache, vcpu); }void kvm_arch_vcpu_destroy (struct kvm_vcpu *vcpu)
arch/arm/kvm/exports.c +1 -0
@@ -33,5 +33,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 +8 -0
@@ -31,6 +31,14 @@ __kvm_hyp_code_start: .globl __kvm_hyp_code_start @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Flush per-VMID TLBs +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +ENTRY(__kvm_tlb_flush_vmid) + bx lr +ENDPROC(__kvm_tlb_flush_vmid) + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ Flush TLBs and instruction caches of current CPU for all VMIDs @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
arch/arm/kvm/mmu.c +373 -0
390 lines not on this page · raw message
arch/arm/mm/mmu.c +3 -0
@@ -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 ];
@@ -514,6 +516,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;
Re: [kvmarm] [PATCH v10 07/14] KVM: ARM: Memory virtualization setup
Quoted 586 lines · Christoffer Dall
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 inarch/arm/kvm/arm_mmu.c:
-kvm_alloc_stage2_pgd(struct kvm *kvm);
-kvm_free_stage2_pgd(struct kvm *kvm);Introduces new ARM-specific kernel memory types,
PAGE_KVM_GUESTandpgprot_guestvariables used to map 2nd stage memory for KVM guests.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 inkvm_arch_destroy_vm(). Both functions are called from the main KVM code.We pre-allocate page table memory to be able to synchronize using a spinlock and be called under
rcu_read_lockfrom the MMU notifiers. We steal themmu_memory_cacheimplementation from x86 and adapt for our specific usage.We support MMU notifiers (thanks to Marc Zyngier) through
kvm_unmap_hvaandkvm_set_spte_hva.Finally, define
kvm_phys_addr_ioremap()to map a device at a guest IPA, which is used by VGIC support to map the virtual CPU interface registers to the guest. This support is added by Marc Zyngier.Signed-off-by Marc Zyngier <[email protected]>Signed-off-by Christoffer Dall <[email protected]>---
arch/arm/include/asm/kvm_asm.h | 2 arch/arm/include/asm/kvm_host.h | 18 ++ arch/arm/include/asm/kvm_mmu.h | 9 + arch/arm/include/asm/pgtable-3level.h | 9 + arch/arm/include/asm/pgtable.h | 4 arch/arm/kvm/Kconfig | 1 arch/arm/kvm/arm.c | 38 +++ arch/arm/kvm/exports.c | 1 arch/arm/kvm/interrupts.S | 8 + arch/arm/kvm/mmu.c | 373 +++++++++++++++++++++++++++++++++ arch/arm/mm/mmu.c | 3 11 files changed, 465 insertions(+), 1 deletion(-)arch/arm/include/asm/kvm_asm.h +2
-0@@ -34,6 +34,7 @@#define SMCHYP_HVBAR_W 0xfffffff0#ifndef __ASSEMBLY__ +struct kvm ;struct kvm_vcpu ;extern char __kvm_hyp_init[];@@ -48,6 +49,7 @@ extern char __kvm_hyp_code_start[];extern char __kvm_hyp_code_end[];extern void __kvm_flush_vm_context (void ); +extern void __kvm_tlb_flush_vmid (struct kvm *kvm);extern int __kvm_vcpu_run (struct kvm_vcpu *vcpu);#endif arch/arm/include/asm/kvm_host.h +18
-0@@ -157,4 +157,22 @@ struct kvm_vcpu_stat {struct kvm_vcpu_init ;int kvm_vcpu_set_target (struct kvm_vcpu *vcpu,const struct kvm_vcpu_init *init); + +#define KVM_ARCH_WANT_MMU_NOTIFIER +struct kvm ; +int kvm_unmap_hva (struct kvm *kvm,unsigned long hva); +int kvm_unmap_hva_range (struct kvm *kvm, +unsigned long start,unsigned long end); +void kvm_set_spte_hva (struct kvm *kvm,unsigned long hva,pte_t pte); + +/* 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 ; +}#endif /* __ARM_KVM_HOST_H__ */ arch/arm/include/asm/kvm_mmu.h +9
-0@@ -33,4 +33,13 @@ int create_hyp_mappings(void *from, void *to);int create_hyp_io_mappings (void *from,void *to,phys_addr_t );void free_hyp_pmds (void ); +int kvm_alloc_stage2_pgd (struct kvm *kvm); +void kvm_free_stage2_pgd (struct kvm *kvm); +int kvm_phys_addr_ioremap (struct kvm *kvm,phys_addr_t guest_ipa, +phys_addr_t pa,unsigned long size); + +int kvm_handle_guest_abort (struct kvm_vcpu *vcpu,struct kvm_run *run); + +void kvm_mmu_free_memory_caches (struct kvm_vcpu *vcpu); +#endif /* __ARM_KVM_MMU_H__ */ arch/arm/include/asm/pgtable-3level.h +9
-0@@ -102,6 +102,15 @@*/ #define L_PGD_SWAPPER (_AT(pgdval_t, 1) << 55)/* swapper_pg_dir entry */ +/* +* 2-nd stage PTE definitions for LPAE. +*/ +#define L_PTE2_SHARED L_PTE_SHARED +#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@@ -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_NORM_WB | L_PTE2_INNER_WB | \ + L_PTE2_SHARED)#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@@ -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/arm.c +35
-1@@ -82,12 +82,34 @@ void kvm_arch_sync_events(struct kvm *kvm) { } +/** +* 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 ; -return 0 ; + ret =kvm_alloc_stage2_pgd (kvm); +if (ret) + goto out_fail_alloc; +spin_lock_init (&kvm->arch.pgd_lock); + + ret =create_hyp_mappings (kvm, kvm +1 ); +if (ret) + goto out_free_stage2_pgd; + +/* Mark the initial VMID generation invalid */ + kvm->arch.vmid_gen =0 ; + +return ret; +out_free_stage2_pgd: +kvm_free_stage2_pgd (kvm); +out_fail_alloc: +return ret; }int kvm_arch_vcpu_fault (struct kvm_vcpu *vcpu,struct vm_fault *vmf)@@ -105,10 +127,16 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)return 0 ; } +/** +* 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]);@@ -184,7 +212,13 @@ 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 vcpu_uninit; +return vcpu; +vcpu_uninit: +kvm_vcpu_uninit (vcpu);
free_vcpu:kmem_cache_free(kvm_vcpu_cache, vcpu);out:
arch/arm/kvm/arm.c +2
-0@@ -193,6 +227,8 @@ out:void kvm_arch_vcpu_free (struct kvm_vcpu *vcpu) { +kvm_mmu_free_memory_caches (vcpu); +kmem_cache_free (kvm_vcpu_cache, vcpu); }void kvm_arch_vcpu_destroy (struct kvm_vcpu *vcpu)arch/arm/kvm/exports.c +1
-0@@ -33,5 +33,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 +8
-0@@ -31,6 +31,14 @@ __kvm_hyp_code_start: .globl __kvm_hyp_code_start@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Flush per-VMID TLBs +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +ENTRY(__kvm_tlb_flush_vmid) + bx lr +ENDPROC(__kvm_tlb_flush_vmid) + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Flush TLBs and instruction caches of current CPU for all VMIDs
arch/arm/kvm/interrupts.S +0
-0@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@arch/arm/kvm/mmu.c +287
-0@@ -23,10 +23,43 @@#include <asm/pgalloc.h> #include <asm/kvm_arm.h> #include <asm/kvm_mmu.h> +#include <asm/kvm_asm.h> #include <asm/mach/map.h> static DEFINE_MUTEX (kvm_hyp_pgd_mutex); +static int mmu_topup_memory_cache (struct kvm_mmu_memory_cache *cache, +int min,int max) +{ +void *page; + +BUG_ON (max >KVM_NR_MEM_OBJS ); +if (cache->nobjs >= min) +return 0 ; +while (cache->nobjs < max) { + page = (void *)__get_free_page (PGALLOC_GFP ); +if (!page) +return -ENOMEM ; + cache->objects[cache->nobjs++] = page; + } +return 0 ; +} + +static void mmu_free_memory_cache (struct kvm_mmu_memory_cache *mc) +{ +while (mc->nobjs) +free_page ((unsigned long )mc->objects[--mc->nobjs]); +} + +static void *mmu_memory_cache_alloc (struct kvm_mmu_memory_cache *mc) +{ +void *p; + +BUG_ON (!mc || !mc->nobjs); + p = mc->objects[--mc->nobjs]; +return p; +} +static void free_ptes (pmd_t *pmd,unsigned long addr) {pte_t *pte;@@ -200,7 +233,347 @@ int create_hyp_io_mappings(void *from, void *to, phys_addr_t addr)return __create_hyp_mappings (from, to, &pfn); } +/** +* 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. +* +* Note we don't need locking here as this is only called when the VM is +* created, which can only be done once. +*/ +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 ; +} + +static void free_guest_pages (pte_t *pte,unsigned long addr) +{ +unsigned int i; +struct page *page, *pte_page; + + pte_page =virt_to_page (pte); + +for (i =0 ; i <PTRS_PER_PTE ; i++) { +if (pte_present (*pte)) { +unsigned long pfn =pte_pfn (*pte); + +if (pfn_valid (pfn)) {/* Skip over device memory */ + page =pfn_to_page (pfn); +put_page (page); + } +put_page (pte_page); + } + pte++; + } +} + +static void free_stage2_ptes (pmd_t *pmd,unsigned long addr) +{ +unsigned int i; +pte_t *pte; +struct page *page, *pmd_page; + + pmd_page =virt_to_page (pmd); + +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 (page_count (page) !=1 ); +pte_free_kernel (NULL , pte); + +put_page (pmd_page); + } + 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. +* +* Note we don't need locking here as this is only called when the VM is +* destroyed, which can only be done once. +*/ +void kvm_free_stage2_pgd (struct kvm *kvm) +{ +pgd_t *pgd; +pud_t *pud; +pmd_t *pmd; +unsigned long long i, addr; +struct page *page, *pud_page; + +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); + pud_page =virt_to_page (pud); + +if (pud_none (*pud)) +continue ; + +BUG_ON (pud_bad (*pud)); + + pmd =pmd_offset (pud, addr); +free_stage2_ptes (pmd, addr); + page =virt_to_page ((void *)pmd); +WARN_ON (page_count (page) !=1 ); +pmd_free (NULL , pmd); +put_page (pud_page); + } + +WARN_ON (page_count (pud_page) !=1 ); +free_pages ((unsigned long )kvm->arch.pgd,PGD2_ORDER ); + kvm->arch.pgd =NULL ; +} + +/* +* Clear a stage-2 PTE, lowering the various ref-counts. Also takes +* care of invalidating the TLBs. Must be called while holding +* pgd_lock, otherwise another faulting VCPU may come in and mess +* things behind our back. +*/ +static void stage2_clear_pte (struct kvm *kvm,phys_addr_t addr) +{ +pgd_t *pgd; +pud_t *pud; +pmd_t *pmd; +pte_t *pte; +struct page *page; + +kvm_debug ("Clearing PTE&%08llx\n" , addr); + pgd = kvm->arch.pgd +pgd_index (addr); + pud =pud_offset (pgd, addr); +BUG_ON (pud_none (*pud)); + + pmd =pmd_offset (pud, addr); +BUG_ON (pmd_none (*pmd)); + + pte =pte_offset_kernel (pmd, addr); +set_pte_ext (pte,__pte (0 ),0 ); + + page =virt_to_page (pte); +put_page (page); +if (page_count (page) !=1 ) { +__kvm_tlb_flush_vmid (kvm); +return ; + } + +/* Need to remove pte page */ +pmd_clear (pmd); +__kvm_tlb_flush_vmid (kvm); +pte_free_kernel (NULL , (pte_t *)((unsigned long )pte &PAGE_MASK )); + + page =virt_to_page (pmd); +put_page (page); +if (page_count (page) !=1 ) +return ; + +/* +* Need to remove pmd page. This is the worst case, and we end +* up invalidating the TLB twice. No big deal. +*/ +pud_clear (pud); +__kvm_tlb_flush_vmid (kvm); +pmd_free (NULL , (pmd_t *)((unsigned long )pmd &PAGE_MASK )); + + page =virt_to_page (pud); +put_page (page); +} + +static void stage2_set_pte (struct kvm *kvm,struct kvm_mmu_memory_cache *cache, +phys_addr_t addr,const pte_t *new_pte) +{ +pgd_t *pgd; +pud_t *pud; +pmd_t *pmd; +pte_t *pte; + +/* Create 2nd stage page table mapping - Level 1 */ + pgd = kvm->arch.pgd +pgd_index (addr); + pud =pud_offset (pgd, addr); +if (pud_none (*pud)) { +if (!cache) +return ;/* ignore calls from kvm_set_spte_hva */ + pmd =mmu_memory_cache_alloc (cache); +pud_populate (NULL , pud, pmd); + pmd +=pmd_index (addr); +get_page (virt_to_page (pud)); + }else + pmd =pmd_offset (pud, addr); + +/* Create 2nd stage page table mapping - Level 2 */ +if (pmd_none (*pmd)) { +if (!cache) +return ;/* ignore calls from kvm_set_spte_hva */ + pte =mmu_memory_cache_alloc (cache); +clean_pte_table (pte); +pmd_populate_kernel (NULL , pmd, pte); + pte +=pte_index (addr); +get_page (virt_to_page (pmd)); + }else + pte =pte_offset_kernel (pmd, addr); + +/* Create 2nd stage page table mapping - Level 3 */ +BUG_ON (pte_none (pte)); +set_pte_ext (pte, *new_pte,0 ); +get_page (virt_to_page (pte)); +} + +/** +* kvm_phys_addr_ioremap - map a device range to guest IPA +* +* @kvm: The KVM pointer +* @guest_ipa: The IPA at which to insert the mapping +* @pa: The physical address of the device +* @size: The size of the mapping +*/ +int kvm_phys_addr_ioremap (struct kvm *kvm,phys_addr_t guest_ipa, +phys_addr_t pa,unsigned long size) +{ +phys_addr_t addr, end; +pgprot_t prot; +int ret =0 ; +unsigned long pfn; +struct kvm_mmu_memory_cache cache = {0 , }; + + end = (guest_ipa + size +PAGE_SIZE -1 ) &PAGE_MASK ; + prot =__pgprot (get_mem_type_prot_pte (MT_DEVICE ) |L_PTE_USER | +L_PTE2_READ |L_PTE2_WRITE ); + pfn =__phys_to_pfn (pa); + +for (addr = guest_ipa; addr < end; addr +=PAGE_SIZE ) { +pte_t pte =pfn_pte (pfn, prot); + + ret =mmu_topup_memory_cache (&cache,2 ,2 ); +if (ret) + goto out; +spin_lock (&kvm->arch.pgd_lock); +stage2_set_pte (kvm, &cache, addr, &pte); +spin_unlock (&kvm->arch.pgd_lock); + + pfn++; + } + +out:
arch/arm/kvm/mmu.c int create_hyp_io_mappings(void *from, void *to, phys_addr_t addr) · line 499
Quoted 10 lines
arch/arm/kvm/mmu.c +6
-0+mmu_free_memory_cache (&cache); +return ret; +} +int kvm_handle_guest_abort (struct kvm_vcpu *vcpu,struct kvm_run *run) {return -EINVAL ; } + +static bool hva_to_gpa (struct kvm *kvm,unsigned long hva,gpa_t *gpa)
A single hva can have multiple gpas mapped, no? At least that's what I gathered from the discussion about my attempt to a function similar to this :).
I'm also having a hard time following your mmu code in general. When do pages get mapped? Where do they get mapped from?
Alex
Re: [kvmarm] [PATCH v10 07/14] KVM: ARM: Memory virtualization setup
Quoted 593 lines · Alexander Graf
Quoted 590 lines · Christoffer Dall
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 inarch/arm/kvm/arm_mmu.c:
-kvm_alloc_stage2_pgd(struct kvm *kvm);
-kvm_free_stage2_pgd(struct kvm *kvm);Introduces new ARM-specific kernel memory types,
PAGE_KVM_GUESTandpgprot_guestvariables used to map 2nd stage memory for KVM guests.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 inkvm_arch_destroy_vm(). Both functions are called from the main KVM code.We pre-allocate page table memory to be able to synchronize using a spinlock and be called under
rcu_read_lockfrom the MMU notifiers. We steal themmu_memory_cacheimplementation from x86 and adapt for our specific usage.We support MMU notifiers (thanks to Marc Zyngier) through
kvm_unmap_hvaandkvm_set_spte_hva.Finally, define
kvm_phys_addr_ioremap()to map a device at a guest IPA, which is used by VGIC support to map the virtual CPU interface registers to the guest. This support is added by Marc Zyngier.Signed-off-by Marc Zyngier <[email protected]>Signed-off-by Christoffer Dall <[email protected]>---
arch/arm/include/asm/kvm_asm.h | 2 arch/arm/include/asm/kvm_host.h | 18 ++ arch/arm/include/asm/kvm_mmu.h | 9 + arch/arm/include/asm/pgtable-3level.h | 9 + arch/arm/include/asm/pgtable.h | 4 arch/arm/kvm/Kconfig | 1 arch/arm/kvm/arm.c | 38 +++ arch/arm/kvm/exports.c | 1 arch/arm/kvm/interrupts.S | 8 + arch/arm/kvm/mmu.c | 373 +++++++++++++++++++++++++++++++++ arch/arm/mm/mmu.c | 3 11 files changed, 465 insertions(+), 1 deletion(-)arch/arm/include/asm/kvm_asm.h +2
-0@@ -34,6 +34,7 @@#define SMCHYP_HVBAR_W 0xfffffff0#ifndef __ASSEMBLY__ +struct kvm ;struct kvm_vcpu ;extern char __kvm_hyp_init[];@@ -48,6 +49,7 @@ extern char __kvm_hyp_code_start[];extern char __kvm_hyp_code_end[];extern void __kvm_flush_vm_context (void ); +extern void __kvm_tlb_flush_vmid (struct kvm *kvm);extern int __kvm_vcpu_run (struct kvm_vcpu *vcpu);#endif arch/arm/include/asm/kvm_host.h +18
-0@@ -157,4 +157,22 @@ struct kvm_vcpu_stat {struct kvm_vcpu_init ;int kvm_vcpu_set_target (struct kvm_vcpu *vcpu,const struct kvm_vcpu_init *init); + +#define KVM_ARCH_WANT_MMU_NOTIFIER +struct kvm ; +int kvm_unmap_hva (struct kvm *kvm,unsigned long hva); +int kvm_unmap_hva_range (struct kvm *kvm, +unsigned long start,unsigned long end); +void kvm_set_spte_hva (struct kvm *kvm,unsigned long hva,pte_t pte); + +/* 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 ; +}#endif /* __ARM_KVM_HOST_H__ */ arch/arm/include/asm/kvm_mmu.h +9
-0@@ -33,4 +33,13 @@ int create_hyp_mappings(void *from, void *to);int create_hyp_io_mappings (void *from,void *to,phys_addr_t );void free_hyp_pmds (void ); +int kvm_alloc_stage2_pgd (struct kvm *kvm); +void kvm_free_stage2_pgd (struct kvm *kvm); +int kvm_phys_addr_ioremap (struct kvm *kvm,phys_addr_t guest_ipa, +phys_addr_t pa,unsigned long size); + +int kvm_handle_guest_abort (struct kvm_vcpu *vcpu,struct kvm_run *run); + +void kvm_mmu_free_memory_caches (struct kvm_vcpu *vcpu); +#endif /* __ARM_KVM_MMU_H__ */ arch/arm/include/asm/pgtable-3level.h +9
-0@@ -102,6 +102,15 @@*/ #define L_PGD_SWAPPER (_AT(pgdval_t, 1) << 55)/* swapper_pg_dir entry */ +/* +* 2-nd stage PTE definitions for LPAE. +*/ +#define L_PTE2_SHARED L_PTE_SHARED +#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@@ -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_NORM_WB | L_PTE2_INNER_WB | \ + L_PTE2_SHARED)#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@@ -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/arm.c +35
-1@@ -82,12 +82,34 @@ void kvm_arch_sync_events(struct kvm *kvm) { } +/** +* 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 ; -return 0 ; + ret =kvm_alloc_stage2_pgd (kvm); +if (ret) + goto out_fail_alloc; +spin_lock_init (&kvm->arch.pgd_lock); + + ret =create_hyp_mappings (kvm, kvm +1 ); +if (ret) + goto out_free_stage2_pgd; + +/* Mark the initial VMID generation invalid */ + kvm->arch.vmid_gen =0 ; + +return ret; +out_free_stage2_pgd: +kvm_free_stage2_pgd (kvm); +out_fail_alloc: +return ret; }int kvm_arch_vcpu_fault (struct kvm_vcpu *vcpu,struct vm_fault *vmf)@@ -105,10 +127,16 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)return 0 ; } +/** +* 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]);@@ -184,7 +212,13 @@ 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 vcpu_uninit; +return vcpu; +vcpu_uninit: +kvm_vcpu_uninit (vcpu);
free_vcpu:kmem_cache_free(kvm_vcpu_cache, vcpu);out:
arch/arm/kvm/arm.c +2
-0@@ -193,6 +227,8 @@ out:void kvm_arch_vcpu_free (struct kvm_vcpu *vcpu) { +kvm_mmu_free_memory_caches (vcpu); +kmem_cache_free (kvm_vcpu_cache, vcpu); }void kvm_arch_vcpu_destroy (struct kvm_vcpu *vcpu)arch/arm/kvm/exports.c +1
-0@@ -33,5 +33,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 +8
-0@@ -31,6 +31,14 @@ __kvm_hyp_code_start: .globl __kvm_hyp_code_start@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Flush per-VMID TLBs +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +ENTRY(__kvm_tlb_flush_vmid) + bx lr +ENDPROC(__kvm_tlb_flush_vmid) + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Flush TLBs and instruction caches of current CPU for all VMIDs
arch/arm/kvm/interrupts.S +0
-0@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@arch/arm/kvm/mmu.c +291
-0@@ -23,10 +23,43 @@#include <asm/pgalloc.h> #include <asm/kvm_arm.h> #include <asm/kvm_mmu.h> +#include <asm/kvm_asm.h> #include <asm/mach/map.h> static DEFINE_MUTEX (kvm_hyp_pgd_mutex); +static int mmu_topup_memory_cache (struct kvm_mmu_memory_cache *cache, +int min,int max) +{ +void *page; + +BUG_ON (max >KVM_NR_MEM_OBJS ); +if (cache->nobjs >= min) +return 0 ; +while (cache->nobjs < max) { + page = (void *)__get_free_page (PGALLOC_GFP ); +if (!page) +return -ENOMEM ; + cache->objects[cache->nobjs++] = page; + } +return 0 ; +} + +static void mmu_free_memory_cache (struct kvm_mmu_memory_cache *mc) +{ +while (mc->nobjs) +free_page ((unsigned long )mc->objects[--mc->nobjs]); +} + +static void *mmu_memory_cache_alloc (struct kvm_mmu_memory_cache *mc) +{ +void *p; + +BUG_ON (!mc || !mc->nobjs); + p = mc->objects[--mc->nobjs]; +return p; +} +static void free_ptes (pmd_t *pmd,unsigned long addr) {pte_t *pte;@@ -200,7 +233,347 @@ int create_hyp_io_mappings(void *from, void *to, phys_addr_t addr)return __create_hyp_mappings (from, to, &pfn); } +/** +* 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. +* +* Note we don't need locking here as this is only called when the VM is +* created, which can only be done once. +*/ +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 ; +} + +static void free_guest_pages (pte_t *pte,unsigned long addr) +{ +unsigned int i; +struct page *page, *pte_page; + + pte_page =virt_to_page (pte); + +for (i =0 ; i <PTRS_PER_PTE ; i++) { +if (pte_present (*pte)) { +unsigned long pfn =pte_pfn (*pte); + +if (pfn_valid (pfn)) {/* Skip over device memory */ + page =pfn_to_page (pfn); +put_page (page); + } +put_page (pte_page); + } + pte++; + } +} + +static void free_stage2_ptes (pmd_t *pmd,unsigned long addr) +{ +unsigned int i; +pte_t *pte; +struct page *page, *pmd_page; + + pmd_page =virt_to_page (pmd); + +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 (page_count (page) !=1 ); +pte_free_kernel (NULL , pte); + +put_page (pmd_page); + } + 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. +* +* Note we don't need locking here as this is only called when the VM is +* destroyed, which can only be done once. +*/ +void kvm_free_stage2_pgd (struct kvm *kvm) +{ +pgd_t *pgd; +pud_t *pud; +pmd_t *pmd; +unsigned long long i, addr; +struct page *page, *pud_page; + +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); + pud_page =virt_to_page (pud); + +if (pud_none (*pud)) +continue ; + +BUG_ON (pud_bad (*pud)); + + pmd =pmd_offset (pud, addr); +free_stage2_ptes (pmd, addr); + page =virt_to_page ((void *)pmd); +WARN_ON (page_count (page) !=1 ); +pmd_free (NULL , pmd); +put_page (pud_page); + } + +WARN_ON (page_count (pud_page) !=1 ); +free_pages ((unsigned long )kvm->arch.pgd,PGD2_ORDER ); + kvm->arch.pgd =NULL ; +} + +/* +* Clear a stage-2 PTE, lowering the various ref-counts. Also takes +* care of invalidating the TLBs. Must be called while holding +* pgd_lock, otherwise another faulting VCPU may come in and mess +* things behind our back. +*/ +static void stage2_clear_pte (struct kvm *kvm,phys_addr_t addr) +{ +pgd_t *pgd; +pud_t *pud; +pmd_t *pmd; +pte_t *pte; +struct page *page; + +kvm_debug ("Clearing PTE&%08llx\n" , addr); + pgd = kvm->arch.pgd +pgd_index (addr); + pud =pud_offset (pgd, addr); +BUG_ON (pud_none (*pud)); + + pmd =pmd_offset (pud, addr); +BUG_ON (pmd_none (*pmd)); + + pte =pte_offset_kernel (pmd, addr); +set_pte_ext (pte,__pte (0 ),0 ); + + page =virt_to_page (pte); +put_page (page); +if (page_count (page) !=1 ) { +__kvm_tlb_flush_vmid (kvm); +return ; + } + +/* Need to remove pte page */ +pmd_clear (pmd); +__kvm_tlb_flush_vmid (kvm); +pte_free_kernel (NULL , (pte_t *)((unsigned long )pte &PAGE_MASK )); + + page =virt_to_page (pmd); +put_page (page); +if (page_count (page) !=1 ) +return ; + +/* +* Need to remove pmd page. This is the worst case, and we end +* up invalidating the TLB twice. No big deal. +*/ +pud_clear (pud); +__kvm_tlb_flush_vmid (kvm); +pmd_free (NULL , (pmd_t *)((unsigned long )pmd &PAGE_MASK )); + + page =virt_to_page (pud); +put_page (page); +} + +static void stage2_set_pte (struct kvm *kvm,struct kvm_mmu_memory_cache *cache, +phys_addr_t addr,const pte_t *new_pte) +{ +pgd_t *pgd; +pud_t *pud; +pmd_t *pmd; +pte_t *pte; + +/* Create 2nd stage page table mapping - Level 1 */ + pgd = kvm->arch.pgd +pgd_index (addr); + pud =pud_offset (pgd, addr); +if (pud_none (*pud)) { +if (!cache) +return ;/* ignore calls from kvm_set_spte_hva */ + pmd =mmu_memory_cache_alloc (cache); +pud_populate (NULL , pud, pmd); + pmd +=pmd_index (addr); +get_page (virt_to_page (pud)); + }else + pmd =pmd_offset (pud, addr); + +/* Create 2nd stage page table mapping - Level 2 */ +if (pmd_none (*pmd)) { +if (!cache) +return ;/* ignore calls from kvm_set_spte_hva */ + pte =mmu_memory_cache_alloc (cache); +clean_pte_table (pte); +pmd_populate_kernel (NULL , pmd, pte); + pte +=pte_index (addr); +get_page (virt_to_page (pmd)); + }else + pte =pte_offset_kernel (pmd, addr); + +/* Create 2nd stage page table mapping - Level 3 */ +BUG_ON (pte_none (pte)); +set_pte_ext (pte, *new_pte,0 ); +get_page (virt_to_page (pte)); +} + +/** +* kvm_phys_addr_ioremap - map a device range to guest IPA +* +* @kvm: The KVM pointer +* @guest_ipa: The IPA at which to insert the mapping +* @pa: The physical address of the device +* @size: The size of the mapping +*/ +int kvm_phys_addr_ioremap (struct kvm *kvm,phys_addr_t guest_ipa, +phys_addr_t pa,unsigned long size) +{ +phys_addr_t addr, end; +pgprot_t prot; +int ret =0 ; +unsigned long pfn; +struct kvm_mmu_memory_cache cache = {0 , }; + + end = (guest_ipa + size +PAGE_SIZE -1 ) &PAGE_MASK ; + prot =__pgprot (get_mem_type_prot_pte (MT_DEVICE ) |L_PTE_USER | +L_PTE2_READ |L_PTE2_WRITE ); + pfn =__phys_to_pfn (pa); + +for (addr = guest_ipa; addr < end; addr +=PAGE_SIZE ) { +pte_t pte =pfn_pte (pfn, prot); + + ret =mmu_topup_memory_cache (&cache,2 ,2 ); +if (ret) + goto out; +spin_lock (&kvm->arch.pgd_lock); +stage2_set_pte (kvm, &cache, addr, &pte); +spin_unlock (&kvm->arch.pgd_lock); + + pfn++; + } + +out: +mmu_free_memory_cache (&cache); +return ret; +} +
Quoted 9 lines
arch/arm/kvm/mmu.c int create_hyp_io_mappings(void *from, void *to, phys_addr_t addr) · line 499
Quoted 6 lines
arch/arm/kvm/mmu.c +2
-0int kvm_handle_guest_abort (struct kvm_vcpu *vcpu,struct kvm_run *run) {return -EINVAL ; } + +static bool hva_to_gpa (struct kvm *kvm,unsigned long hva,gpa_t *gpa)A single hva can have multiple gpas mapped, no? At least that's what I gathered from the discussion about my attempt to a function similar to this :).
I don't think this is the case for ARM, can you provide an example? We use gfn_to_pfn_prot and only allow user memory regions. What you suggest would be multiple physical addresses pointing to the same memory bank, I don't think that makes any sense on ARM hardware, for x86 and PPC I don't know.
Unless I miss something completely all together here...?
Quoted 2 lines
I'm also having a hard time following your mmu code in general. When do pages get mapped? Where do they get mapped from?
as the text describes, this happens in a later patch. See user_mem_abort in patch 12.
Quoted 3 lines
Alex
Re: [kvmarm] [PATCH v10 07/14] KVM: ARM: Memory virtualization setup
Quoted 9 lines · Christoffer Dall
Quoted 2 lines · Alexander Graf
A single hva can have multiple gpas mapped, no? At least that's what I gathered from the discussion about my attempt to a function similar to this :).
I don't think this is the case for ARM, can you provide an example? We use
gfn_to_pfn_protand only allow user memory regions. What you suggest would be multiple physical addresses pointing to the same memory bank, I don't think that makes any sense on ARM hardware, for x86 and PPC I don't know.
I don't know what an hva is, but yes, ARM boards can have the same block of RAM aliased into multiple places in the physical address space. (we don't currently bother to implement the aliases in qemu's vexpress-a15 though because it's a bunch of mappings of the low 2GB into high addresses mostly intended to let you test LPAE code without having to put lots of RAM on the hardware).
-- PMM
Re: [kvmarm] [PATCH v10 07/14] KVM: ARM: Memory virtualization setup
Quoted 5 lines · Peter Maydell
Quoted 3 lines · Christoffer Dall
Quoted 2 lines · Alexander Graf
A single hva can have multiple gpas mapped, no? At least that's what I gathered from the discussion about my attempt to a function similar to this :).
Quoted 7 lines
Quoted 5 lines
I don't think this is the case for ARM, can you provide an example? We use
gfn_to_pfn_protand only allow user memory regions. What you suggest would be multiple physical addresses pointing to the same memory bank, I don't think that makes any sense on ARM hardware, for x86 and PPC I don't know.I don't know what an hva is,
host virtual address
(see Documentation/virtual/kvm/mmu.txt for more TLAs in this area).
but yes, ARM boards can have the same
Quoted 5 lines
block of RAM aliased into multiple places in the physical address space. (we don't currently bother to implement the aliases in qemu's vexpress-a15 though because it's a bunch of mappings of the low 2GB into high addresses mostly intended to let you test LPAE code without having to put lots of RAM on the hardware).
Even if it weren't common, the API allows it, so we must behave sensibly.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [kvmarm] [PATCH v10 07/14] KVM: ARM: Memory virtualization setup
Quoted 12 lines · Avi Kivity
Quoted 11 lines · Peter Maydell
Quoted 9 lines · Christoffer Dall
Quoted 2 lines · Alexander Graf
A single hva can have multiple gpas mapped, no? At least that's what I gathered from the discussion about my attempt to a function similar to this :).
I don't think this is the case for ARM, can you provide an example? We use
gfn_to_pfn_protand only allow user memory regions. What you suggest would be multiple physical addresses pointing to the same memory bank, I don't think that makes any sense on ARM hardware, for x86 and PPC I don't know.
Quoted 12 lines
Quoted 1 line
I don't know what an hva is,
host virtual address
(see
Documentation/virtual/kvm/mmu.txtfor more TLAs in this area).but yes, ARM boards can have the same
Quoted 5 lines
block of RAM aliased into multiple places in the physical address space. (we don't currently bother to implement the aliases in qemu's vexpress-a15 though because it's a bunch of mappings of the low 2GB into high addresses mostly intended to let you test LPAE code without having to put lots of RAM on the hardware).
I stand corrected.
Quoted 3 lines
Even if it weren't common, the API allows it, so we must behave sensibly.
true, this should be a solution:
commit 2a8661fd7e6c15889a20a4547bd7861e84b778a8
Author: Christoffer Dall <[email protected]> Date: Sun Aug 19 15:52:10 2012 -0400
KVM: ARM: A single hva can map multiple gpas
Handle mmu notifier ops for every such mapping.
arch/arm/kvm/mmu.c +0 -0
@@ -754,11 +754,14 @@ int kvm_handle_guest_abort(struct kvm_vcpu
*vcpu, struct kvm_run *run)
return ret ? ret : 1;
}
-static bool hva_to_gpa(struct kvm *kvm, unsigned long hva, gpa_t *gpa) +static int handle_hva_to_gpa(struct kvm *kvm, unsigned long hva,
+ void (*handler)(struct kvm *kvm, unsigned long hva,
+ gpa_t gpa, void *data),
+ void *data)
{
struct kvm_memslots *slots;
struct kvm_memory_slot *memslot;
- bool found = false;
+ int cnt = 0;
slots = kvm_memslots(kvm);
@@ -769,31 +772,36 @@ static bool hva_to_gpa(struct kvm *kvm, unsigned long hva, gpa_t *gpa)
end = start + (memslot->npages << PAGE_SHIFT); if (hva >= start && hva < end) {
+ gpa_t gpa;
gpa_t gpa_offset = hva - start;
- *gpa = (memslot->base_gfn << PAGE_SHIFT) + gpa_offset;
- found = true;
- /* no overlapping memslots allowed: break */
- break;
+ gpa = (memslot->base_gfn << PAGE_SHIFT) + gpa_offset;
+ handler(kvm, hva, gpa, data);
+ cnt++;
}
}
- return found;
+ return cnt;
+}
+
+static void kvm_unmap_hva_handler(struct kvm *kvm, unsigned long hva,
+ gpa_t gpa, void *data)
+{
+ spin_lock(&kvm->arch.pgd_lock);
+ stage2_clear_pte(kvm, gpa);
+ spin_unlock(&kvm->arch.pgd_lock);
}
int kvm_unmap_hva(struct kvm *kvm, unsigned long hva) {
- bool found;
- gpa_t gpa;
+ int found;
if (!kvm->arch.pgd)
return 0;
- found = hva_to_gpa(kvm, hva, &gpa);
- if (found) {
- spin_lock(&kvm->arch.pgd_lock);
- stage2_clear_pte(kvm, gpa);
- spin_unlock(&kvm->arch.pgd_lock);
- }
+ found = handle_hva_to_gpa(kvm, hva, &kvm_unmap_hva_handler, NULL);
+ if (found > 0)
+ __kvm_tlb_flush_vmid(kvm);
+
return 0;
}
@@ -814,21 +822,27 @@ int kvm_unmap_hva_range(struct kvm *kvm,
return 0;
}
+static void kvm_set_spte_handler(struct kvm *kvm, unsigned long hva,
+ gpa_t gpa, void *data)
+{
+ pte_t *pte = (pte_t *)data;
+
+ spin_lock(&kvm->arch.pgd_lock);
+ stage2_set_pte(kvm, NULL, gpa, pte);
+ spin_unlock(&kvm->arch.pgd_lock);
+}
+
+
void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte) {
- gpa_t gpa;
- bool found;
+ int found;
if (!kvm->arch.pgd)
return;
- found = hva_to_gpa(kvm, hva, &gpa);
- if (found) {
- spin_lock(&kvm->arch.pgd_lock);
- stage2_set_pte(kvm, NULL, gpa, &pte);
- spin_unlock(&kvm->arch.pgd_lock);
+ found = handle_hva_to_gpa(kvm, hva, &kvm_set_spte_handler, &pte);
+ if (found > 0)
__kvm_tlb_flush_vmid(kvm);
- }
}
void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu)
--
RE: [PATCH v10 07/14] KVM: ARM: Memory virtualization setup
In reply to Christoffer DallDear Christoffer Dall,
In you code, kvm_alloc_stage2_pgd function allocates page table and memset. isn't it necessary to clean the data cache after memset?
For reference, pgd_alloc cleans the area with clean_dcache_area.
There's some situation that cleaning is not necessary, and clean_dcache_area function becomes empty in that case. But I think explicit cleaning would be better for portability.
Best Regards
Min-gyu Kim
-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Christoffer Dall
Sent: Friday, August 17, 2012 12:29 AM
To: [email protected]; [email protected]
Subject: [PATCH v10 07/14] KVM: ARM: Memory virtualization setup
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);
Introduces new ARM-specific kernel memory types, PAGE_KVM_GUEST and pgprot_guest variables used to map 2nd stage memory for KVM guests.
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.
We pre-allocate page table memory to be able to synchronize using a spinlock and be called under rcu_read_lock from the MMU notifiers. We steal the mmu_memory_cache implementation from x86 and adapt for our specific usage.
We support MMU notifiers (thanks to Marc Zyngier) through kvm_unmap_hva and kvm_set_spte_hva.
Finally, define kvm_phys_addr_ioremap() to map a device at a guest IPA, which is used by VGIC support to map the virtual CPU interface registers to the guest. This support is added by Marc Zyngier.
--- arch/arm/include/asm/kvm_asm.h | 2 arch/arm/include/asm/kvm_host.h | 18 ++ arch/arm/include/asm/kvm_mmu.h | 9 + arch/arm/include/asm/pgtable-3level.h | 9 + arch/arm/include/asm/pgtable.h | 4 arch/arm/kvm/Kconfig | 1 arch/arm/kvm/arm.c | 38 +++ arch/arm/kvm/exports.c | 1 arch/arm/kvm/interrupts.S | 8 + arch/arm/kvm/mmu.c | 373 +++++++++++++++++++++++++++++++++ arch/arm/mm/mmu.c | 3 11 files changed, 465 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_asm.h +20 -1
@@ -34,6 +34,7 @@#define SMCHYP_HVBAR_W 0xfffffff0#ifndef __ASSEMBLY__ +struct kvm ;struct kvm_vcpu ;extern char __kvm_hyp_init[];
@@ -48,6 +49,7 @@ extern char __kvm_hyp_code_start[]; extern char __kvm_hyp_code_end[];extern void __kvm_flush_vm_context (void ); +extern void __kvm_tlb_flush_vmid (struct kvm *kvm);extern int __kvm_vcpu_run (struct kvm_vcpu *vcpu);#endif diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index d7e3398..d86ce39 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h
@@ -157,4 +157,22 @@ struct kvm_vcpu_stat { struct kvm_vcpu_init; int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, const struct kvm_vcpu_init *init); + +#define KVM_ARCH_WANT_MMU_NOTIFIER +struct kvm ; +int kvm_unmap_hva (struct kvm *kvm,unsigned long hva);int +kvm_unmap_hva_range (struct kvm *kvm, +unsigned long start,unsigned long end);void +kvm_set_spte_hva (struct kvm *kvm,unsigned long hva,pte_t pte); + +/* 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 ; +}#endif /* __ARM_KVM_HOST_H__ */
arch/arm/include/asm/kvm_mmu.h +9 -0
@@ -33,4 +33,13 @@ int create_hyp_mappings(void *from, void *to); int create_hyp_io_mappings(void *from, void *to, phys_addr_t); void free_hyp_pmds(void); +int kvm_alloc_stage2_pgd (struct kvm *kvm);void +kvm_free_stage2_pgd (struct kvm *kvm);int kvm_phys_addr_ioremap (struct +kvm *kvm,phys_addr_t guest_ipa, +phys_addr_t pa,unsigned long size); + +int kvm_handle_guest_abort (struct kvm_vcpu *vcpu,struct kvm_run *run); + +void kvm_mmu_free_memory_caches (struct kvm_vcpu *vcpu); +#endif /* __ARM_KVM_MMU_H__ */
arch/arm/include/asm/pgtable-3level.h +9 -0
@@ -102,6 +102,15 @@*/ #define L_PGD_SWAPPER (_AT(pgdval_t, 1) << 55)/* swapper_pg_dir entry */ +/* +* 2-nd stage PTE definitions for LPAE. +*/ +#define L_PTE2_SHARED L_PTE_SHARED +#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
@@ -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_NORM_WB | L_PTE2_INNER_WB | \ + L_PTE2_SHARED)#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
@@ -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/arm.c +23 -1
@@ -82,12 +82,34 @@ void kvm_arch_sync_events(struct kvm *kvm) { } +/** +* 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 ; -return 0 ; + ret =kvm_alloc_stage2_pgd (kvm); +if (ret) + goto out_fail_alloc; +spin_lock_init (&kvm->arch.pgd_lock); + + ret =create_hyp_mappings (kvm, kvm +1 ); +if (ret) + goto out_free_stage2_pgd; + +/* Mark the initial VMID generation invalid */ + kvm->arch.vmid_gen =0 ; + +return ret; +out_free_stage2_pgd: +kvm_free_stage2_pgd (kvm); +out_fail_alloc: +return ret; } int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf) @@ -105,10 +127,16 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) return 0; }
+/**
+ * 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]);
@@ -184,7 +212,13 @@ 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 vcpu_uninit;
+
return vcpu;
+vcpu_uninit:
+ kvm_vcpu_uninit(vcpu);
free_vcpu:
kmem_cache_free(kvm_vcpu_cache, vcpu);
out:
@@ -193,6 +227,8 @@ out:
void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu) {
+ kvm_mmu_free_memory_caches(vcpu);
+ kmem_cache_free(kvm_vcpu_cache, vcpu);
}
void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) diff --git a/arch/arm/kvm/exports.c b/arch/arm/kvm/exports.c index 8ebdf07..f39f823 100644
arch/arm/kvm/exports.c +1 -0
@@ -33,5 +33,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 +8 -0
@@ -31,6 +31,14 @@ __kvm_hyp_code_start: .globl __kvm_hyp_code_start @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Flush per-VMID TLBs +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +ENTRY(__kvm_tlb_flush_vmid) + bx lr +ENDPROC(__kvm_tlb_flush_vmid) + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ Flush TLBs and instruction caches of current CPU for all VMIDs @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
arch/arm/kvm/mmu.c +365 -0
379 lines not on this page · raw message
arch/arm/mm/mmu.c +3 -0
@@ -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 ];
@@ -514,6 +516,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;
--
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
Re: [PATCH v10 07/14] KVM: ARM: Memory virtualization setup
Quoted 2 lines · Min-gyu Kim
Dear Christoffer Dall,
Hi Min-gyu
(it's not considered best practice to top-post in these types of mailing list, please post inline or bottom-post in the future).
Quoted 5 lines
In you code,
kvm_alloc_stage2_pgdfunction allocates page table and memset. isn't it necessary to clean the data cache after memset?For reference,
pgd_alloccleans the area withclean_dcache_area.
You are right, it does. I don't know if the architecture requires it, but we certainly don't want the MMU to see anything else than zeroes at that point. Good catch!
Quoted 3 lines
There's some situation that cleaning is not necessary, and
clean_dcache_areafunction becomes empty in that case. But I think explicit cleaning would be better for portability.
Thanks,
-Christoffer
PATCH v10 8/14 KVM: ARM: Inject IRQs and FIQs from userspace
In reply to Christoffer DallFrom: 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 | 47 +++++++++++++++++++++++++++++++++++++ include/linux/kvm.h | 1 + 5 files changed, 66 insertions(+), 4 deletions(-)
Documentation/virtual/kvm/api.txt +8 -4
@@ -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
@@ -22,6 +22,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
@@ -74,6 +74,7 @@#define HCR_GUEST_MASK (HCR_TSC | HCR_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \ HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \ HCR_SWIO | HCR_TIDCP) +#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 +47 -0
65 lines not on this page · raw message
include/linux/kvm.h +1 -0
@@ -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;
Re: [PATCH v10 08/14] KVM: ARM: Inject IRQs and FIQs from userspace
Quoted 35 lines · Christoffer Dall
From: Christoffer Dall <[email protected]>
Userspace can inject IRQs and FIQs through the
KVM_IRQ_LINEVM 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->irqfield. The irq fild value will be:
IRQ:vcpu_index<< 1FIQ: (vcpu_index << 1) | 1This is documented in
Documentation/kvm/api.txt.The effect of the ioctl is simply to simply raise/lower the corresponding
irq_linefield on the VCPU struct, which will cause the world-switch code to raise/lower virtual interrupts when running the guest on next switch. Thewait_for_interruptflag 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.Signed-off-by Christoffer Dall <[email protected]>--- 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 | 47 +++++++++++++++++++++++++++++++++++++ include/linux/kvm.h | 1 + 5 files changed, 66 insertions(+), 4 deletions(-)Documentation/virtual/kvm/api.txt +0
-0
Documentation/virtual/kvm/api.txt only go to the IOAPIC. On ia64, a IOSAPIC is created. · line 614
Quoted 1 line
Documentation/virtual/kvm/api.txt +0
-0@@ -614,15 +614,19 @@ only go to the IOAPIC. On ia64, a IOSAPIC is created. 4.25 KVM_IRQ_LINE
So this IOCTL is now used for injecting from a userspace GIC model to the VCPUs, right? Then this version is OK as userspace should be able to detect IRQ coalescing per IRQ generating device.
However, injecting into an in-kernel GIC should use KVM_IRQ_LINE_STATUS later on, keeping the door open for userspace implementing the IRQ de-coalescing (for custom periodic timer devices). Have you thought about how that IRQ injecting interface will look like, how the interrupts will be specified? Do you actually plan to have both version as ABI (with and without in-kernel irqchip)?
Documentation/virtual/kvm/api.txt only go to the IOAPIC. On ia64, a IOSAPIC is created. · line 623
Quoted 13 lines
Documentation/virtual/kvm/api.txt +2
-4Capability: 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
^^^^
Please be more specific to help the poor users of this interface guessing if the should or should not call CREATE_IRQCHIP first.
Quoted 127 lines
Documentation/virtual/kvm/api.txt +6
-0+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@@ -22,6 +22,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@@ -74,6 +74,7 @@#define HCR_GUEST_MASK (HCR_TSC | HCR_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \ HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \ HCR_SWIO | HCR_TIDCP) +#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 +47
-0@@ -24,6 +24,7 @@#include <linux/fs.h> #include <linux/mman.h> #include <linux/sched.h> +#include <linux/kvm.h> #include <trace/events/kvm.h> #define CREATE_TRACE_POINTS @@ -265,6 +266,7 @@ void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)void kvm_arch_vcpu_load (struct kvm_vcpu *vcpu,int cpu) { + vcpu->cpu = cpu; }void kvm_arch_vcpu_put (struct kvm_vcpu *vcpu)@@ -305,6 +307,51 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)return -EINVAL ; } +int kvm_vm_ioctl_irq_line (struct kvm *kvm,struct kvm_irq_level *irq_level) +{ +unsigned int vcpu_idx; +struct kvm_vcpu *vcpu; +unsigned long *ptr; +bool set; +int bit_index; + + 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 ; + +trace_kvm_set_irq (irq_level->irq, irq_level->level,0 ); + +if ((irq_level->irq &1 ) ==KVM_ARM_IRQ_LINE ) + bit_index =ffs (HCR_VI ) -1 ; +else /* KVM_ARM_FIQ_LINE */ + bit_index =ffs (HCR_VF ) -1 ; + + ptr = (unsigned long *)&vcpu->arch.irq_lines; +if (irq_level->level) + set =test_and_set_bit (bit_index, ptr); +else + set =test_and_clear_bit (bit_index, ptr); + +/* +* If we didn't change anything, no need to wake up or kick other CPUs +*/ +if (!!set == !!irq_level->level) +return 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); + +return 0 ; +} +long kvm_arch_vcpu_ioctl (struct file *filp,unsigned int ioctl,unsigned long arg) {include/linux/kvm.h +1
-1@@ -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; --
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
Jan
Signature · 2 lines
Siemens AG, Corporate Technology, CT RTC ITP SDP-DE Corporate Competence Center Embedded Linux
Re: [PATCH v10 08/14] KVM: ARM: Inject IRQs and FIQs from userspace
Quoted 42 lines · Jan Kiszka
Quoted 35 lines · Christoffer Dall
From: Christoffer Dall <[email protected]>
Userspace can inject IRQs and FIQs through the
KVM_IRQ_LINEVM 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->irqfield. The irq fild value will be:
IRQ:vcpu_index<< 1FIQ: (vcpu_index << 1) | 1This is documented in
Documentation/kvm/api.txt.The effect of the ioctl is simply to simply raise/lower the corresponding
irq_linefield on the VCPU struct, which will cause the world-switch code to raise/lower virtual interrupts when running the guest on next switch. Thewait_for_interruptflag 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.Signed-off-by Christoffer Dall <[email protected]>--- 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 | 47 +++++++++++++++++++++++++++++++++++++ include/linux/kvm.h | 1 + 5 files changed, 66 insertions(+), 4 deletions(-)Documentation/virtual/kvm/api.txt +0
-0Documentation/virtual/kvm/api.txt only go to the IOAPIC. On ia64, a IOSAPIC is created. · line 614
Quoted 1 line
Documentation/virtual/kvm/api.txt +0
-0@@ -614,15 +614,19 @@ only go to the IOAPIC. On ia64, a IOSAPIC is created. 4.25 KVM_IRQ_LINESo this IOCTL is now used for injecting from a userspace GIC model to the VCPUs, right? Then this version is OK as userspace should be able to detect IRQ coalescing per IRQ generating device.
Quoted 7 lines
However, injecting into an in-kernel GIC should use
KVM_IRQ_LINE_STATUSlater on, keeping the door open for userspace implementing the IRQ de-coalescing (for custom periodic timer devices). Have you thought about how that IRQ injecting interface will look like, how the interrupts will be specified? Do you actually plan to have both version as ABI (with and without in-kernel irqchip)?
Hi Jan,
this is being reworked when the in-kernel vgic support gets merged, see the discussion starting with this mail:
https://lists.cs.columbia.edu/pipermail/kvmarm/2012-August/002922.html
Quoted 8 lines
Quoted 8 lines
Documentation/virtual/kvm/api.txt +1
-1Capability: 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
Quoted 9 lines
Documentation/virtual/kvm/api.txt only go to the IOAPIC. On ia64, a IOSAPIC is created. · line 623
Quoted 5 lines
Documentation/virtual/kvm/api.txt +1
-3Sets 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^^^^Please be more specific to help the poor users of this interface guessing if the should or should not call
CREATE_IRQCHIPfirst.
ok, I will revise that text.
PATCH v10 9/14 KVM: ARM: World-switch implementation
In reply to Christoffer DallProvides 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.
The following Hyp-ABI is also documented in the code:
Hyp-ABI: Switching from host kernel to Hyp-mode:
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):
...
stack_page + 4: spsr (Host-SVC cpsr)
stack_page : lr_usr
--------------: stack bottom
Hyp-ABI: Switching from Hyp-mode to host kernel SVC mode:
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 bottom of the Hyp is derived from the Hyp stack pointer (only a single page aligned stack is used per CPU) and the initial SVC registers are used to restore the host state.
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.
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.
To support VFP/NEON we trap those instructions using the HPCTR. When we trap, we switch the FPU. After a guest exit, the VFP state is returned to the host. When disabling access to floating point instructions, we also mask FPEXC_EN in order to avoid the guest receiving Undefined instruction exceptions before we have a chance to switch back the floating point state. We are reusing vfp_hard_struct, so we depend on VFPv3 being enabled in the host kernel, if not, we still trap cp10 and cp11 in order to inject an undefined instruction exception whenever the guest tries to use VFP/NEON. VFP/NEON developed by Antionios Motakis and Rusty Russell.
--- arch/arm/include/asm/kvm_arm.h | 38 ++ arch/arm/include/asm/kvm_host.h | 10 + arch/arm/kernel/asm-offsets.c | 45 ++ arch/arm/kvm/arm.c | 166 +++++++++ arch/arm/kvm/interrupts.S | 711 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 967 insertions(+), 3 deletions(-)
arch/arm/include/asm/kvm_arm.h +38 -0
@@ -104,6 +104,18 @@#define TTBCR_T0SZ 3#define HTCR_MASK (TTBCR_T0SZ | TTBCR_IRGN0 | TTBCR_ORGN0 | TTBCR_SH0) +/* Hyp System Trap Register */ +#define HSTR_T (x) (1 << x) +#define HSTR_TTEE (1 << 16) +#define HSTR_TJDBX (1 << 17) + +/* Hyp Coprocessor Trap Register */ +#define HCPTR_TCP (x) (1 << x) +#define HCPTR_TCP_MASK (0x3fff) +#define HCPTR_TASE (1 << 15) +#define HCPTR_TTA (1 << 20) +#define HCPTR_TCPAC (1 << 31) +/* Hyp Debug Configuration Register bits */ #define HDCR_TDRA (1 << 11)#define HDCR_TDOSA (1 << 10)
@@ -134,5 +146,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 /* __ARM_KVM_ARM_H__ */
arch/arm/include/asm/kvm_host.h +10 -0
@@ -19,6 +19,8 @@#ifndef __ARM_KVM_HOST_H__#define __ARM_KVM_HOST_H__ +#include <asm/fpstate.h> +#define KVM_MAX_VCPUS 4#define KVM_MEMORY_SLOTS 32#define KVM_PRIVATE_MEM_SLOTS 4
@@ -132,6 +134,14 @@ struct kvm_vcpu_arch {u64 pc_ipa2;/* same as above, but for non-aligned wide thumb instructions */ +/* Floating point registers (VFP and Advanced SIMD/NEON) */ +struct vfp_hard_struct vfp_guest; +struct vfp_hard_struct *vfp_host; + +/* +* Anything that is not used directly from assembly code goes +* here. +*/ /* IO related fields */ bool mmio_sign_extend;/* for byte/halfword loads */ u32 mmio_rd;
arch/arm/kernel/asm-offsets.c +45 -0
56 lines not on this page · raw message
arch/arm/kvm/arm.c +165 -1
195 lines not on this page · raw message
arch/arm/kvm/interrupts.S +709 -2
748 lines not on this page · raw message
PATCH v10 10/14 KVM: ARM: Emulation framework and CP15 emulation
In reply to Christoffer DallAdds 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.
Minor notes about the coproc register reset:
1) We reserve a value of 0 as an invalid cp15 offset, to catch bugs in our table, at cost of 4 bytes per vcpu.
2) Added comments on the table indicating how we handle each register, for simplicity of understanding.
--- arch/arm/include/asm/kvm_arm.h | 9 + arch/arm/include/asm/kvm_coproc.h | 7 arch/arm/include/asm/kvm_emulate.h | 5 arch/arm/include/asm/kvm_host.h | 5 arch/arm/kvm/arm.c | 166 ++++++++++ arch/arm/kvm/coproc.c | 572 ++++++++++++++++++++++++++++++++++++ arch/arm/kvm/emulate.c | 120 ++++++++ arch/arm/kvm/trace.h | 28 ++ 8 files changed, 910 insertions(+), 2 deletions(-)
arch/arm/include/asm/kvm_arm.h +9 -0
@@ -76,6 +76,11 @@ HCR_SWIO | HCR_TIDCP)#define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF) +/* System Control Register (SCTLR) bits */ +#define SCTLR_TE (1 << 30) +#define SCTLR_EE (1 << 25) +#define SCTLR_V (1 << 13) +/* Hyp System Control Register (HSCTLR) bits */ #define HSCTLR_TE (1 << 30)#define HSCTLR_EE (1 << 25)
@@ -153,6 +158,10 @@#define HSR_ISS (HSR_IL - 1)#define HSR_ISV_SHIFT (24)#define HSR_ISV (1U << HSR_ISV_SHIFT) +#define HSR_CV_SHIFT (24) +#define HSR_CV (1U << HSR_CV_SHIFT) +#define HSR_COND_SHIFT (20) +#define HSR_COND (0xfU << HSR_COND_SHIFT)#define HSR_EC_UNKNOWN (0x00)#define HSR_EC_WFI (0x01)
arch/arm/include/asm/kvm_coproc.h +7 -0
@@ -21,4 +21,11 @@void kvm_reset_coprocs (struct kvm_vcpu *vcpu); +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); +void kvm_coproc_table_init (void );#endif /* __ARM_KVM_COPROC_H__ */
arch/arm/include/asm/kvm_emulate.h +5 -0
@@ -51,6 +51,11 @@ static inline enum vcpu_mode vcpu_mode(struct kvm_vcpu *vcpu)return mode; } +int kvm_handle_wfi (struct kvm_vcpu *vcpu,struct kvm_run *run); +void kvm_adjust_itstate (struct kvm_vcpu *vcpu); +void kvm_skip_instr (struct kvm_vcpu *vcpu,bool is_wide_instr); +void kvm_inject_undefined (struct kvm_vcpu *vcpu); +/* * Return the SPSR for the specified mode of the virtual CPU. */
arch/arm/include/asm/kvm_host.h +5 -0
@@ -102,6 +102,7 @@ enum cp15_regs { c5_AIFSR,/* Auxilary Instruction Fault Status Register */ c6_DFAR,/* Data Fault Address Register */ c6_IFAR,/* Instruction Fault Address Register */ + c9_L2CTLR,/* Cortex A15 L2 Control Register */ c10_PRRR,/* Primary Region Remap Register */ c10_NMRR,/* Normal Memory Remap Register */ c12_VBAR, /* Vector Base Address Register */
@@ -142,6 +143,10 @@ struct kvm_vcpu_arch {* Anything that is not used directly from assembly code goes * here. */ +/* dcache set/way operation pending */ +int last_pcpu; +cpumask_t require_dcache_flush; +/* IO related fields */ bool mmio_sign_extend;/* for byte/halfword loads */ u32 mmio_rd;
arch/arm/kvm/arm.c +164 -2
206 lines not on this page · raw message
arch/arm/kvm/coproc.c +572 -0
580 lines not on this page · raw message
arch/arm/kvm/emulate.c +120 -0
130 lines not on this page · raw message
arch/arm/kvm/trace.h +28 -0
@@ -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 */
PATCH v10 11/14 KVM: ARM: User space API for getting/setting co-proc registers
In reply to Christoffer DallThe following three ioctls are implemented:
- KVM_VCPU_GET_MSR_INDEX_LIST - KVM_GET_MSRS - KVM_SET_MSRS
Now we have a table for all the cp15 registers, we can drive a generic API. x86 already has one for sparse-numbered registers, so we simply reproduce that. The only difference is that our KVM_GET_MSR_INDEX_LIST is a per-vcpu ioctl; we can't know the MSRs until we know the cpu type. Thus we add KVM_VCPU_GET_MSR_INDEX_LIST.
The numbering for the indices for coprocesors is simple, if userspace cares (it might not for simple save and restore): the upper 16 bits are the coprocessor number. If it's > 15, it's something else, for future expansion.
Bit 15 indicates a 64-bit register. For 64 bit registers the bottom 4 bits are CRm, the next 4 are opc1 (just like the MCRR/MRCC instruction encoding). For 32 bit registers, the bottom 4 bits are CRm, the next 3 are opc2, the next 4 CRn, and the next 3 opc1 (the same order as the MRC/MCR instruction encoding, but not the same bit positions).
64-bit coprocessor register:
...|19 18 17 16|15|14 13 12 11 10 9 8| 7 6 5 4 |3 2 1 0| ...0 0 | cp num | 1| 0 0 0 0 0 0 0| opc1 | CRm |
32-bit coprocessor register:
...|19 18 17 16|15|14|13 12 11|10 9 8 7 |6 5 4 |3 2 1 0| ...0 0 | cp num | 0| 0| opc1 | CRn | opc2 | CRm |
Non-coprocessor register:
| 32 31 30 29 28 27 26 25 24 23 22 21 20|19 18 17 16 15 ... | < some non-zero value > | ...
For futureproofing, we need to tell QEMU about the CP15 registers the host lets the guest access.
It will need this information to restore a current guest on a future CPU or perhaps a future KVM which allow some of these to be changed.
We use a separate table for these, as they're only for the userspace API.
--- Documentation/virtual/kvm/api.txt | 73 +++++++ arch/arm/include/asm/kvm.h | 31 +++ arch/arm/include/asm/kvm_coproc.h | 7 + arch/arm/kvm/arm.c | 29 +++ arch/arm/kvm/coproc.c | 368 +++++++++++++++++++++++++++++++++++++ include/linux/kvm.h | 1 6 files changed, 506 insertions(+), 3 deletions(-)
Documentation/virtual/kvm/api.txt +70 -3
@@ -451,13 +451,14 @@ Support for this has been removed. Use KVM_SET_GUEST_DEBUG instead. 4.18 KVM_GET_MSRS Capability: basic -Architectures: x86 +Architectures: x86, arm Type: vcpu ioctl Parameters: struct kvm_msrs (in/out) Returns: 0 on success, -1 on error Reads model-specific registers from the vcpu. Supported msr indices can -be obtained using KVM_GET_MSR_INDEX_LIST. +be obtained using KVM_GET_MSR_INDEX_LIST (x86) or KVM_VCPU_GET_MSR_INDEX_LIST +(arm). struct kvm_msrs { __u32 nmsrs; /* number of msrs in entries */
@@ -480,7 +481,7 @@ kvm will fill in the 'data' member. 4.19 KVM_SET_MSRS Capability: basic -Architectures: x86 +Architectures: x86, arm Type: vcpu ioctl Parameters: struct kvm_msrs (in) Returns: 0 on success, -1 on error
@@ -1985,6 +1986,72 @@ the virtualized real-mode area (VRMA) facility, the kernel will re-create the VMRA HPTEs on the next KVM_RUN of any vcpu.) +4.76 KVM_VCPU_GET_MSR_INDEX_LIST + +Capability: basic +Architectures: arm +Type: vcpu ioctl +Parameters: struct kvm_msr_list (in/out) +Returns: 0 on success; -1 on error +Errors: + E2BIG: the msr index list is too big to fit in the array specified by + the user. + +struct kvm_msr_list { + __u32 nmsrs; /* number of msrs in entries */ + __u32 indices[0]; +}; + +This ioctl returns the guest special registers that are supported, and +is only valid after KVM_ARM_VCPU_INIT has been performed to initialize +the vcpu type and features. It is otherwise the equivalent of the +x86-specific KVM_GET_MSR_INDEX_LIST, for arm's coprocessor registers +and other non-register state. + +The numbering for the indices for coprocesors is simple: the upper 16 +bits are the coprocessor number. If it's > 15, it's something else, +for future expansion. + +Bit 15 indicates a 64-bit register. For 64 bit registers the bottom 4 +bits are CRm, the next 4 are opc1 (just like the MCRR/MRCC instruction +encoding). For 32 bit registers, the bottom 4 bits are CRm, the next +3 are opc2, the next 4 CRn, and the next 3 opc1 (the same order as the +MRC/MCR instruction encoding, but not the same bit positions). + +64-bit coprocessor register: + ...|19 18 17 16|15|14 13 12 11 10 9 8| 7 6 5 4 |3 2 1 0| + ...0 0 | cp num | 1| 0 0 0 0 0 0 0| opc1 | CRm | + +32-bit coprocessor register: + ...|19 18 17 16|15|14|13 12 11|10 9 8 7 |6 5 4 |3 2 1 0| + ...0 0 | cp num | 0| 0| opc1 | CRn | opc2 | CRm | + +Non-coprocessor register: + + | 32 31 30 29 28 27 26 25 24 23 22 21 20|19 18 17 16 15 ... + | < some non-zero value > | ... + + +4.77 KVM_ARM_VCPU_INIT + +Capability: basic +Architectures: arm +Type: vcpu ioctl +Parameters: struct struct kvm_vcpu_init (in) +Returns: 0 on success; -1 on error +Errors: + EINVAL: the target is unknown, or the combination of features is invalid. + ENOENT: a features bit specified is unknown. + +This tells KVM what type of CPU to present to the guest, and what +optional features it should have. This will cause a reset of the cpu +registers to their initial values. If this is not called, KVM_RUN will +return ENOEXEC for that vcpu. + +Note that because some registers reflect machine topology, all vcpus +should be created before this ioctl is invoked. + + 5. The kvm_run structure ------------------------
arch/arm/include/asm/kvm.h +31 -0
@@ -85,4 +85,35 @@ struct kvm_sync_regs {struct kvm_arch_memory_slot { }; +/* Based on x86, but we use KVM_GET_VCPU_MSR_INDEX_LIST. */ +struct kvm_msr_entry { +__u32 index; +__u32 reserved; +__u64 data; +}; + +/* for KVM_GET_MSRS and KVM_SET_MSRS */ +struct kvm_msrs { +__u32 nmsrs;/* number of msrs in entries */ +__u32 pad; + +struct kvm_msr_entry entries[0 ]; +}; + +/* for KVM_VCPU_GET_MSR_INDEX_LIST */ +struct kvm_msr_list { +__u32 nmsrs;/* number of msrs in entries */ +__u32 indices[0 ]; +}; + +/* If you need to interpret the index values, here's the key. */ +#define KVM_ARM_MSR_COPROC_MASK 0xFFFF0000 +#define KVM_ARM_MSR_64_BIT_MASK 0x00008000 +#define KVM_ARM_MSR_64_OPC1_MASK 0x000000F0 +#define KVM_ARM_MSR_64_CRM_MASK 0x0000000F +#define KVM_ARM_MSR_32_CRM_MASK 0x0000000F +#define KVM_ARM_MSR_32_OPC2_MASK 0x00000070 +#define KVM_ARM_MSR_32_CRN_MASK 0x00000780 +#define KVM_ARM_MSR_32_OPC1_MASK 0x00003800 +#endif /* __ARM_KVM_H__ */
arch/arm/include/asm/kvm_coproc.h +7 -0
@@ -27,5 +27,12 @@ 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_arm_get_msrs (struct kvm_vcpu *vcpu, +struct kvm_msr_entry __user *entries,u32 num); +int kvm_arm_set_msrs (struct kvm_vcpu *vcpu, +struct kvm_msr_entry __user *entries,u32 num); +unsigned long kvm_arm_num_guest_msrs (struct kvm_vcpu *vcpu); +int kvm_arm_copy_msrindices (struct kvm_vcpu *vcpu,u32 __user *uindices);void kvm_coproc_table_init (void );#endif /* __ARM_KVM_COPROC_H__ */
arch/arm/kvm/arm.c +29 -0
@@ -693,6 +693,35 @@ long kvm_arch_vcpu_ioctl(struct file *filp,return kvm_vcpu_set_target (vcpu, &init); } +case KVM_VCPU_GET_MSR_INDEX_LIST : { +struct kvm_msr_list __user *user_msr_list = argp; +struct kvm_msr_list msr_list; +unsigned n; + +if (copy_from_user (&msr_list, user_msr_list,sizeof msr_list)) +return -EFAULT ; + n = msr_list.nmsrs; + msr_list.nmsrs =kvm_arm_num_guest_msrs (vcpu); +if (copy_to_user (user_msr_list, &msr_list,sizeof msr_list)) +return -EFAULT ; +if (n < msr_list.nmsrs) +return -E2BIG ; +return kvm_arm_copy_msrindices (vcpu, user_msr_list->indices); + } +case KVM_GET_MSRS : { +struct kvm_msrs msrs; +struct kvm_msrs __user *umsrs = argp; +if (copy_from_user (&msrs, umsrs,sizeof (msrs)) !=0 ) +return -EFAULT ; +return kvm_arm_get_msrs (vcpu, umsrs->entries, msrs.nmsrs); + } +case KVM_SET_MSRS : { +struct kvm_msrs msrs; +struct kvm_msrs __user *umsrs = argp; +if (copy_from_user (&msrs, umsrs,sizeof (msrs)) !=0 ) +return -EFAULT ; +return kvm_arm_set_msrs (vcpu, umsrs->entries, msrs.nmsrs); + }default :return -EINVAL ; }
arch/arm/kvm/coproc.c +368 -0
394 lines not on this page · raw message
include/linux/kvm.h +1 -0
@@ -906,6 +906,7 @@ struct kvm_s390_ucas_mapping {/* VM is being stopped by host */ #define KVM_KVMCLOCK_CTRL _IO(KVMIO, 0xad)#define KVM_ARM_VCPU_INIT _IOW(KVMIO, 0xae, struct kvm_vcpu_init) +#define KVM_VCPU_GET_MSR_INDEX_LIST _IOWR(KVMIO, 0xaf, struct kvm_msr_list)#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)
PATCH v10 12/14 KVM: ARM: Handle guest faults in KVM
In reply to Christoffer DallHandles the guest faults in KVM by mapping in corresponding user pages in the 2nd stage page tables.
--- arch/arm/include/asm/kvm_arm.h | 9 ++++ arch/arm/include/asm/kvm_asm.h | 2 + arch/arm/kvm/mmu.c | 102 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_arm.h +9 -0
@@ -158,11 +158,20 @@#define HSR_ISS (HSR_IL - 1)#define HSR_ISV_SHIFT (24)#define HSR_ISV (1U << HSR_ISV_SHIFT) +#define HSR_FSC (0x3f) +#define HSR_FSC_TYPE (0x3c) +#define HSR_WNR (1 << 6)#define HSR_CV_SHIFT (24)#define HSR_CV (1U << HSR_CV_SHIFT)#define HSR_COND_SHIFT (20)#define HSR_COND (0xfU << HSR_COND_SHIFT) +#define FSC_FAULT (0x04) +#define FSC_PERM (0x0c) + +/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */ +#define HPFAR_MASK (~0xf) +#define HSR_EC_UNKNOWN (0x00)#define HSR_EC_WFI (0x01)#define HSR_EC_CP15_32 (0x03)
arch/arm/include/asm/kvm_asm.h +2 -0
@@ -48,6 +48,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 void __kvm_tlb_flush_vmid (struct kvm *kvm);
arch/arm/kvm/mmu.c +101 -1
116 lines not on this page · raw message
PATCH v10 13/14 KVM: ARM: Handle I/O aborts
In reply to Christoffer DallWhen 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_arm.h | 3 arch/arm/include/asm/kvm_emulate.h | 2 arch/arm/include/asm/kvm_mmu.h | 1 arch/arm/kvm/arm.c | 6 + arch/arm/kvm/emulate.c | 273 ++++++++++++++++++++++++++++++++++++ arch/arm/kvm/mmu.c | 162 +++++++++++++++++++++ arch/arm/kvm/trace.h | 21 +++ 7 files changed, 466 insertions(+), 2 deletions(-)
arch/arm/include/asm/kvm_arm.h +3 -0
@@ -158,8 +158,11 @@#define HSR_ISS (HSR_IL - 1)#define HSR_ISV_SHIFT (24)#define HSR_ISV (1U << HSR_ISV_SHIFT) +#define HSR_SRT_SHIFT (16) +#define HSR_SRT_MASK (0xf << HSR_SRT_SHIFT)#define HSR_FSC (0x3f)#define HSR_FSC_TYPE (0x3c) +#define HSR_SSE (1 << 21)#define HSR_WNR (1 << 6)#define HSR_CV_SHIFT (24)#define HSR_CV (1U << HSR_CV_SHIFT)
arch/arm/include/asm/kvm_emulate.h +2 -0
@@ -52,6 +52,8 @@ static inline enum vcpu_mode vcpu_mode(struct kvm_vcpu *vcpu) }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);void kvm_skip_instr (struct kvm_vcpu *vcpu,bool is_wide_instr);void kvm_inject_undefined (struct kvm_vcpu *vcpu);
arch/arm/include/asm/kvm_mmu.h +1 -0
@@ -38,6 +38,7 @@ void kvm_free_stage2_pgd(struct kvm *kvm);int kvm_phys_addr_ioremap (struct kvm *kvm,phys_addr_t guest_ipa,phys_addr_t pa,unsigned long size); +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);void kvm_mmu_free_memory_caches (struct kvm_vcpu *vcpu);
arch/arm/kvm/arm.c +6 -0
@@ -565,6 +565,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)if (unlikely (!vcpu->arch.target))return -ENOEXEC ; +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 +273 -0
284 lines not on this page · raw message
arch/arm/kvm/mmu.c +160 -2
186 lines not on this page · raw message
arch/arm/kvm/trace.h +21 -0
@@ -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->instr = instr; + __entry->cpsr = 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,
PATCH v10 14/14 KVM: ARM: Guest wait-for-interrupts (WFI) support
In reply to Christoffer DallFrom: 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 block the vcpu when the guest excecutes a wfi instruction and the IRQ or FIQ lines are not raised.
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.
--- arch/arm/kvm/arm.c | 10 ++++++++-- arch/arm/kvm/emulate.c | 13 ++++++++++++- arch/arm/kvm/trace.h | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-)
arch/arm/kvm/arm.c +8 -2
@@ -313,9 +313,16 @@ 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 an interrupt line is +* asserted, the CPU is by definition runnable. +*/ int kvm_arch_vcpu_runnable (struct kvm_vcpu *v) { -return 0 ; +return !!v->arch.irq_lines; }int kvm_arch_vcpu_in_guest_mode (struct kvm_vcpu *v)
@@ -581,7 +588,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)* Check conditions before entering the guest */ cond_resched (); -update_vttbr (vcpu->kvm);local_irq_disable ();
arch/arm/kvm/emulate.c +12 -1
@@ -154,9 +154,20 @@ static int kvm_instr_index(u32 instr, u32 table[][2], int table_entries)return INSTR_NONE ; } +/** +* 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) { -return 0 ; +trace_kvm_wfi (vcpu->arch.regs.pc); +kvm_vcpu_block (vcpu); +return 1 ; }
arch/arm/kvm/trace.h +16 -0
@@ -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