Skip to contentSkip to conversation

KVM/ARM Implementation

PATCH Superseded Christoffer Dall 8 participants 65 messages Nov 10, 2012 – Dec 3, 2012

Subsystems (7)

v4 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v4 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 Linux 3.7-rc4 with kvm/next merged:
git://git.kernel.org/pub/scm/virt/kvm/kvm.git

        branch: next (f026399fc)

This is Version 14 of the patch series, the first 10 versions were reviewed on the KVM/ARM and KVM mailing lists. Changes can also be pulled from:

    git://github.com/virtualopensystems/linux-kvm-arm.git
        branch: kvm-arm-v14
        branch: kvm-arm-v14-vgic
        branch: kvm-arm-v14-vgic-timers

A non-flattened edition of the patch series, which can always be merged, can be found at:
git://github.com/virtualopensystems/linux-kvm-arm.git kvm-arm-master

This patch series requires QEMU compatibility. Use the branch
git://github.com/virtualopensystems/qemu.git kvm-arm

There is also WIP QEMU patches to support virtio on ARM:
git://github.com/virtualopensystems/qemu.git kvm-arm-virtio

Following this patch series, which implements core KVM support, are two other patch series implementing Virtual Generic Interrupt Controller (VGIC) support and Architected Generic Timers. All three patch series should be applied for full QEMU compatibility.

The implementation is broken up into a logical set of patches, the first are preparatory patches:
1. ARM: Add page table defines for KVM
3. ARM: Section based HYP idmaps
3. ARM: Factor out cpuid implementor and part_number fields

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:
4. Skeleton and reset hooks
5. Hypervisor initialization
6. Memory virtualization setup (hyp mode mappings and 2nd stage)
7. Inject IRQs and FIQs from userspace
8. World-switch implementation and Hyp exception vectors
9. Emulation framework and coproc emulation
10. Coproc user space API
11. Demux multiplexed coproc registers
12. User spac API to get/set VFP registers
13. Handle guest user memory aborts
14. Handle guest MMIO aborts

Testing:
Tested on FAST Models and Versatile Express test-chip2. Tested by running three simultaenous VMs, all running SMP, on an SMP host, each VM running hackbench and cyclictest and with extreme memory pressure applied to the host with swapping enabled to provoke page eviction. Also tested KSM merging and GCC inside VMs. Fully boots both Ubuntu (user space Thumb-2) and Debian (user space ARM) guests.

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

Changes since v13:
- Fix VTTBR mask bug
- Change KVM_MAX_VCPUS to config option (defualt 4)
- Go back to struct pt_regs in kvm_regs struct
- Factor out mmio instruction decoding to a separate file with non kvm-specific data structures as the interface.
- Update kvm_device_address struct to use 64-bit fields
- Various cleanups and compile fixes

Changes since v12:
- Documentation updates
- Change Hyp-ABI to function call based paradigm
- Cleanup world-switch code
- Unify HIFAR/HDFAR on the vcpu struct
- Simplify vcpu register access in sofware
- Enforce use of vcpu field accessors
- Factor out mmio handling into separate file
- Check for overlaps in mmio address mappings
- Bugfix in mmio decoding
- Complete rework of ARM mmio load/store instruction

Changes since v11:
- Memory setup and page table defines reworked
- We do not export unused perf bitfields anymore
- No module support anymore and following cleanup
- Hide vcpu register accessors
- Fix unmap range mmu notifier race condition
- Factored out A15 coprocs in separate file
- Factored out world-switch assembly macros to separate file
- Add dmux of multiplexed coprocs to user space
- Add VFP get/set interface to user space
- Addressed various cleanup comments from reviewers

Changes since v10:
- Boot in Hyp mode and user HVC to initialize HVBAR
- Support VGIC
- Support Arch timers
- Support Thumb-2 mmio instruction decoding
- Transition to GET_ONE/SET_ONE register API
- Added KVM_VCPU_GET_REG_LIST
- New interrupt injection API
- Don't pin guest pages anymore
- Fix race condition in page fault handler
- Cleanup guest instruction copying.
- Fix race when copying SMP guest instructions
- Inject data/prefetch aborts when guest does something strange

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 (13):
ARM: Add page table and page defines needed by KVM
ARM: Section based HYP idmap
ARM: Factor out cpuid implementor and part number
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: Demux CCSIDR in the userspace API
KVM: ARM: Handle guest faults in KVM
KVM: ARM: Handle I/O aborts

Rusty Russell (1):
KVM: ARM: VFP userspace interface

 Documentation/virtual/kvm/api.txt           |  135 +++
 arch/arm/Kconfig                            |    2 
 arch/arm/Makefile                           |    1 
 arch/arm/include/asm/cputype.h              |   26 +
 arch/arm/include/asm/idmap.h                |    5 
 arch/arm/include/asm/kvm_arm.h              |  195 +++++
 arch/arm/include/asm/kvm_asm.h              |   84 ++
 arch/arm/include/asm/kvm_coproc.h           |   47 +
 arch/arm/include/asm/kvm_decode.h           |   47 +
 arch/arm/include/asm/kvm_emulate.h          |   63 ++
 arch/arm/include/asm/kvm_host.h             |  157 ++++
 arch/arm/include/asm/kvm_mmio.h             |   51 +
 arch/arm/include/asm/kvm_mmu.h              |   48 +
 arch/arm/include/asm/pgtable-3level-hwdef.h |    5 
 arch/arm/include/asm/pgtable-3level.h       |   18 
 arch/arm/include/asm/pgtable.h              |    7 
 arch/arm/include/uapi/asm/kvm.h             |  124 +++
 arch/arm/kernel/asm-offsets.c               |   23 +
 arch/arm/kernel/perf_event_cpu.c            |   30 -
 arch/arm/kernel/vmlinux.lds.S               |    6 
 arch/arm/kvm/Kconfig                        |   56 +
 arch/arm/kvm/Makefile                       |   22 +
 arch/arm/kvm/arm.c                          |  965 +++++++++++++++++++++++++
 arch/arm/kvm/coproc.c                       | 1045 +++++++++++++++++++++++++++
 arch/arm/kvm/coproc.h                       |  153 ++++
 arch/arm/kvm/coproc_a15.c                   |  164 ++++
 arch/arm/kvm/decode.c                       |  462 ++++++++++++
 arch/arm/kvm/emulate.c                      |  540 ++++++++++++++
 arch/arm/kvm/guest.c                        |  222 ++++++
 arch/arm/kvm/init.S                         |  126 +++
 arch/arm/kvm/interrupts.S                   |  461 ++++++++++++
 arch/arm/kvm/interrupts_head.S              |  409 +++++++++++
 arch/arm/kvm/mmio.c                         |  154 ++++
 arch/arm/kvm/mmu.c                          |  766 ++++++++++++++++++++
 arch/arm/kvm/reset.c                        |   74 ++
 arch/arm/kvm/trace.h                        |  215 ++++++
 arch/arm/mm/idmap.c                         |   74 ++
 arch/arm/mm/mmu.c                           |   25 +
 include/uapi/linux/kvm.h                    |    8 
 mm/memory.c                                 |    2 
 40 files changed, 6981 insertions(+), 36 deletions(-)
 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_decode.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_mmio.h
 create mode 100644 arch/arm/include/asm/kvm_mmu.h
 create mode 100644 arch/arm/include/uapi/asm/kvm.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/coproc.h
 create mode 100644 arch/arm/kvm/coproc_a15.c
 create mode 100644 arch/arm/kvm/decode.c
 create mode 100644 arch/arm/kvm/emulate.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/interrupts_head.S
 create mode 100644 arch/arm/kvm/mmio.c
 create mode 100644 arch/arm/kvm/mmu.c
 create mode 100644 arch/arm/kvm/reset.c
 create mode 100644 arch/arm/kvm/trace.h
#1 lore raw

PATCH v4 1/14 ARM: Add page table and page defines needed by KVM

KVM uses the stage-2 page tables and the Hyp page table format, so we define the fields and page protection flags needed by KVM.

The nomenclature is this:
- page_hyp: PL2 code/data mappings
- page_hyp_device: PL2 device mappings (vgic access)
- page_s2: Stage-2 code/data page mappings
- page_s2_device: Stage-2 device mappings (vgic access)

Christoffer Dall <[email protected]>

---
 arch/arm/include/asm/pgtable-3level.h |   18 ++++++++++++++++++
 arch/arm/include/asm/pgtable.h        |    7 +++++++
 arch/arm/mm/mmu.c                     |   25 +++++++++++++++++++++++++
 3 files changed, 50 insertions(+)
arch/arm/include/asm/pgtable-3level.h +18 -0
index b249035..eaba5a4 100644
@@ -102,11 +102,29 @@
  */
 #define L_PGD_SWAPPER		(_AT(pgdval_t, 1) << 55)	/* swapper_pg_dir entry */
 
+/*
+ * 2nd stage PTE definitions for LPAE.
+ */
+#define L_PTE_S2_MT_UNCACHED	 (_AT(pteval_t, 0x5) << 2) /* MemAttr[3:0] */
+#define L_PTE_S2_MT_WRITETHROUGH (_AT(pteval_t, 0xa) << 2) /* MemAttr[3:0] */
+#define L_PTE_S2_MT_WRITEBACK	 (_AT(pteval_t, 0xf) << 2) /* MemAttr[3:0] */
+#define L_PTE_S2_RDONLY		 (_AT(pteval_t, 1) << 6)   /* HAP[1]   */
+#define L_PTE_S2_RDWR		 (_AT(pteval_t, 2) << 6)   /* HAP[2:1] */
+
+/*
+ * Hyp-mode PL2 PTE definitions for LPAE.
+ */
+#define L_PTE_HYP		L_PTE_USER
+
 #ifndef __ASSEMBLY__
 
 #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 +7 -0
index 08c1231..dfb3918 100644
@@ -70,6 +70,9 @@ 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_hyp_device;
+extern pgprot_t		pgprot_s2;
+extern pgprot_t		pgprot_s2_device;
 
 #define _MOD_PROT(p, b)	__pgprot(pgprot_val(p) | (b))
 
@@ -82,6 +85,10 @@ 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_HYP)
+#define PAGE_HYP_DEVICE		_MOD_PROT(pgprot_hyp_device, L_PTE_HYP)
+#define PAGE_S2			_MOD_PROT(pgprot_s2, L_PTE_S2_RDONLY)
+#define PAGE_S2_DEVICE		_MOD_PROT(pgprot_s2_device, L_PTE_USER | L_PTE_S2_RDONLY)
 
 #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/mm/mmu.c +25 -0
index 941dfb9..087d949 100644
@@ -57,43 +57,61 @@ 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_hyp_device;
+pgprot_t pgprot_s2;
+pgprot_t pgprot_s2_device;
 
 EXPORT_SYMBOL(pgprot_user);
 EXPORT_SYMBOL(pgprot_kernel);
+EXPORT_SYMBOL(pgprot_hyp_device);
+EXPORT_SYMBOL(pgprot_s2);
+EXPORT_SYMBOL(pgprot_s2_device);
 
 struct cachepolicy {
 	const char	policy[16];
 	unsigned int	cr_mask;
 	pmdval_t	pmd;
 	pteval_t	pte;
+	pteval_t	pte_s2;
 };
 
+#ifdef CONFIG_ARM_LPAE
+#define s2_policy(policy)	policy
+#else
+#define s2_policy(policy)	0
+#endif
+
 static struct cachepolicy cache_policies[] __initdata = {
 	{
 		.policy		= "uncached",
 		.cr_mask	= CR_W|CR_C,
 		.pmd		= PMD_SECT_UNCACHED,
 		.pte		= L_PTE_MT_UNCACHED,
+		.pte_s2		= s2_policy(L_PTE_S2_MT_UNCACHED),
 	}, {
 		.policy		= "buffered",
 		.cr_mask	= CR_C,
 		.pmd		= PMD_SECT_BUFFERED,
 		.pte		= L_PTE_MT_BUFFERABLE,
+		.pte_s2		= s2_policy(L_PTE_S2_MT_UNCACHED),
 	}, {
 		.policy		= "writethrough",
 		.cr_mask	= 0,
 		.pmd		= PMD_SECT_WT,
 		.pte		= L_PTE_MT_WRITETHROUGH,
+		.pte_s2		= s2_policy(L_PTE_S2_MT_WRITETHROUGH),
 	}, {
 		.policy		= "writeback",
 		.cr_mask	= 0,
 		.pmd		= PMD_SECT_WB,
 		.pte		= L_PTE_MT_WRITEBACK,
+		.pte_s2		= s2_policy(L_PTE_S2_MT_WRITEBACK),
 	}, {
 		.policy		= "writealloc",
 		.cr_mask	= 0,
 		.pmd		= PMD_SECT_WBWA,
 		.pte		= L_PTE_MT_WRITEALLOC,
+		.pte_s2		= s2_policy(L_PTE_S2_MT_WRITEBACK),
 	}
 };
 
@@ -310,6 +328,7 @@ static void __init build_mem_type_table(void)
 	struct cachepolicy *cp;
 	unsigned int cr = get_cr();
 	pteval_t user_pgprot, kern_pgprot, vecs_pgprot;
+	pteval_t hyp_device_pgprot, s2_pgprot, s2_device_pgprot;
 	int cpu_arch = cpu_architecture();
 	int i;
 
@@ -421,6 +440,8 @@ static void __init build_mem_type_table(void)
 	 */
 	cp = &cache_policies[cachepolicy];
 	vecs_pgprot = kern_pgprot = user_pgprot = cp->pte;
+	s2_pgprot = cp->pte_s2;
+	hyp_device_pgprot = s2_device_pgprot = mem_types[MT_DEVICE].prot_pte;
 
 	/*
 	 * ARMv6 and above have extended page tables.
@@ -444,6 +465,7 @@ static void __init build_mem_type_table(void)
 			user_pgprot |= L_PTE_SHARED;
 			kern_pgprot |= L_PTE_SHARED;
 			vecs_pgprot |= L_PTE_SHARED;
+			s2_pgprot |= L_PTE_SHARED;
 			mem_types[MT_DEVICE_WC].prot_sect |= PMD_SECT_S;
 			mem_types[MT_DEVICE_WC].prot_pte |= L_PTE_SHARED;
 			mem_types[MT_DEVICE_CACHED].prot_sect |= PMD_SECT_S;
@@ -498,6 +520,9 @@ 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_s2  = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | s2_pgprot);
+	pgprot_s2_device  = __pgprot(s2_device_pgprot);
+	pgprot_hyp_device  = __pgprot(hyp_device_pgprot);
 
 	mem_types[MT_LOW_VECTORS].prot_l1 |= ecc_mask;
 	mem_types[MT_HIGH_VECTORS].prot_l1 |= ecc_mask;
#2 lore raw

Re: [PATCH v4 01/14] ARM: Add page table and page defines needed by KVM

Quoted 11 lines · Christoffer Dall

KVM uses the stage-2 page tables and the Hyp page table format, so we define the fields and page protection flags needed by KVM.

The nomenclature is this:
- page_hyp: PL2 code/data mappings
- page_hyp_device: PL2 device mappings (vgic access)
- page_s2: Stage-2 code/data page mappings
- page_s2_device: Stage-2 device mappings (vgic access)

Christoffer Dall <[email protected]>

[...]

Quoted 7 lines
arch/arm/mm/mmu.c +0 -0
index 941dfb9..087d949 100644
@@ -57,43 +57,61 @@ static unsigned int cachepolicy __initdata = CPOLICY_WRITEBACK;
 static unsigned int ecc_mask __initdata = 0;
 pgprot_t pgprot_user;

arch/arm/mm/mmu.c static unsigned int cachepolicy __initdata = CPOLICY_WRITEBACK; · line 68

Quoted 10 lines
arch/arm/mm/mmu.c +6 -0
 pgprot_t pgprot_kernel;
+pgprot_t pgprot_hyp_device;
+pgprot_t pgprot_s2;
+pgprot_t pgprot_s2_device;
 
 EXPORT_SYMBOL(pgprot_user);
 EXPORT_SYMBOL(pgprot_kernel);
+EXPORT_SYMBOL(pgprot_hyp_device);
+EXPORT_SYMBOL(pgprot_s2);
+EXPORT_SYMBOL(pgprot_s2_device);

Do we still need these?

arch/arm/mm/mmu.c static unsigned int cachepolicy __initdata = CPOLICY_WRITEBACK; · line 82

Quoted 13 lines
arch/arm/mm/mmu.c +6 -0
 struct cachepolicy {
 	const char	policy[16];
 	unsigned int	cr_mask;
 	pmdval_t	pmd;
 	pteval_t	pte;
+	pteval_t	pte_s2;
 };
 
+#ifdef CONFIG_ARM_LPAE
+#define s2_policy(policy)	policy
+#else
+#define s2_policy(policy)	0
+#endif

Put the macro in pgtable-{2,3}level.h?

Will

#3 lore raw

Re: [PATCH v4 01/14] ARM: Add page table and page defines needed by KVM

Quoted 27 lines · Will Deacon
Quoted 11 lines · Christoffer Dall

KVM uses the stage-2 page tables and the Hyp page table format, so we define the fields and page protection flags needed by KVM.

The nomenclature is this:
- page_hyp: PL2 code/data mappings
- page_hyp_device: PL2 device mappings (vgic access)
- page_s2: Stage-2 code/data page mappings
- page_s2_device: Stage-2 device mappings (vgic access)

Christoffer Dall <[email protected]>

[...]

Quoted 12 lines
arch/arm/mm/mmu.c +3 -0
index 941dfb9..087d949 100644
@@ -57,43 +57,61 @@ 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_hyp_device;
+pgprot_t pgprot_s2;
+pgprot_t pgprot_s2_device;
 
Quoted 8 lines

arch/arm/mm/mmu.c static unsigned int cachepolicy __initdata = CPOLICY_WRITEBACK; · line 68

Quoted 5 lines
arch/arm/mm/mmu.c +3 -0
 EXPORT_SYMBOL(pgprot_user);
 EXPORT_SYMBOL(pgprot_kernel);
+EXPORT_SYMBOL(pgprot_hyp_device);
+EXPORT_SYMBOL(pgprot_s2);
+EXPORT_SYMBOL(pgprot_s2_device);

Do we still need these?

the exports, nope.

Quoted 8 lines
Quoted 8 lines
arch/arm/mm/mmu.c +1 -0
 struct cachepolicy {
      const char      policy[16];
      unsigned int    cr_mask;
      pmdval_t        pmd;
      pteval_t        pte;
+     pteval_t        pte_s2;
 };
 
Quoted 8 lines

arch/arm/mm/mmu.c static unsigned int cachepolicy __initdata = CPOLICY_WRITEBACK; · line 81

Quoted 5 lines
arch/arm/mm/mmu.c +5 -0
+#ifdef CONFIG_ARM_LPAE
+#define s2_policy(policy)    policy
+#else
+#define s2_policy(policy)    0
+#endif

Put the macro in pgtable-{2,3}level.h?

I think that's weird, defining something far away from where it's used will only make it harder to read, pgtable.h is not included in this file, and there are other #ifdef CONFIG_ARM_LPAE in that file.

Here's the fix from above:

arch/arm/mm/mmu.c +0 -3
index 087d949..46ca62b 100644
@@ -63,9 +63,6 @@ pgprot_t pgprot_s2_device;
 
 EXPORT_SYMBOL(pgprot_user);
 EXPORT_SYMBOL(pgprot_kernel);
-EXPORT_SYMBOL(pgprot_hyp_device);
-EXPORT_SYMBOL(pgprot_s2);
-EXPORT_SYMBOL(pgprot_s2_device);
 
 struct cachepolicy {
 	const char	policy[16];

-Christoffer

#4 lore raw

Re: [PATCH v4 01/14] ARM: Add page table and page defines needed by KVM

Quoted 12 lines · Christoffer Dall
Quoted 10 lines · Will Deacon

Review

Quoted 6 lines · Christoffer Dall
(quoted diff) +5 -0
+#ifdef CONFIG_ARM_LPAE
+#define s2_policy(policy)    policy
+#else
+#define s2_policy(policy)    0
+#endif

Put the macro in pgtable-{2,3}level.h?

Quoted 3 lines

I think that's weird, defining something far away from where it's used will only make it harder to read, pgtable.h is not included in this file, and there are other #ifdef CONFIG_ARM_LPAE in that file.

Of course pgtable.h is included in this file -- we have direct references to L_PTE_MT_UNCACHED, for example, so by your logic we should inline all of that too!

Yes, there are other CONFIG_ARM_LPAE checks in this file, but only where there's a piece of code that is not applicable one way or the other. For data definitions, it's really easy to fix in the headers so please do it there instead.

Will

#5 lore raw

Re: [PATCH v4 01/14] ARM: Add page table and page defines needed by KVM

Quoted 13 lines · Will Deacon
Quoted 12 lines · Christoffer Dall
Quoted 10 lines · Will Deacon

Review

Quoted 6 lines · Christoffer Dall
(quoted diff) +5 -0
+#ifdef CONFIG_ARM_LPAE
+#define s2_policy(policy)    policy
+#else
+#define s2_policy(policy)    0
+#endif

Put the macro in pgtable-{2,3}level.h?

Quoted 8 lines
Quoted 3 lines

I think that's weird, defining something far away from where it's used will only make it harder to read, pgtable.h is not included in this file, and there are other #ifdef CONFIG_ARM_LPAE in that file.

Of course pgtable.h is included in this file -- we have direct references to L_PTE_MT_UNCACHED, for example, so by your logic we should inline all of that too!

no, not at all!

Quoted 6 lines

Yes, there are other CONFIG_ARM_LPAE checks in this file, but only where there's a piece of code that is not applicable one way or the other. For data definitions, it's really easy to fix in the headers so please do it there instead.

I don't see that macro as a data definition, it's a way to avoid compile error when those defines are not there, and I don't see where that would ever be used outside this file, so moving it just makes you go hunting for the definition elsewhere.

Perhaps there's an idea behind these files stating that it's universally better to have things in a header file, but I don't quite see why?

(I can definitely move it, and I don't want to fight over it, I'm merely trying to understand why it would make the code cleaner/better to maintain/etc.)

-Christoffer

#6 lore raw

PATCH v4 2/14 ARM: Section based HYP idmap

In reply to Christoffer Dall

Add a method (hyp_idmap_setup) to populate a hyp pgd with an identity mapping of the code contained in the .hyp.idmap.text section.

Offer a method to drop this identity mapping through hyp_idmap_teardown.

Make all the above depend on CONFIG_ARM_VIRT_EXT and CONFIG_ARM_LPAE.

---
 arch/arm/include/asm/idmap.h                |    5 ++
 arch/arm/include/asm/pgtable-3level-hwdef.h |    1 
 arch/arm/kernel/vmlinux.lds.S               |    6 ++
 arch/arm/mm/idmap.c                         |   74 +++++++++++++++++++++++----
 4 files changed, 73 insertions(+), 13 deletions(-)
arch/arm/include/asm/idmap.h +5 -0
index bf863ed..36708ba 100644
@@ -11,4 +11,9 @@ extern pgd_t *idmap_pgd;
 
 void setup_mm_for_reboot(void);
 
+#ifdef CONFIG_ARM_VIRT_EXT
+void hyp_idmap_teardown(pgd_t *hyp_pgd);
+void hyp_idmap_setup(pgd_t *hyp_pgd);
+#endif
+
 #endif	/* __ASM_IDMAP_H */
arch/arm/include/asm/pgtable-3level-hwdef.h +1 -0
index d795282..a2d404e 100644
@@ -44,6 +44,7 @@
 #define PMD_SECT_XN		(_AT(pmdval_t, 1) << 54)
 #define PMD_SECT_AP_WRITE	(_AT(pmdval_t, 0))
 #define PMD_SECT_AP_READ	(_AT(pmdval_t, 0))
+#define PMD_SECT_AP1		(_AT(pmdval_t, 1) << 6)
 #define PMD_SECT_TEX(x)		(_AT(pmdval_t, 0))
 
 /*
arch/arm/kernel/vmlinux.lds.S +5 -1
index 36ff15b..12fd2eb 100644
@@ -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 +62 -12

107 lines not on this page · raw message

#7 lore raw

Re: [PATCH v4 02/14] ARM: Section based HYP idmap

Quoted 25 lines · Christoffer Dall

Add a method (hyp_idmap_setup) to populate a hyp pgd with an identity mapping of the code contained in the .hyp.idmap.text section.

Offer a method to drop this identity mapping through hyp_idmap_teardown.

Make all the above depend on CONFIG_ARM_VIRT_EXT and CONFIG_ARM_LPAE.

---
 arch/arm/include/asm/idmap.h                |    5 ++
 arch/arm/include/asm/pgtable-3level-hwdef.h |    1 
 arch/arm/kernel/vmlinux.lds.S               |    6 ++
 arch/arm/mm/idmap.c                         |   74 +++++++++++++++++++++++----
 4 files changed, 73 insertions(+), 13 deletions(-)
arch/arm/include/asm/idmap.h +0 -0
index bf863ed..36708ba 100644

arch/arm/include/asm/idmap.h extern pgd_t *idmap_pgd; · line 17

Quoted 7 lines
arch/arm/include/asm/idmap.h +4 -0
@@ -11,4 +11,9 @@ extern pgd_t *idmap_pgd;
 
 void setup_mm_for_reboot(void);
 
+#ifdef CONFIG_ARM_VIRT_EXT
+void hyp_idmap_teardown(pgd_t *hyp_pgd);
+void hyp_idmap_setup(pgd_t *hyp_pgd);
+#endif

I wonder whether the dependency is quite right here. Functionally, we're only dependent on LPAE but it doesn't make sense if !ARM_VIRT_EXT. In fact, I start to question whether CONFIG_ARM_VIRT_EXT is worthwhile at all as it stands. Maybe it would be better to add the LPAE dependency there and make the hyp_stub stuff that's currently in mainline unconditional?

Quoted 19 lines
arch/arm/include/asm/idmap.h +19 -0
+/*
+ * This version actually frees the underlying pmds for all pgds in range and
+ * clear the pgds themselves afterwards.
+ */
+void hyp_idmap_teardown(pgd_t *hyp_pgd)
+{
+	unsigned long addr, end;
+	unsigned long next;
+	pgd_t *pgd = hyp_pgd;
+
+	addr = virt_to_phys(__hyp_idmap_text_start);
+	end = virt_to_phys(__hyp_idmap_text_end);
+
+	pgd += pgd_index(addr);
+	do {
+		next = pgd_addr_end(addr, end);
+		if (!pgd_none_or_clear_bad(pgd))
+			hyp_idmap_del_pmd(pgd, addr);
+	} while (pgd++, addr = next, addr < end);

arch/arm/include/asm/idmap.h extern pgd_t *idmap_pgd;

Quoted 10 lines
arch/arm/include/asm/idmap.h +10 -0
+}
+EXPORT_SYMBOL_GPL(hyp_idmap_teardown);
+
+void hyp_idmap_setup(pgd_t *hyp_pgd)
+{
+	identity_mapping_add(hyp_pgd, __hyp_idmap_text_start,
+			     __hyp_idmap_text_end, PMD_SECT_AP1);
+}
+EXPORT_SYMBOL_GPL(hyp_idmap_setup);
+#endif

Again, do we need these exports?

I also think it might be cleaner to declare the hyp_pgd next to the idmap_pgd then add the hyp_idmap_setup code to init_static_idmap, so that we don't add new entry points to this file. The teardown can all be moved into the kvm/ code as it doesn't need any of the existing idmap functions.

Will

#8 lore raw

Re: [PATCH v4 02/14] ARM: Section based HYP idmap

Quoted 29 lines · Will Deacon
Quoted 28 lines · Christoffer Dall

Add a method (hyp_idmap_setup) to populate a hyp pgd with an identity mapping of the code contained in the .hyp.idmap.text section.

Offer a method to drop this identity mapping through hyp_idmap_teardown.

Make all the above depend on CONFIG_ARM_VIRT_EXT and CONFIG_ARM_LPAE.

---
 arch/arm/include/asm/idmap.h                |    5 ++
 arch/arm/include/asm/pgtable-3level-hwdef.h |    1
 arch/arm/kernel/vmlinux.lds.S               |    6 ++
 arch/arm/mm/idmap.c                         |   74 +++++++++++++++++++++++----
 4 files changed, 73 insertions(+), 13 deletions(-)
arch/arm/include/asm/idmap.h +0 -0
index bf863ed..36708ba 100644
@@ -11,4 +11,9 @@ extern pgd_t *idmap_pgd;
 
 void setup_mm_for_reboot(void);
 
Quoted 12 lines

arch/arm/include/asm/idmap.h extern pgd_t *idmap_pgd; · line 17

Quoted 4 lines
arch/arm/include/asm/idmap.h +4 -0
+#ifdef CONFIG_ARM_VIRT_EXT
+void hyp_idmap_teardown(pgd_t *hyp_pgd);
+void hyp_idmap_setup(pgd_t *hyp_pgd);
+#endif

I wonder whether the dependency is quite right here. Functionally, we're only dependent on LPAE but it doesn't make sense if !ARM_VIRT_EXT. In fact, I start to question whether CONFIG_ARM_VIRT_EXT is worthwhile at all as it stands. Maybe it would be better to add the LPAE dependency there and make the hyp_stub stuff that's currently in mainline unconditional?

perhaps it would be more clean, but it's not something that would break to fix later, and not in that sense something this patch series would depend on. If the hyp_stub stuff is truly compatible with all legacy stuff then yes, we could remove that define, but if not, then I guess it's necessary.

For now, I'll simply remove these ifdef's as Rob pointed out.

Quoted 23 lines
Quoted 23 lines
arch/arm/include/asm/idmap.h +23 -0
+/*
+ * This version actually frees the underlying pmds for all pgds in range and
+ * clear the pgds themselves afterwards.
+ */
+void hyp_idmap_teardown(pgd_t *hyp_pgd)
+{
+     unsigned long addr, end;
+     unsigned long next;
+     pgd_t *pgd = hyp_pgd;
+
+     addr = virt_to_phys(__hyp_idmap_text_start);
+     end = virt_to_phys(__hyp_idmap_text_end);
+
+     pgd += pgd_index(addr);
+     do {
+             next = pgd_addr_end(addr, end);
+             if (!pgd_none_or_clear_bad(pgd))
+                     hyp_idmap_del_pmd(pgd, addr);
+     } while (pgd++, addr = next, addr < end);
+}
+EXPORT_SYMBOL_GPL(hyp_idmap_teardown);
+
+void hyp_idmap_setup(pgd_t *hyp_pgd)
Quoted 8 lines

arch/arm/include/asm/idmap.h

Quoted 6 lines
arch/arm/include/asm/idmap.h +6 -0
+{
+     identity_mapping_add(hyp_pgd, __hyp_idmap_text_start,
+                          __hyp_idmap_text_end, PMD_SECT_AP1);
+}
+EXPORT_SYMBOL_GPL(hyp_idmap_setup);
+#endif

Again, do we need these exports?

no we don't

Quoted 7 lines

I also think it might be cleaner to declare the hyp_pgd next to the idmap_pgd then add the hyp_idmap_setup code to init_static_idmap, so that we don't add new entry points to this file. The teardown can all be moved into the kvm/ code as it doesn't need any of the existing idmap functions.

hmm, we had some attempts at this earlier, but it wasn't all that nice. Allocating the hyp_pgd inside idmap.c requires some more logic, and the #ifdefs inside init_static_idmap are also not pretty. Additionally, other potential users of Hyp mode might have a separate hyp_pgd, in theory.

While KVM is the only current user of hyp_idmap_teardown it's not really KVM specific, and the could theoretically be other users of hyp idmaps. Also, the externs __hyp_idmap_text_<start|end> would have to be either moved to a header file or duplicated inside KVM, which is also not that pretty.

I see how you would like to clean this up, but it's not really hard to read or understand, imho:

The ifdef cleanup patch:

Quoted 1 line

From 9cbe2830b74072b4d7167254562fc06c08f724e1 Mon Sep 17 00:00:00 2001

From: Christoffer Dall <[email protected]>
Date: Thu, 29 Nov 2012 13:56:03 -0500
Subject: [PATCH] KVM: ARM: Remove exports and ifdefs in hyp idmap code

The exports are no longer needed as KVM/ARM cannot be compiled as a module and the the #ifdef is not required around a declaration.

---
 arch/arm/include/asm/idmap.h |    2 --
 arch/arm/mm/idmap.c          |    2 --
 2 files changed, 4 deletions(-)
arch/arm/include/asm/idmap.h +0 -2
index 36708ba..6ddb707 100644
@@ -11,9 +11,7 @@ extern pgd_t *idmap_pgd;
 
 void setup_mm_for_reboot(void);
 
-#ifdef CONFIG_ARM_VIRT_EXT
 void hyp_idmap_teardown(pgd_t *hyp_pgd);
 void hyp_idmap_setup(pgd_t *hyp_pgd);
-#endif
 
 #endif	/* __ASM_IDMAP_H */
arch/arm/mm/idmap.c +0 -2
index ea7430e..0b002ee 100644
@@ -136,14 +136,12 @@ void hyp_idmap_teardown(pgd_t *hyp_pgd)
 			hyp_idmap_del_pmd(pgd, addr);
 	} while (pgd++, addr = next, addr < end);
 }
-EXPORT_SYMBOL_GPL(hyp_idmap_teardown);
 
 void hyp_idmap_setup(pgd_t *hyp_pgd)
 {
 	identity_mapping_add(hyp_pgd, __hyp_idmap_text_start,
 			     __hyp_idmap_text_end, PMD_SECT_AP1);
 }
-EXPORT_SYMBOL_GPL(hyp_idmap_setup);
 #endif
 
 /*
Signature · 3 lines
1.7.9.5

-Christoffer
#9 lore raw

Re: [PATCH v4 02/14] ARM: Section based HYP idmap

Quoted 12 lines · Christoffer Dall
Quoted 7 lines · Will Deacon

I also think it might be cleaner to declare the hyp_pgd next to the idmap_pgd then add the hyp_idmap_setup code to init_static_idmap, so that we don't add new entry points to this file. The teardown can all be moved into the kvm/ code as it doesn't need any of the existing idmap functions.

hmm, we had some attempts at this earlier, but it wasn't all that nice. Allocating the hyp_pgd inside idmap.c requires some more logic, and the #ifdefs inside init_static_idmap are also not pretty.

[...]

Quoted 2 lines

I see how you would like to clean this up, but it's not really hard to read or understand, imho:

I have to disagree. As it stands, idmap.c has *no* entry points for manipulating the page tables, which is a good thing because it puts all the mapping code in one place and makes it both easy to use and easy to reason about. Your patch starts exposing a load of this stuff to the rest of the kernel, which is actively going against the design that we currently have.

Here is a quick, untested patch to fix that (modulo the ARM_VIRT_EXT implies ARM_LPAE change I suggested).

Will

--->8

arch/arm/include/asm/idmap.h +3 -4
index 36708ba..3e79d33 100644
@@ -9,11 +9,10 @@
 
 extern pgd_t *idmap_pgd;
 
-void setup_mm_for_reboot(void);
-
 #ifdef CONFIG_ARM_VIRT_EXT
-void hyp_idmap_teardown(pgd_t *hyp_pgd);
-void hyp_idmap_setup(pgd_t *hyp_pgd);
+extern pgd_t *hyp_pgd;
 #endif
 
+void setup_mm_for_reboot(void);
+
 #endif	/* __ASM_IDMAP_H */
arch/arm/kvm/mmu.c +26 -8
index 59d422f..a6f6134 100644
@@ -33,8 +33,9 @@
 
 #include "trace.h"
 
+extern char  __hyp_idmap_text_start[], __hyp_idmap_text_end[];
+
 static DEFINE_MUTEX(kvm_hyp_pgd_mutex);
-static pgd_t *hyp_pgd;
 
 static void kvm_tlb_flush_vmid(struct kvm *kvm)
 {
@@ -725,15 +726,32 @@ unsigned long kvm_mmu_get_httbr(void)
 
 int kvm_mmu_init(void)
 {
-	hyp_pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL);
-	if (!hyp_pgd)
-		return -ENOMEM;
-
-	hyp_idmap_setup(hyp_pgd);
-	return 0;
+	return hyp_pgd ? 0 : -ENOMEM;
 }
 
 void kvm_mmu_exit(void)
 {
-	hyp_idmap_teardown(hyp_pgd);
+	/*
+	 * This version actually frees the underlying pmds for all pgds
+	 * in range and clear the pgds themselves afterwards.
+	 */
+	unsigned long addr, end;
+	unsigned long next;
+	pgd_t *pgd = hyp_pgd;
+
+	addr = virt_to_phys(__hyp_idmap_text_start);
+	end = virt_to_phys(__hyp_idmap_text_end);
+
+	pgd += pgd_index(addr);
+	do {
+		next = pgd_addr_end(addr, end);
+		if (!pgd_none_or_clear_bad(pgd)) {
+			pud_t *pud = pud_offset(pgd, addr);
+			pmd_t *pmd = pmd_offset(pud, addr);
+
+			pud_clear(pud);
+			clean_pmd_entry(pmd);
+			pmd_free(NULL, (pmd_t *)((unsigned long)pmd & PAGE_MASK));
+		}
+	} while (pgd++, addr = next, addr < end);
 }
arch/arm/mm/idmap.c +22 -44

87 lines not on this page · raw message

#10 lore raw

Re: [PATCH v4 02/14] ARM: Section based HYP idmap

Quoted 16 lines · Will Deacon
Quoted 12 lines · Christoffer Dall
Quoted 7 lines · Will Deacon

I also think it might be cleaner to declare the hyp_pgd next to the idmap_pgd then add the hyp_idmap_setup code to init_static_idmap, so that we don't add new entry points to this file. The teardown can all be moved into the kvm/ code as it doesn't need any of the existing idmap functions.

hmm, we had some attempts at this earlier, but it wasn't all that nice. Allocating the hyp_pgd inside idmap.c requires some more logic, and the #ifdefs inside init_static_idmap are also not pretty.

[...]

Quoted 9 lines
Quoted 2 lines

I see how you would like to clean this up, but it's not really hard to read or understand, imho:

I have to disagree. As it stands, idmap.c has *no* entry points for manipulating the page tables, which is a good thing because it puts all the mapping code in one place and makes it both easy to use and easy to reason about. Your patch starts exposing a load of this stuff to the rest of the kernel, which is actively going against the design that we currently have.

having not been a part of that design discussion, I still don't quite see the harm. On the contrary, now we get code in KVM that makes assumptions about how things are mapped in idmap when unmapping. Also it feels a bit weird that the highly kvm-specific hyp_pgd is now exported everywhere and initialized in idmap.c, but nothing that I can't live with.

In any case, you know much better how the idmap code is viewed, so I'll try to follow your thoughts.

Quoted 4 lines

Here is a quick, untested patch to fix that (modulo the ARM_VIRT_EXT implies ARM_LPAE change I suggested).

thanks, I tweaked it a bit to avoid the over 80-character lines and renamed kvm_mmu_exit to kvm_clear_hyp_idmap, which is what it actually does:

arch/arm/include/asm/idmap.h +3 -4
index 36708ba..3e79d33 100644
@@ -9,11 +9,10 @@
 
 extern pgd_t *idmap_pgd;
 
-void setup_mm_for_reboot(void);
-
 #ifdef CONFIG_ARM_VIRT_EXT
-void hyp_idmap_teardown(pgd_t *hyp_pgd);
-void hyp_idmap_setup(pgd_t *hyp_pgd);
+extern pgd_t *hyp_pgd;
 #endif
 
+void setup_mm_for_reboot(void);
+
 #endif	/* __ASM_IDMAP_H */
arch/arm/include/asm/kvm_mmu.h +1 -1
index 4d55218..c7f939d 100644
@@ -34,5 +34,5 @@ void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);
 
 unsigned long kvm_mmu_get_httbr(void);
 int kvm_mmu_init(void);
-void kvm_mmu_exit(void);
+void kvm_clear_hyp_idmap(void);
 #endif /* __ARM_KVM_MMU_H__ */
arch/arm/kvm/arm.c +1 -1
index b93da23..5b86d27 100644
@@ -1004,7 +1004,7 @@ static int init_hyp_mode(void)
 	/*
 	 * Unmap the identity mapping
 	 */
-	kvm_mmu_exit();
+	kvm_clear_hyp_idmap();
 
 	/*
 	 * Map the Hyp-code called directly from the host
arch/arm/kvm/mmu.c +29 -9
index 59d422f..50ac252 100644
@@ -33,8 +33,9 @@
 
 #include "trace.h"
 
+extern char  __hyp_idmap_text_start[], __hyp_idmap_text_end[];
+
 static DEFINE_MUTEX(kvm_hyp_pgd_mutex);
-static pgd_t *hyp_pgd;
 
 static void kvm_tlb_flush_vmid(struct kvm *kvm)
 {
@@ -725,15 +726,34 @@ unsigned long kvm_mmu_get_httbr(void)
 
 int kvm_mmu_init(void)
 {
-	hyp_pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL);
-	if (!hyp_pgd)
-		return -ENOMEM;
-
-	hyp_idmap_setup(hyp_pgd);
-	return 0;
+	return hyp_pgd ? 0 : -ENOMEM;
 }
 
-void kvm_mmu_exit(void)
+/**
+ * kvm_clear_idmap - remove all idmaps from the hyp pgd
+ *
+ * Free the underlying pmds for all pgds in range and clear the pgds (but
+ * don't free them) afterwards.
+ */
+void kvm_clear_hyp_idmap(void)
 {
-	hyp_idmap_teardown(hyp_pgd);
+	unsigned long addr, end;
+	unsigned long next;
+	pgd_t *pgd = hyp_pgd;
+
+	addr = virt_to_phys(__hyp_idmap_text_start);
+	end = virt_to_phys(__hyp_idmap_text_end);
+
+	pgd += pgd_index(addr);
+	do {
+		next = pgd_addr_end(addr, end);
+		if (pgd_none_or_clear_bad(pgd))
+			continue;
+		pud_t *pud = pud_offset(pgd, addr);
+		pmd_t *pmd = pmd_offset(pud, addr);
+
+		pud_clear(pud);
+		clean_pmd_entry(pmd);
+		pmd_free(NULL, (pmd_t *)((unsigned long)pmd & PAGE_MASK));
+	} while (pgd++, addr = next, addr < end);
 }
arch/arm/mm/idmap.c +0 -0
index ea7430e..c97612e 100644
@@ -86,65 +86,43 @@ static void identity_mapping_add(pgd_t *pgd, const

char *text_start,
} while (pgd++, addr = next, addr != end);
}

-extern char __idmap_text_start[], __idmap_text_end[]; +#ifdef CONFIG_ARM_VIRT_EXT
+pgd_t *hyp_pgd;

-static int __init init_static_idmap(void)
+extern char __hyp_idmap_text_start[], __hyp_idmap_text_end[]; +
+static int __init init_static_idmap_hyp(void)
{
- idmap_pgd = pgd_alloc(&init_mm);
- if (!idmap_pgd)
+ hyp_pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL);
+ if (!hyp_pgd)
return -ENOMEM;

- identity_mapping_add(idmap_pgd, __idmap_text_start,
- __idmap_text_end, 0);
+ identity_mapping_add(hyp_pgd, __hyp_idmap_text_start,
+ __hyp_idmap_text_end, PMD_SECT_AP1);

return 0;
}
-early_initcall(init_static_idmap);
-
-#if defined(CONFIG_ARM_VIRT_EXT) && defined(CONFIG_ARM_LPAE) -static void hyp_idmap_del_pmd(pgd_t *pgd, unsigned long addr) +#else
+static int __init init_static_idmap_hyp(void)
{
- pud_t *pud;
- pmd_t *pmd;
-
- pud = pud_offset(pgd, addr);
- pmd = pmd_offset(pud, addr);
- pud_clear(pud);
- clean_pmd_entry(pmd);
- pmd_free(NULL, (pmd_t *)((unsigned long)pmd & PAGE_MASK));
+ return 0;
}
+#endif

-extern char __hyp_idmap_text_start[], __hyp_idmap_text_end[]; +extern char __idmap_text_start[], __idmap_text_end[];

-/*
- * This version actually frees the underlying pmds for all pgds in range and
- * clear the pgds themselves afterwards.
- */
-void hyp_idmap_teardown(pgd_t *hyp_pgd)
+static int __init init_static_idmap(void)
{
- unsigned long addr, end;
- unsigned long next;
- pgd_t *pgd = hyp_pgd;
-
- addr = virt_to_phys(__hyp_idmap_text_start);
- end = virt_to_phys(__hyp_idmap_text_end);
+ idmap_pgd = pgd_alloc(&init_mm);
+ if (!idmap_pgd)
+ return -ENOMEM;

- pgd += pgd_index(addr);
- do {
- next = pgd_addr_end(addr, end);
- if (!pgd_none_or_clear_bad(pgd))
- hyp_idmap_del_pmd(pgd, addr);
- } while (pgd++, addr = next, addr < end);
-}
-EXPORT_SYMBOL_GPL(hyp_idmap_teardown);
+ identity_mapping_add(idmap_pgd, __idmap_text_start,
+ __idmap_text_end, 0);

-void hyp_idmap_setup(pgd_t *hyp_pgd)
-{
- identity_mapping_add(hyp_pgd, __hyp_idmap_text_start,
- __hyp_idmap_text_end, PMD_SECT_AP1);
+ return init_static_idmap_hyp();
}
-EXPORT_SYMBOL_GPL(hyp_idmap_setup);
-#endif
+early_initcall(init_static_idmap);

/*
* In order to soft-boot, we need to switch to a 1:1 mapping for the
--

-Christoffer

#11 lore raw

Re: [PATCH v4 02/14] ARM: Section based HYP idmap

In reply to Christoffer Dall
Quoted 25 lines · Christoffer Dall

Add a method (hyp_idmap_setup) to populate a hyp pgd with an identity mapping of the code contained in the .hyp.idmap.text section.

Offer a method to drop this identity mapping through hyp_idmap_teardown.

Make all the above depend on CONFIG_ARM_VIRT_EXT and CONFIG_ARM_LPAE.

---
 arch/arm/include/asm/idmap.h                |    5 ++
 arch/arm/include/asm/pgtable-3level-hwdef.h |    1 
 arch/arm/kernel/vmlinux.lds.S               |    6 ++
 arch/arm/mm/idmap.c                         |   74 +++++++++++++++++++++++----
 4 files changed, 73 insertions(+), 13 deletions(-)
arch/arm/include/asm/idmap.h +0 -0
index bf863ed..36708ba 100644

arch/arm/include/asm/idmap.h extern pgd_t *idmap_pgd; · line 17

Quoted 7 lines
arch/arm/include/asm/idmap.h +4 -0
@@ -11,4 +11,9 @@ extern pgd_t *idmap_pgd;
 
 void setup_mm_for_reboot(void);
 
+#ifdef CONFIG_ARM_VIRT_EXT
+void hyp_idmap_teardown(pgd_t *hyp_pgd);
+void hyp_idmap_setup(pgd_t *hyp_pgd);
+#endif

Minor nit. You don't need ifdefs around declarations.

Rob

#12 lore raw

PATCH v4 3/14 ARM: Factor out cpuid implementor and part number

In reply to Christoffer Dall

Decoding the implementor and part number of the CPU id in the CPU ID register is needed by KVM, so we factor it out to share the code.

---
 arch/arm/include/asm/cputype.h   |   26 ++++++++++++++++++++++++++
 arch/arm/kernel/perf_event_cpu.c |   30 +++++++++++++++---------------
 2 files changed, 41 insertions(+), 15 deletions(-)
arch/arm/include/asm/cputype.h +26 -0
index cb47d28..306fb2c 100644
@@ -51,6 +51,22 @@ extern unsigned int processor_id;
 #define read_cpuid_ext(reg) 0
 #endif
 
+#define IMPLEMENTOR_ARM		0x41
+#define IMPLEMENTOR_INTEL	0x69
+
+#define PART_NUMBER_ARM1136	0xB360
+#define PART_NUMBER_ARM1156	0xB560
+#define PART_NUMBER_ARM1176	0xB760
+#define PART_NUMBER_ARM11MPCORE	0xB020
+#define PART_NUMBER_CORTEX_A8	0xC080
+#define PART_NUMBER_CORTEX_A9 	0xC090
+#define PART_NUMBER_CORTEX_A5 	0xC050
+#define PART_NUMBER_CORTEX_A15	0xC0F0
+#define PART_NUMBER_CORTEX_A7	0xC070
+
+#define PART_NUMBER_XSCALE1	0x1
+#define PART_NUMBER_XSCALE2	0x2
+
 /*
  * The CPU ID never changes at run time, so we might as well tell the
  * compiler that it's constant.  Use this function to read the CPU ID
@@ -61,6 +77,16 @@ static inline unsigned int __attribute_const__ read_cpuid_id(void)
 	return read_cpuid(CPUID_ID);
 }
 
+static inline unsigned int __attribute_const__ read_cpuid_implementor(void)
+{
+	return (read_cpuid_id() & 0xFF000000) >> 24;
+}
+
+static inline unsigned int __attribute_const__ read_cpuid_part_number(void)
+{
+	return (read_cpuid_id() & 0xFFF0);
+}
+
 static inline unsigned int __attribute_const__ read_cpuid_cachetype(void)
 {
 	return read_cpuid(CPUID_CACHETYPE);
arch/arm/kernel/perf_event_cpu.c +15 -15
index 8d7d8d4..ff18566 100644
@@ -200,46 +200,46 @@ static struct arm_pmu *__devinit probe_current_pmu(void)
 	struct arm_pmu *pmu = NULL;
 	int cpu = get_cpu();
 	unsigned long cpuid = read_cpuid_id();
-	unsigned long implementor = (cpuid & 0xFF000000) >> 24;
-	unsigned long part_number = (cpuid & 0xFFF0);
+	unsigned long implementor = read_cpuid_implementor();
+	unsigned long part_number = read_cpuid_part_number();
 
 	pr_info("probing PMU on CPU %d\n", cpu);
 
 	/* ARM Ltd CPUs. */
-	if (0x41 == implementor) {
+	if (implementor == IMPLEMENTOR_ARM) {
 		switch (part_number) {
-		case 0xB360:	/* ARM1136 */
-		case 0xB560:	/* ARM1156 */
-		case 0xB760:	/* ARM1176 */
+		case PART_NUMBER_ARM1136:
+		case PART_NUMBER_ARM1156:
+		case PART_NUMBER_ARM1176:
 			pmu = armv6pmu_init();
 			break;
-		case 0xB020:	/* ARM11mpcore */
+		case PART_NUMBER_ARM11MPCORE:
 			pmu = armv6mpcore_pmu_init();
 			break;
-		case 0xC080:	/* Cortex-A8 */
+		case PART_NUMBER_CORTEX_A8:
 			pmu = armv7_a8_pmu_init();
 			break;
-		case 0xC090:	/* Cortex-A9 */
+		case PART_NUMBER_CORTEX_A9:
 			pmu = armv7_a9_pmu_init();
 			break;
-		case 0xC050:	/* Cortex-A5 */
+		case PART_NUMBER_CORTEX_A5:
 			pmu = armv7_a5_pmu_init();
 			break;
-		case 0xC0F0:	/* Cortex-A15 */
+		case PART_NUMBER_CORTEX_A15:
 			pmu = armv7_a15_pmu_init();
 			break;
-		case 0xC070:	/* Cortex-A7 */
+		case PART_NUMBER_CORTEX_A7:
 			pmu = armv7_a7_pmu_init();
 			break;
 		}
 	/* Intel CPUs [xscale]. */
-	} else if (0x69 == implementor) {
+	} else if (implementor == IMPLEMENTOR_INTEL) {
 		part_number = (cpuid >> 13) & 0x7;
 		switch (part_number) {
-		case 1:
+		case PART_NUMBER_XSCALE1:
 			pmu = xscale1pmu_init();
 			break;
-		case 2:
+		case PART_NUMBER_XSCALE2:
 			pmu = xscale2pmu_init();
 			break;
 		}
#13 lore raw

Re: [PATCH v4 03/14] ARM: Factor out cpuid implementor and part number

Quoted 5 lines · Christoffer Dall

Decoding the implementor and part number of the CPU id in the CPU ID register is needed by KVM, so we factor it out to share the code.

[...]

Quoted 13 lines
arch/arm/include/asm/cputype.h +5 -0
index cb47d28..306fb2c 100644
@@ -51,6 +51,22 @@ extern unsigned int processor_id;
 #define read_cpuid_ext(reg) 0
 #endif
 
+#define IMPLEMENTOR_ARM		0x41
+#define IMPLEMENTOR_INTEL	0x69
+
+#define PART_NUMBER_ARM1136	0xB360
+#define PART_NUMBER_ARM1156	0xB560

arch/arm/include/asm/cputype.h extern unsigned int processor_id; · line 68

Quoted 10 lines
arch/arm/include/asm/cputype.h +10 -0
+#define PART_NUMBER_ARM1176	0xB760
+#define PART_NUMBER_ARM11MPCORE	0xB020
+#define PART_NUMBER_CORTEX_A8	0xC080
+#define PART_NUMBER_CORTEX_A9 	0xC090
+#define PART_NUMBER_CORTEX_A5 	0xC050
+#define PART_NUMBER_CORTEX_A15	0xC0F0
+#define PART_NUMBER_CORTEX_A7	0xC070
+
+#define PART_NUMBER_XSCALE1	0x1
+#define PART_NUMBER_XSCALE2	0x2

We should probably prefix these with ARM_CPU_ and make the current names shorter to compensate. e.g. ARM_CPU_PART_1136, ARM_CPU_IMP_ARM ?

Quoted 6 lines
arch/arm/include/asm/cputype.h +0 -0
 /*
  * The CPU ID never changes at run time, so we might as well tell the
  * compiler that it's constant.  Use this function to read the CPU ID
@@ -61,6 +77,16 @@ static inline unsigned int __attribute_const__ read_cpuid_id(void)
 	return read_cpuid(CPUID_ID);
 }

arch/arm/include/asm/cputype.h static inline unsigned int __attribute_const__ read_cpuid_id(void) · line 88

Quoted 10 lines
arch/arm/include/asm/cputype.h +9 -0
 
+static inline unsigned int __attribute_const__ read_cpuid_implementor(void)
+{
+	return (read_cpuid_id() & 0xFF000000) >> 24;
+}
+
+static inline unsigned int __attribute_const__ read_cpuid_part_number(void)
+{
+	return (read_cpuid_id() & 0xFFF0);
+}

Perhaps this should take the implementor as an argument, given that the part number is described differently between implementors. The xscale stuff can then move in here (we'll need to check the xscale docs in case perf is using a subfield -- I can't remember off-hand).

Quoted 59 lines
arch/arm/include/asm/cputype.h +0 -0
 static inline unsigned int __attribute_const__ read_cpuid_cachetype(void)
 {
 	return read_cpuid(CPUID_CACHETYPE);
arch/arm/kernel/perf_event_cpu.c +13 -13
index 8d7d8d4..ff18566 100644
@@ -200,46 +200,46 @@ static struct arm_pmu *__devinit probe_current_pmu(void)
 	struct arm_pmu *pmu = NULL;
 	int cpu = get_cpu();
 	unsigned long cpuid = read_cpuid_id();
-	unsigned long implementor = (cpuid & 0xFF000000) >> 24;
-	unsigned long part_number = (cpuid & 0xFFF0);
+	unsigned long implementor = read_cpuid_implementor();
+	unsigned long part_number = read_cpuid_part_number();
 
 	pr_info("probing PMU on CPU %d\n", cpu);
 
 	/* ARM Ltd CPUs. */
-	if (0x41 == implementor) {
+	if (implementor == IMPLEMENTOR_ARM) {
 		switch (part_number) {
-		case 0xB360:	/* ARM1136 */
-		case 0xB560:	/* ARM1156 */
-		case 0xB760:	/* ARM1176 */
+		case PART_NUMBER_ARM1136:
+		case PART_NUMBER_ARM1156:
+		case PART_NUMBER_ARM1176:
 			pmu = armv6pmu_init();
 			break;
-		case 0xB020:	/* ARM11mpcore */
+		case PART_NUMBER_ARM11MPCORE:
 			pmu = armv6mpcore_pmu_init();
 			break;
-		case 0xC080:	/* Cortex-A8 */
+		case PART_NUMBER_CORTEX_A8:
 			pmu = armv7_a8_pmu_init();
 			break;
-		case 0xC090:	/* Cortex-A9 */
+		case PART_NUMBER_CORTEX_A9:
 			pmu = armv7_a9_pmu_init();
 			break;
-		case 0xC050:	/* Cortex-A5 */
+		case PART_NUMBER_CORTEX_A5:
 			pmu = armv7_a5_pmu_init();
 			break;
-		case 0xC0F0:	/* Cortex-A15 */
+		case PART_NUMBER_CORTEX_A15:
 			pmu = armv7_a15_pmu_init();
 			break;
-		case 0xC070:	/* Cortex-A7 */
+		case PART_NUMBER_CORTEX_A7:
 			pmu = armv7_a7_pmu_init();
 			break;
 		}
 	/* Intel CPUs [xscale]. */
-	} else if (0x69 == implementor) {
+	} else if (implementor == IMPLEMENTOR_INTEL) {
 		part_number = (cpuid >> 13) & 0x7;

arch/arm/kernel/perf_event_cpu.c static struct arm_pmu *__devinit probe_current_pmu(void) · line 245

Quoted 10 lines
arch/arm/kernel/perf_event_cpu.c +2 -2
 		switch (part_number) {
-		case 1:
+		case PART_NUMBER_XSCALE1:
 			pmu = xscale1pmu_init();
 			break;
-		case 2:
+		case PART_NUMBER_XSCALE2:
 			pmu = xscale2pmu_init();
 			break;
 		}

If you stick this one in a separate patch, I can take it via the perf tree (along with the CPUID rework above).

Will

#14 lore raw

Re: [PATCH v4 03/14] ARM: Factor out cpuid implementor and part number

Quoted 122 lines · Will Deacon
Quoted 5 lines · Christoffer Dall

Decoding the implementor and part number of the CPU id in the CPU ID register is needed by KVM, so we factor it out to share the code.

[...]

Quoted 13 lines
arch/arm/include/asm/cputype.h +5 -0
index cb47d28..306fb2c 100644
@@ -51,6 +51,22 @@ extern unsigned int processor_id;
 #define read_cpuid_ext(reg) 0
 #endif
 
+#define IMPLEMENTOR_ARM              0x41
+#define IMPLEMENTOR_INTEL    0x69
+
+#define PART_NUMBER_ARM1136  0xB360
+#define PART_NUMBER_ARM1156  0xB560

arch/arm/include/asm/cputype.h extern unsigned int processor_id; · line 68

Quoted 10 lines
arch/arm/include/asm/cputype.h +10 -0
+#define PART_NUMBER_ARM1176  0xB760
+#define PART_NUMBER_ARM11MPCORE      0xB020
+#define PART_NUMBER_CORTEX_A8        0xC080
+#define PART_NUMBER_CORTEX_A9        0xC090
+#define PART_NUMBER_CORTEX_A5        0xC050
+#define PART_NUMBER_CORTEX_A15       0xC0F0
+#define PART_NUMBER_CORTEX_A7        0xC070
+
+#define PART_NUMBER_XSCALE1  0x1
+#define PART_NUMBER_XSCALE2  0x2

We should probably prefix these with ARM_CPU_ and make the current names shorter to compensate. e.g. ARM_CPU_PART_1136, ARM_CPU_IMP_ARM ?

Quoted 6 lines
arch/arm/include/asm/cputype.h +0 -0
 /*
  * The CPU ID never changes at run time, so we might as well tell the
  * compiler that it's constant.  Use this function to read the CPU ID
@@ -61,6 +77,16 @@ static inline unsigned int __attribute_const__ read_cpuid_id(void)
      return read_cpuid(CPUID_ID);
 }

arch/arm/include/asm/cputype.h static inline unsigned int __attribute_const__ read_cpuid_id(void) · line 88

Quoted 10 lines
arch/arm/include/asm/cputype.h +9 -0
 
+static inline unsigned int __attribute_const__ read_cpuid_implementor(void)
+{
+     return (read_cpuid_id() & 0xFF000000) >> 24;
+}
+
+static inline unsigned int __attribute_const__ read_cpuid_part_number(void)
+{
+     return (read_cpuid_id() & 0xFFF0);
+}

Perhaps this should take the implementor as an argument, given that the part number is described differently between implementors. The xscale stuff can then move in here (we'll need to check the xscale docs in case perf is using a subfield -- I can't remember off-hand).

Quoted 64 lines
arch/arm/include/asm/cputype.h +0 -0
 static inline unsigned int __attribute_const__ read_cpuid_cachetype(void)
 {
      return read_cpuid(CPUID_CACHETYPE);
arch/arm/kernel/perf_event_cpu.c +14 -14
index 8d7d8d4..ff18566 100644
@@ -200,46 +200,46 @@ static struct arm_pmu *__devinit probe_current_pmu(void)
      struct arm_pmu *pmu = NULL;
      int cpu = get_cpu();
      unsigned long cpuid = read_cpuid_id();
-     unsigned long implementor = (cpuid & 0xFF000000) >> 24;
-     unsigned long part_number = (cpuid & 0xFFF0);
+     unsigned long implementor = read_cpuid_implementor();
+     unsigned long part_number = read_cpuid_part_number();
 
      pr_info("probing PMU on CPU %d\n", cpu);
 
      /* ARM Ltd CPUs. */
-     if (0x41 == implementor) {
+     if (implementor == IMPLEMENTOR_ARM) {
              switch (part_number) {
-             case 0xB360:    /* ARM1136 */
-             case 0xB560:    /* ARM1156 */
-             case 0xB760:    /* ARM1176 */
+             case PART_NUMBER_ARM1136:
+             case PART_NUMBER_ARM1156:
+             case PART_NUMBER_ARM1176:
                      pmu = armv6pmu_init();
                      break;
-             case 0xB020:    /* ARM11mpcore */
+             case PART_NUMBER_ARM11MPCORE:
                      pmu = armv6mpcore_pmu_init();
                      break;
-             case 0xC080:    /* Cortex-A8 */
+             case PART_NUMBER_CORTEX_A8:
                      pmu = armv7_a8_pmu_init();
                      break;
-             case 0xC090:    /* Cortex-A9 */
+             case PART_NUMBER_CORTEX_A9:
                      pmu = armv7_a9_pmu_init();
                      break;
-             case 0xC050:    /* Cortex-A5 */
+             case PART_NUMBER_CORTEX_A5:
                      pmu = armv7_a5_pmu_init();
                      break;
-             case 0xC0F0:    /* Cortex-A15 */
+             case PART_NUMBER_CORTEX_A15:
                      pmu = armv7_a15_pmu_init();
                      break;
-             case 0xC070:    /* Cortex-A7 */
+             case PART_NUMBER_CORTEX_A7:
                      pmu = armv7_a7_pmu_init();
                      break;
              }
      /* Intel CPUs [xscale]. */
-     } else if (0x69 == implementor) {
+     } else if (implementor == IMPLEMENTOR_INTEL) {
              part_number = (cpuid >> 13) & 0x7;
              switch (part_number) {
-             case 1:
+             case PART_NUMBER_XSCALE1:
                      pmu = xscale1pmu_init();
                      break;
Quoted 9 lines

arch/arm/kernel/perf_event_cpu.c static struct arm_pmu *__devinit probe_current_pmu(void) · line 245

Quoted 5 lines
arch/arm/kernel/perf_event_cpu.c +1 -1
-             case 2:
+             case PART_NUMBER_XSCALE2:
                      pmu = xscale2pmu_init();
                      break;
              }

If you stick this one in a separate patch, I can take it via the perf tree (along with the CPUID rework above).

thanks,
I sent a separate patch.

-Christoffer

#15 lore raw

Re: [PATCH v4 03/14] ARM: Factor out cpuid implementor and part number

Quoted 7 lines · Christoffer Dall
Quoted 6 lines · Will Deacon
Quoted 5 lines · Christoffer Dall

Decoding the implementor and part number of the CPU id in the CPU ID register is needed by KVM, so we factor it out to share the code.

[...]

Quoted 14 lines
Quoted 14 lines

arch/arm/include/asm/cputype.h static inline unsigned int __attribute_const__ read_cpuid_id(void) · line 88

Quoted 9 lines
arch/arm/include/asm/cputype.h +9 -0
+static inline unsigned int __attribute_const__ read_cpuid_implementor(void)
+{
+     return (read_cpuid_id() & 0xFF000000) >> 24;
+}
+
+static inline unsigned int __attribute_const__ read_cpuid_part_number(void)
+{
+     return (read_cpuid_id() & 0xFFF0);
+}

Perhaps this should take the implementor as an argument, given that the part number is described differently between implementors. The xscale stuff can then move in here (we'll need to check the xscale docs in case perf is using a subfield -- I can't remember off-hand).

[...]

Quoted 5 lines
Quoted 3 lines

If you stick this one in a separate patch, I can take it via the perf tree (along with the CPUID rework above).

thanks,
I sent a separate patch.

Looks like we still have the ugly xscale cpuid parsing inline. Could you move it as I suggested, please?

Will

#16 lore raw

Re: [PATCH v4 03/14] ARM: Factor out cpuid implementor and part number

Quoted 28 lines · Will Deacon
Quoted 7 lines · Christoffer Dall
Quoted 6 lines · Will Deacon
Quoted 5 lines · Christoffer Dall

Decoding the implementor and part number of the CPU id in the CPU ID register is needed by KVM, so we factor it out to share the code.

[...]

Quoted 14 lines
Quoted 14 lines

arch/arm/include/asm/cputype.h static inline unsigned int __attribute_const__ read_cpuid_id(void) · line 88

Quoted 9 lines
arch/arm/include/asm/cputype.h +9 -0
+static inline unsigned int __attribute_const__ read_cpuid_implementor(void)
+{
+     return (read_cpuid_id() & 0xFF000000) >> 24;
+}
+
+static inline unsigned int __attribute_const__ read_cpuid_part_number(void)
+{
+     return (read_cpuid_id() & 0xFFF0);
+}

Perhaps this should take the implementor as an argument, given that the part number is described differently between implementors. The xscale stuff can then move in here (we'll need to check the xscale docs in case perf is using a subfield -- I can't remember off-hand).

[...]

Quoted 9 lines
Quoted 5 lines
Quoted 3 lines

If you stick this one in a separate patch, I can take it via the perf tree (along with the CPUID rework above).

thanks,
I sent a separate patch.

Looks like we still have the ugly xscale cpuid parsing inline. Could you move it as I suggested, please?

yes, sorry I missed that one. I went hunting through some old Xscale docs, but cannot find anything that specifies more details about the part number (or "product number" as intel calls it), so I preserved the existing bit parsing.

-Christoffer

#17 lore raw

PATCH v4 4/14 KVM: ARM: Initial skeleton to compile KVM support

In reply to Christoffer Dall

Targets KVM support for Cortex A-15 processors.

Contains all the framework components, make files, header files, some tracing functionality, and basic user space API.

Only supported core is Cortex-A15 for now.

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

---
 Documentation/virtual/kvm/api.txt  |   58 ++++++
 arch/arm/Kconfig                   |    2 
 arch/arm/Makefile                  |    1 
 arch/arm/include/asm/kvm_arm.h     |   24 +++
 arch/arm/include/asm/kvm_asm.h     |   58 ++++++
 arch/arm/include/asm/kvm_coproc.h  |   24 +++
 arch/arm/include/asm/kvm_emulate.h |   50 +++++
 arch/arm/include/asm/kvm_host.h    |  114 ++++++++++++
 arch/arm/include/uapi/asm/kvm.h    |   82 +++++++++
 arch/arm/kvm/Kconfig               |   55 ++++++
 arch/arm/kvm/Makefile              |   21 ++
 arch/arm/kvm/arm.c                 |  343 ++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/coproc.c              |   22 ++
 arch/arm/kvm/emulate.c             |  149 ++++++++++++++++
 arch/arm/kvm/guest.c               |  221 +++++++++++++++++++++++
 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/uapi/linux/kvm.h           |    7 +
 21 files changed, 1408 insertions(+), 4 deletions(-)
 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/uapi/asm/kvm.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/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
Documentation/virtual/kvm/api.txt +54 -4
index 6671fdc..2a5dc41 100644
@@ -293,7 +293,7 @@ kvm_run' (see below).
 4.11 KVM_GET_REGS
 
 Capability: basic
-Architectures: all
+Architectures: all except ARM
 Type: vcpu ioctl
 Parameters: struct kvm_regs (out)
 Returns: 0 on success, -1 on error
@@ -314,7 +314,7 @@ struct kvm_regs {
 4.12 KVM_SET_REGS
 
 Capability: basic
-Architectures: all
+Architectures: all except ARM
 Type: vcpu ioctl
 Parameters: struct kvm_regs (in)
 Returns: 0 on success, -1 on error
@@ -600,7 +600,7 @@ struct kvm_fpu {
 4.24 KVM_CREATE_IRQCHIP
 
 Capability: KVM_CAP_IRQCHIP
-Architectures: x86, ia64
+Architectures: x86, ia64, ARM
 Type: vm ioctl
 Parameters: none
 Returns: 0 on success, -1 on error
@@ -608,7 +608,8 @@ Returns: 0 on success, -1 on error
 Creates an interrupt controller model in the kernel.  On x86, creates a virtual
 ioapic, a virtual PIC (two PICs, nested), and sets up future vcpus to have a
 local APIC.  IRQ routing for GSIs 0-15 is set to both PIC and IOAPIC; GSI 16-23
-only go to the IOAPIC.  On ia64, a IOSAPIC is created.
+only go to the IOAPIC.  On ia64, a IOSAPIC is created. On ARM, a GIC is
+created.
 
 
 4.25 KVM_IRQ_LINE
@@ -1774,6 +1775,14 @@ registers, find a list below:
   PPC   | KVM_REG_PPC_VPA_SLB   | 128
   PPC   | KVM_REG_PPC_VPA_DTL   | 128
 
+ARM registers are mapped using the lower 32 bits.  The upper 16 of that
+is the register group type, or coprocessor number:
+
+ARM core registers have the following id bit patterns:
+  0x4002 0000 0010 <index into the kvm_regs struct:16>
+
+
+
 4.69 KVM_GET_ONE_REG
 
 Capability: KVM_CAP_ONE_REG
@@ -1791,6 +1800,7 @@ The list of registers accessible using this interface is identical to the
 list in 4.68.
 
 
+
 4.70 KVM_KVMCLOCK_CTRL
 
 Capability: KVM_CAP_KVMCLOCK_CTRL
@@ -2072,6 +2082,46 @@ KVM_S390_INT_EXTERNAL_CALL (vcpu) - sigp external call; source cpu in parm
 Note that the vcpu ioctl is asynchronous to vcpu execution.
 
 
+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.
+
+
+4.78 KVM_GET_REG_LIST
+
+Capability: basic
+Architectures: arm
+Type: vcpu ioctl
+Parameters: struct kvm_reg_list (in/out)
+Returns: 0 on success; -1 on error
+Errors:
+ ?E2BIG: ????the reg index list is too big to fit in the array specified by
+ ????????????the user (the number required will be written into n).
+
+struct kvm_reg_list {
+	__u64 n; /* number of registers in reg[] */
+	__u64 reg[0];
+};
+
+This ioctl returns the guest registers that are supported for the
+KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
+
+
 5. The kvm_run structure
 ------------------------
 
arch/arm/Kconfig +2 -0
index ade7e92..43a997a 100644
@@ -2311,3 +2311,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "arch/arm/kvm/Kconfig"
arch/arm/Makefile +1 -0
index 5f914fc..433becd 100644
@@ -257,6 +257,7 @@ core-$(CONFIG_XEN)		+= arch/arm/xen/
 core-y				+= arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
 core-y				+= arch/arm/net/
 core-y				+= arch/arm/crypto/
+core-y 				+= arch/arm/kvm/
 core-y				+= $(machdirs) $(platdirs)
 
 drivers-$(CONFIG_OPROFILE)      += arch/arm/oprofile/
arch/arm/include/asm/kvm_arm.h new file +24 -0
new file mode 100644
index 0000000..c196a22
@@ -0,0 +1,24 @@
+/*
+ * 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__
+
+#include <asm/types.h>
+
+#endif /* __ARM_KVM_ARM_H__ */
arch/arm/include/asm/kvm_asm.h new file +58 -0

58 lines not on this page · raw message

arch/arm/include/asm/kvm_coproc.h new file +24 -0
new file mode 100644
index 0000000..b6d023d
@@ -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 +50 -0

50 lines not on this page · raw message

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

114 lines not on this page · raw message

arch/arm/include/uapi/asm/kvm.h new file +82 -0

82 lines not on this page · raw message

arch/arm/kvm/Kconfig new file +55 -0

55 lines not on this page · raw message

arch/arm/kvm/Makefile new file +21 -0
new file mode 100644
index 0000000..7acf3ea
@@ -0,0 +1,21 @@
+#
+# 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. $(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
+
+obj-$(CONFIG_KVM_ARM_HOST) += $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
+
+obj-$(CONFIG_KVM_ARM_HOST) += arm.o guest.o mmu.o emulate.o reset.o coproc.o
arch/arm/kvm/arm.c new file +343 -0

343 lines not on this page · raw message

arch/arm/kvm/coproc.c new file +22 -0
new file mode 100644
index 0000000..4b9dad8
@@ -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 +149 -0

149 lines not on this page · raw message

arch/arm/kvm/guest.c new file +221 -0

221 lines not on this page · raw message

arch/arm/kvm/init.S new file +19 -0
new file mode 100644
index 0000000..1dc8926
@@ -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
new file mode 100644
index 0000000..1dc8926
@@ -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
new file mode 100644
index 0000000..10ed464
@@ -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/uapi/linux/kvm.h +7 -0
index 494a84c..ad15f10 100644
@@ -763,6 +763,11 @@ struct kvm_dirty_tlb {
 #define KVM_REG_SIZE_U512	0x0060000000000000ULL
 #define KVM_REG_SIZE_U1024	0x0070000000000000ULL
 
+struct kvm_reg_list {
+	__u64 n; /* number of regs */
+	__u64 reg[0];
+};
+
 struct kvm_one_reg {
 	__u64 id;
 	__u64 addr;
@@ -929,6 +934,8 @@ 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_GET_REG_LIST	  _IOWR(KVMIO, 0xb0, struct kvm_reg_list)
 
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU	(1 << 0)
 #define KVM_DEV_ASSIGN_PCI_2_3		(1 << 1)
#18 lore raw

Re: [PATCH v4 04/14] KVM: ARM: Initial skeleton to compile KVM support

Quoted 13 lines · Christoffer Dall

Targets KVM support for Cortex A-15 processors.

Contains all the framework components, make files, header files, some tracing functionality, and basic user space API.

Only supported core is Cortex-A15 for now.

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

[...]

Documentation/virtual/kvm/api.txt The list of registers accessible using this interface is identical to the · line 1803

Quoted 8 lines

4.69 KVM_GET_ONE_REG

Capability: KVM_CAP_ONE_REG

Documentation/virtual/kvm/api.txt +1 -0
@@ -1791,6 +1800,7 @@ The list of registers accessible using this interface is identical to the
 list in 4.68.
 
 
+

Whitespace.

Quoted 65 lines
Documentation/virtual/kvm/api.txt +40 -0
 4.70 KVM_KVMCLOCK_CTRL
 
 Capability: KVM_CAP_KVMCLOCK_CTRL
@@ -2072,6 +2082,46 @@ KVM_S390_INT_EXTERNAL_CALL (vcpu) - sigp external call; source cpu in parm
 Note that the vcpu ioctl is asynchronous to vcpu execution.
 
 
+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.
+
+
+4.78 KVM_GET_REG_LIST
+
+Capability: basic
+Architectures: arm
+Type: vcpu ioctl
+Parameters: struct kvm_reg_list (in/out)
+Returns: 0 on success; -1 on error
+Errors:
+  E2BIG:     the reg index list is too big to fit in the array specified by
+             the user (the number required will be written into n).
+
+struct kvm_reg_list {
+       __u64 n; /* number of registers in reg[] */
+       __u64 reg[0];
+};
+
+This ioctl returns the guest registers that are supported for the
+KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
+
+
 5. The kvm_run structure
 ------------------------
 
arch/arm/Kconfig +2 -0
index ade7e92..43a997a 100644
@@ -2311,3 +2311,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "arch/arm/kvm/Kconfig"
arch/arm/Makefile +0 -0
index 5f914fc..433becd 100644

arch/arm/Makefile core-$(CONFIG_XEN) += arch/arm/xen/ · line 260

Quoted 4 lines
arch/arm/Makefile +1 -0
@@ -257,6 +257,7 @@ core-$(CONFIG_XEN)          += arch/arm/xen/
 core-y                         += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
 core-y                         += arch/arm/net/
 core-y                         += arch/arm/crypto/
+core-y                                 += arch/arm/kvm/

Predicate this on CONFIG_KVM_ARM_HOST, then add some obj-y to the kvm/Makefile.

Quoted 24 lines
arch/arm/Makefile +0 -0
 core-y                         += $(machdirs) $(platdirs)
 
 drivers-$(CONFIG_OPROFILE)      += arch/arm/oprofile/
arch/arm/include/asm/kvm_host.h new file +15 -0
new file mode 100644
index 0000000..67826b2
@@ -0,0 +1,114 @@
+/*
+ * 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

arch/arm/include/asm/kvm_host.h line 25

Quoted 10 lines
arch/arm/include/asm/kvm_host.h new file +10 -0
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __ARM_KVM_HOST_H__
+#define __ARM_KVM_HOST_H__
+
+#include <asm/kvm.h>
+#include <asm/kvm_asm.h>
+
+#define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS

One thing I noticed when hacking kvmtool is that we don't support the KVM_CAP_{MAX,NR}_VCPUS capabilities. Why is this? Since we have a maximum of 8 due to limitations of the GIC, it might be nice to put that somewhere visible to userspace.

Quoted 49 lines
arch/arm/include/asm/kvm_host.h new file +49 -0
+#define KVM_MEMORY_SLOTS 32
+#define KVM_PRIVATE_MEM_SLOTS 4
+#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
+
+#define KVM_VCPU_MAX_FEATURES 0
+
+/* We don't currently support large pages. */
+#define KVM_HPAGE_GFN_SHIFT(x) 0
+#define KVM_NR_PAGE_SIZES      1
+#define KVM_PAGES_PER_HPAGE(x) (1UL<<31)
+
+struct kvm_vcpu;
+u32 *kvm_vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode);
+int kvm_target_cpu(void);
+int kvm_reset_vcpu(struct kvm_vcpu *vcpu);
+void kvm_reset_coprocs(struct kvm_vcpu *vcpu);
+
+struct kvm_arch {
+       /* VTTBR value associated with below pgd and vmid */
+       u64    vttbr;
+
+       /*
+        * Anything that is not used directly from assembly code goes
+        * here.
+        */
+
+       /* The VMID generation used for the virt. memory system */
+       u64    vmid_gen;
+       u32    vmid;
+
+       /* Stage-2 page table */
+       pgd_t *pgd;
+};
+
+#define KVM_NR_MEM_OBJS     40
+
+/*
+ * We don't want allocation failures within the mmu code, so we preallocate
+ * enough memory for a single page fault in a cache.
+ */
+struct kvm_mmu_memory_cache {
+       int nobjs;
+       void *objects[KVM_NR_MEM_OBJS];
+};
+
+struct kvm_vcpu_arch {
+       struct kvm_regs regs;
+
+       int target; /* Processor target */

arch/arm/include/asm/kvm_host.h line 84

Quoted 10 lines
arch/arm/include/asm/kvm_host.h new file +10 -0
+       DECLARE_BITMAP(features, KVM_VCPU_MAX_FEATURES);
+
+       /* System control coprocessor (cp15) */
+       u32 cp15[NR_CP15_REGS];
+
+       /* The CPU type we expose to the VM */
+       u32 midr;
+
+       /* Exception Information */
+       u32 hsr;                /* Hyp Syndrom Register */

pedantic: syndrome

Quoted 37 lines
arch/arm/include/uapi/asm/kvm.h new file +31 -0
new file mode 100644
index 0000000..00d61a6
@@ -0,0 +1,82 @@
+/*
+ * 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_H__
+#define __ARM_KVM_H__
+
+#include <asm/types.h>
+#include <asm/ptrace.h>
+
+#define __KVM_HAVE_GUEST_DEBUG
+
+#define KVM_REG_SIZE(id)                                               \
+       (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT))
+
+struct kvm_regs {
+       struct pt_regs usr_regs;/* R0_usr - R14_usr, PC, CPSR */

arch/arm/include/uapi/asm/kvm.h line 41

Quoted 10 lines
arch/arm/include/uapi/asm/kvm.h new file +10 -0
+       __u32 svc_regs[3];      /* SP_svc, LR_svc, SPSR_svc */
+       __u32 abt_regs[3];      /* SP_abt, LR_abt, SPSR_abt */
+       __u32 und_regs[3];      /* SP_und, LR_und, SPSR_und */
+       __u32 irq_regs[3];      /* SP_irq, LR_irq, SPSR_irq */
+       __u32 fiq_regs[8];      /* R8_fiq - R14_fiq, SPSR_fiq */
+};
+
+/* Supported Processor Types */
+#define KVM_ARM_TARGET_CORTEX_A15      0
+#define KVM_ARM_NUM_TARGETS            1

In future, it would be nice to have a way of probing the supported targets, rather than trying a bunch of them and seeing which ioctl finally succeeds.

Quoted 34 lines
arch/arm/kvm/Kconfig new file +28 -0
new file mode 100644
index 0000000..c7eb178
@@ -0,0 +1,55 @@
+#
+# KVM configuration
+#
+
+source "virt/kvm/Kconfig"
+
+menuconfig VIRTUALIZATION
+       bool "Virtualization"
+       ---help---
+         Say Y here to get to see options for using your Linux host to run
+         other operating systems inside virtual machines (guests).
+         This option alone does not add any kernel code.
+
+         If you say N, all options in this submenu will be skipped and
+         disabled.
+
+if VIRTUALIZATION
+
+config KVM
+       bool "Kernel-based Virtual Machine (KVM) support"
+       select PREEMPT_NOTIFIERS
+       select ANON_INODES
+       select KVM_MMIO
+       depends on ARM_VIRT_EXT && ARM_LPAE
+       ---help---
+         Support hosting virtualized guest machines. You will also
+         need to select one or more of the processor modules below.
+

arch/arm/kvm/Kconfig line 38

Quoted 10 lines
arch/arm/kvm/Kconfig new file +10 -0
+         This module provides access to the hardware capabilities through
+         a character device node named /dev/kvm.
+
+         If unsure, say N.
+
+config KVM_ARM_HOST
+       bool "KVM host support for ARM cpus."
+       depends on KVM
+       depends on MMU
+       depends on CPU_V7 && ARM_VIRT_EXT

ARM_VIRT_EXT already implies CPU_V7 but, as I suggested earlier, it should probably imply ARM_LPAE which would make much of this simpler.

Quoted 7 lines
arch/arm/kvm/Kconfig new file +7 -0
+       ---help---
+         Provides host support for ARM processors.
+
+config KVM_ARM_MAX_VCPUS
+       int "Number maximum supported virtual CPUs per VM"
+       depends on KVM_ARM_HOST
+       default 4

arch/arm/kvm/Kconfig line 55

Quoted 10 lines
arch/arm/kvm/Kconfig new file +10 -0
+       help
+         Static number of max supported virtual CPUs per VM.
+
+         If you choose a high number, the vcpu structures will be quite
+         large, so only choose a reasonable number that you expect to
+         actually use.
+
+source drivers/virtio/Kconfig
+
+endif # VIRTUALIZATION

I guess the XEN config options should also be moved under here?

Quoted 5 lines
arch/arm/kvm/arm.c new file +0 -0
new file mode 100644
index 0000000..ead5a4e

[...]

Quoted 35 lines
arch/arm/kvm/arm.c new file +35 -0
+long kvm_arch_vcpu_ioctl(struct file *filp,
+                        unsigned int ioctl, unsigned long arg)
+{
+       struct kvm_vcpu *vcpu = filp->private_data;
+       void __user *argp = (void __user *)arg;
+
+       switch (ioctl) {
+       case KVM_ARM_VCPU_INIT: {
+               struct kvm_vcpu_init init;
+
+               if (copy_from_user(&init, argp, sizeof init))
+                       return -EFAULT;
+
+               return kvm_vcpu_set_target(vcpu, &init);
+
+       }
+       case KVM_SET_ONE_REG:
+       case KVM_GET_ONE_REG: {
+               struct kvm_one_reg reg;
+               if (copy_from_user(&reg, argp, sizeof(reg)))
+                       return -EFAULT;
+               if (ioctl == KVM_SET_ONE_REG)
+                       return kvm_arm_set_reg(vcpu, &reg);
+               else
+                       return kvm_arm_get_reg(vcpu, &reg);
+       }
+       case KVM_GET_REG_LIST: {
+               struct kvm_reg_list __user *user_list = argp;
+               struct kvm_reg_list reg_list;
+               unsigned n;
+
+               if (copy_from_user(&reg_list, user_list, sizeof reg_list))
+                       return -EFAULT;
+               n = reg_list.n;
+               reg_list.n = kvm_arm_num_regs(vcpu);

arch/arm/kvm/arm.c line 314

Quoted 10 lines
arch/arm/kvm/arm.c new file +10 -0
+               if (copy_to_user(user_list, &reg_list, sizeof reg_list))
+                       return -EFAULT;
+               if (n < reg_list.n)
+                       return -E2BIG;
+               return kvm_arm_copy_reg_indices(vcpu, user_list->reg);
+       }
+       default:
+               return -EINVAL;
+       }
+}

Your use of brackets with sizeof is inconsistent -- just always make it look like a function call.

Quoted 123 lines
arch/arm/kvm/emulate.c new file +117 -0
new file mode 100644
index 0000000..32e1172
@@ -0,0 +1,149 @@
+/*
+ * 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/kvm_emulate.h>
+
+#define VCPU_NR_MODES 6
+#define REG_OFFSET(_reg) \
+       (offsetof(struct kvm_regs, _reg) / sizeof(u32))
+
+#define USR_REG_OFFSET(_num) REG_OFFSET(usr_regs.uregs[_num])
+
+static const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][15] = {
+       /* USR/SYS Registers */
+       {
+               USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2),
+               USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5),
+               USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
+               USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11),
+               USR_REG_OFFSET(12), USR_REG_OFFSET(13), USR_REG_OFFSET(14),
+       },
+
+       /* FIQ Registers */
+       {
+               USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2),
+               USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5),
+               USR_REG_OFFSET(6), USR_REG_OFFSET(7),
+               REG_OFFSET(fiq_regs[0]), /* r8 */
+               REG_OFFSET(fiq_regs[1]), /* r9 */
+               REG_OFFSET(fiq_regs[2]), /* r10 */
+               REG_OFFSET(fiq_regs[3]), /* r11 */
+               REG_OFFSET(fiq_regs[4]), /* r12 */
+               REG_OFFSET(fiq_regs[5]), /* r13 */
+               REG_OFFSET(fiq_regs[6]), /* r14 */
+       },
+
+       /* IRQ Registers */
+       {
+               USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2),
+               USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5),
+               USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
+               USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11),
+               USR_REG_OFFSET(12),
+               REG_OFFSET(irq_regs[0]), /* r13 */
+               REG_OFFSET(irq_regs[1]), /* r14 */
+       },
+
+       /* SVC Registers */
+       {
+               USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2),
+               USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5),
+               USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
+               USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11),
+               USR_REG_OFFSET(12),
+               REG_OFFSET(svc_regs[0]), /* r13 */
+               REG_OFFSET(svc_regs[1]), /* r14 */
+       },
+
+       /* ABT Registers */
+       {
+               USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2),
+               USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5),
+               USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
+               USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11),
+               USR_REG_OFFSET(12),
+               REG_OFFSET(abt_regs[0]), /* r13 */
+               REG_OFFSET(abt_regs[1]), /* r14 */
+       },
+
+       /* UND Registers */
+       {
+               USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2),
+               USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5),
+               USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
+               USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11),
+               USR_REG_OFFSET(12),
+               REG_OFFSET(und_regs[0]), /* r13 */
+               REG_OFFSET(und_regs[1]), /* r14 */
+       },
+};
+
+/*
+ * Return a pointer to the register number valid in the current mode of
+ * the virtual CPU.
+ */
+u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num)
+{
+       u32 *reg_array = (u32 *)&vcpu->arch.regs;
+       u32 mode = *vcpu_cpsr(vcpu) & MODE_MASK;
+
+       switch (mode) {
+       case USR_MODE...SVC_MODE:
+               mode &= ~MODE32_BIT; /* 0 ... 3 */
+               break;
+
+       case ABT_MODE:
+               mode = 4;
+               break;
+
+       case UND_MODE:
+               mode = 5;
+               break;
+

arch/arm/kvm/emulate.c line 127

Quoted 10 lines
arch/arm/kvm/emulate.c new file +10 -0
+       case SYSTEM_MODE:
+               mode = 0;
+               break;
+
+       default:
+               BUG();
+       }
+
+       return reg_array + vcpu_reg_offsets[mode][reg_num];
+}

]
Have some file-local #defines for those magic mode numbers.

Quoted 12 lines
arch/arm/kvm/emulate.c new file +12 -0
+
+/*
+ * Return the SPSR for the current mode of the virtual CPU.
+ */
+u32 *vcpu_spsr(struct kvm_vcpu *vcpu)
+{
+       u32 mode = *vcpu_cpsr(vcpu) & MODE_MASK;
+       switch (mode) {
+       case SVC_MODE:
+               return &vcpu->arch.regs.svc_regs[2];
+       case ABT_MODE:
+               return &vcpu->arch.regs.abt_regs[2];

arch/arm/kvm/emulate.c line 149

Quoted 10 lines
arch/arm/kvm/emulate.c new file +10 -0
+       case UND_MODE:
+               return &vcpu->arch.regs.und_regs[2];
+       case IRQ_MODE:
+               return &vcpu->arch.regs.irq_regs[2];
+       case FIQ_MODE:
+               return &vcpu->arch.regs.fiq_regs[7];
+       default:
+               BUG();
+       }
+}

Same here: have some #defines for the reg indices. They would also be useful to userspace when manipulating the vcpu state (currently there is just a comment in the header).

Will

#19 lore raw

Re: [PATCH v4 04/14] KVM: ARM: Initial skeleton to compile KVM support

Quoted 132 lines · Will Deacon
Quoted 13 lines · Christoffer Dall

Targets KVM support for Cortex A-15 processors.

Contains all the framework components, make files, header files, some tracing functionality, and basic user space API.

Only supported core is Cortex-A15 for now.

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

[...]

Review The list of registers accessible using this interface is identical to the · line 1803

Quoted 8 lines

4.69 KVM_GET_ONE_REG

Capability: KVM_CAP_ONE_REG

(quoted diff) +1 -0
@@ -1791,6 +1800,7 @@ The list of registers accessible using this interface is identical to the
 list in 4.68.
 
 
+

Whitespace.

Quoted 65 lines
(quoted diff) +40 -0
 4.70 KVM_KVMCLOCK_CTRL
 
 Capability: KVM_CAP_KVMCLOCK_CTRL
@@ -2072,6 +2082,46 @@ KVM_S390_INT_EXTERNAL_CALL (vcpu) - sigp external call; source cpu in parm
 Note that the vcpu ioctl is asynchronous to vcpu execution.
 
 
+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.
+
+
+4.78 KVM_GET_REG_LIST
+
+Capability: basic
+Architectures: arm
+Type: vcpu ioctl
+Parameters: struct kvm_reg_list (in/out)
+Returns: 0 on success; -1 on error
+Errors:
+  E2BIG:     the reg index list is too big to fit in the array specified by
+             the user (the number required will be written into n).
+
+struct kvm_reg_list {
+       __u64 n; /* number of registers in reg[] */
+       __u64 reg[0];
+};
+
+This ioctl returns the guest registers that are supported for the
+KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
+
+
 5. The kvm_run structure
 ------------------------
 
arch/arm/Kconfig +2 -0
index ade7e92..43a997a 100644
@@ -2311,3 +2311,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "arch/arm/kvm/Kconfig"
arch/arm/Makefile +0 -0
index 5f914fc..433becd 100644

arch/arm/Makefile core-$(CONFIG_XEN) += arch/arm/xen/ · line 260

Quoted 4 lines
arch/arm/Makefile +1 -0
@@ -257,6 +257,7 @@ core-$(CONFIG_XEN)          += arch/arm/xen/
 core-y                         += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
 core-y                         += arch/arm/net/
 core-y                         += arch/arm/crypto/
+core-y                                 += arch/arm/kvm/

Predicate this on CONFIG_KVM_ARM_HOST, then add some obj-y to the kvm/Makefile.

Quoted 31 lines
arch/arm/Makefile +0 -0
 core-y                         += $(machdirs) $(platdirs)
 
 drivers-$(CONFIG_OPROFILE)      += arch/arm/oprofile/
arch/arm/include/asm/kvm_host.h new file +22 -0
new file mode 100644
index 0000000..67826b2
@@ -0,0 +1,114 @@
+/*
+ * 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_HOST_H__
+#define __ARM_KVM_HOST_H__
+
+#include <asm/kvm.h>
Quoted 9 lines

arch/arm/include/asm/kvm_host.h line 25

Quoted 3 lines
arch/arm/include/asm/kvm_host.h new file +3 -0
+#include <asm/kvm_asm.h>
+
+#define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS

One thing I noticed when hacking kvmtool is that we don't support the KVM_CAP_{MAX,NR}_VCPUS capabilities. Why is this? Since we have a maximum of 8 due to limitations of the GIC, it might be nice to put that somewhere visible to userspace.

indeed

Quoted 153 lines
Quoted 49 lines
arch/arm/include/asm/kvm_host.h new file +49 -0
+#define KVM_MEMORY_SLOTS 32
+#define KVM_PRIVATE_MEM_SLOTS 4
+#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
+
+#define KVM_VCPU_MAX_FEATURES 0
+
+/* We don't currently support large pages. */
+#define KVM_HPAGE_GFN_SHIFT(x) 0
+#define KVM_NR_PAGE_SIZES      1
+#define KVM_PAGES_PER_HPAGE(x) (1UL<<31)
+
+struct kvm_vcpu;
+u32 *kvm_vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode);
+int kvm_target_cpu(void);
+int kvm_reset_vcpu(struct kvm_vcpu *vcpu);
+void kvm_reset_coprocs(struct kvm_vcpu *vcpu);
+
+struct kvm_arch {
+       /* VTTBR value associated with below pgd and vmid */
+       u64    vttbr;
+
+       /*
+        * Anything that is not used directly from assembly code goes
+        * here.
+        */
+
+       /* The VMID generation used for the virt. memory system */
+       u64    vmid_gen;
+       u32    vmid;
+
+       /* Stage-2 page table */
+       pgd_t *pgd;
+};
+
+#define KVM_NR_MEM_OBJS     40
+
+/*
+ * We don't want allocation failures within the mmu code, so we preallocate
+ * enough memory for a single page fault in a cache.
+ */
+struct kvm_mmu_memory_cache {
+       int nobjs;
+       void *objects[KVM_NR_MEM_OBJS];
+};
+
+struct kvm_vcpu_arch {
+       struct kvm_regs regs;
+
+       int target; /* Processor target */

arch/arm/include/asm/kvm_host.h line 84

Quoted 10 lines
arch/arm/include/asm/kvm_host.h new file +10 -0
+       DECLARE_BITMAP(features, KVM_VCPU_MAX_FEATURES);
+
+       /* System control coprocessor (cp15) */
+       u32 cp15[NR_CP15_REGS];
+
+       /* The CPU type we expose to the VM */
+       u32 midr;
+
+       /* Exception Information */
+       u32 hsr;                /* Hyp Syndrom Register */

pedantic: syndrome

Quoted 37 lines
arch/arm/include/uapi/asm/kvm.h new file +31 -0
new file mode 100644
index 0000000..00d61a6
@@ -0,0 +1,82 @@
+/*
+ * 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_H__
+#define __ARM_KVM_H__
+
+#include <asm/types.h>
+#include <asm/ptrace.h>
+
+#define __KVM_HAVE_GUEST_DEBUG
+
+#define KVM_REG_SIZE(id)                                               \
+       (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT))
+
+struct kvm_regs {
+       struct pt_regs usr_regs;/* R0_usr - R14_usr, PC, CPSR */

arch/arm/include/uapi/asm/kvm.h line 41

Quoted 10 lines
arch/arm/include/uapi/asm/kvm.h new file +10 -0
+       __u32 svc_regs[3];      /* SP_svc, LR_svc, SPSR_svc */
+       __u32 abt_regs[3];      /* SP_abt, LR_abt, SPSR_abt */
+       __u32 und_regs[3];      /* SP_und, LR_und, SPSR_und */
+       __u32 irq_regs[3];      /* SP_irq, LR_irq, SPSR_irq */
+       __u32 fiq_regs[8];      /* R8_fiq - R14_fiq, SPSR_fiq */
+};
+
+/* Supported Processor Types */
+#define KVM_ARM_TARGET_CORTEX_A15      0
+#define KVM_ARM_NUM_TARGETS            1

In future, it would be nice to have a way of probing the supported targets, rather than trying a bunch of them and seeing which ioctl finally succeeds.

Quoted 39 lines
arch/arm/kvm/Kconfig new file +33 -0
new file mode 100644
index 0000000..c7eb178
@@ -0,0 +1,55 @@
+#
+# KVM configuration
+#
+
+source "virt/kvm/Kconfig"
+
+menuconfig VIRTUALIZATION
+       bool "Virtualization"
+       ---help---
+         Say Y here to get to see options for using your Linux host to run
+         other operating systems inside virtual machines (guests).
+         This option alone does not add any kernel code.
+
+         If you say N, all options in this submenu will be skipped and
+         disabled.
+
+if VIRTUALIZATION
+
+config KVM
+       bool "Kernel-based Virtual Machine (KVM) support"
+       select PREEMPT_NOTIFIERS
+       select ANON_INODES
+       select KVM_MMIO
+       depends on ARM_VIRT_EXT && ARM_LPAE
+       ---help---
+         Support hosting virtualized guest machines. You will also
+         need to select one or more of the processor modules below.
+
+         This module provides access to the hardware capabilities through
+         a character device node named /dev/kvm.
+
+         If unsure, say N.
+
Quoted 9 lines

arch/arm/kvm/Kconfig line 38

Quoted 5 lines
arch/arm/kvm/Kconfig new file +5 -0
+config KVM_ARM_HOST
+       bool "KVM host support for ARM cpus."
+       depends on KVM
+       depends on MMU
+       depends on CPU_V7 && ARM_VIRT_EXT

ARM_VIRT_EXT already implies CPU_V7 but, as I suggested earlier, it should probably imply ARM_LPAE which would make much of this simpler.

true, and this depends on KVM and KVM already depends on ARM_VIRT_EXT && ARM_LPAE, so I simply removed this line.

Quoted 11 lines
Quoted 11 lines
arch/arm/kvm/Kconfig new file +11 -0
+       ---help---
+         Provides host support for ARM processors.
+
+config KVM_ARM_MAX_VCPUS
+       int "Number maximum supported virtual CPUs per VM"
+       depends on KVM_ARM_HOST
+       default 4
+       help
+         Static number of max supported virtual CPUs per VM.
+
+         If you choose a high number, the vcpu structures will be quite
Quoted 9 lines

arch/arm/kvm/Kconfig line 55

Quoted 6 lines
arch/arm/kvm/Kconfig new file +6 -0
+         large, so only choose a reasonable number that you expect to
+         actually use.
+
+source drivers/virtio/Kconfig
+
+endif # VIRTUALIZATION

I guess the XEN config options should also be moved under here?

no objections

Quoted 211 lines
Quoted 5 lines
arch/arm/kvm/arm.c new file +0 -0
new file mode 100644
index 0000000..ead5a4e

[...]

Quoted 35 lines
arch/arm/kvm/arm.c new file +35 -0
+long kvm_arch_vcpu_ioctl(struct file *filp,
+                        unsigned int ioctl, unsigned long arg)
+{
+       struct kvm_vcpu *vcpu = filp->private_data;
+       void __user *argp = (void __user *)arg;
+
+       switch (ioctl) {
+       case KVM_ARM_VCPU_INIT: {
+               struct kvm_vcpu_init init;
+
+               if (copy_from_user(&init, argp, sizeof init))
+                       return -EFAULT;
+
+               return kvm_vcpu_set_target(vcpu, &init);
+
+       }
+       case KVM_SET_ONE_REG:
+       case KVM_GET_ONE_REG: {
+               struct kvm_one_reg reg;
+               if (copy_from_user(&reg, argp, sizeof(reg)))
+                       return -EFAULT;
+               if (ioctl == KVM_SET_ONE_REG)
+                       return kvm_arm_set_reg(vcpu, &reg);
+               else
+                       return kvm_arm_get_reg(vcpu, &reg);
+       }
+       case KVM_GET_REG_LIST: {
+               struct kvm_reg_list __user *user_list = argp;
+               struct kvm_reg_list reg_list;
+               unsigned n;
+
+               if (copy_from_user(&reg_list, user_list, sizeof reg_list))
+                       return -EFAULT;
+               n = reg_list.n;
+               reg_list.n = kvm_arm_num_regs(vcpu);

arch/arm/kvm/arm.c

Quoted 10 lines
arch/arm/kvm/arm.c new file +10 -0
+               if (copy_to_user(user_list, &reg_list, sizeof reg_list))
+                       return -EFAULT;
+               if (n < reg_list.n)
+                       return -E2BIG;
+               return kvm_arm_copy_reg_indices(vcpu, user_list->reg);
+       }
+       default:
+               return -EINVAL;
+       }
+}

Your use of brackets with sizeof is inconsistent -- just always make it look like a function call.

Quoted 123 lines
arch/arm/kvm/emulate.c new file +117 -0
new file mode 100644
index 0000000..32e1172
@@ -0,0 +1,149 @@
+/*
+ * 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/kvm_emulate.h>
+
+#define VCPU_NR_MODES 6
+#define REG_OFFSET(_reg) \
+       (offsetof(struct kvm_regs, _reg) / sizeof(u32))
+
+#define USR_REG_OFFSET(_num) REG_OFFSET(usr_regs.uregs[_num])
+
+static const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][15] = {
+       /* USR/SYS Registers */
+       {
+               USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2),
+               USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5),
+               USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
+               USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11),
+               USR_REG_OFFSET(12), USR_REG_OFFSET(13), USR_REG_OFFSET(14),
+       },
+
+       /* FIQ Registers */
+       {
+               USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2),
+               USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5),
+               USR_REG_OFFSET(6), USR_REG_OFFSET(7),
+               REG_OFFSET(fiq_regs[0]), /* r8 */
+               REG_OFFSET(fiq_regs[1]), /* r9 */
+               REG_OFFSET(fiq_regs[2]), /* r10 */
+               REG_OFFSET(fiq_regs[3]), /* r11 */
+               REG_OFFSET(fiq_regs[4]), /* r12 */
+               REG_OFFSET(fiq_regs[5]), /* r13 */
+               REG_OFFSET(fiq_regs[6]), /* r14 */
+       },
+
+       /* IRQ Registers */
+       {
+               USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2),
+               USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5),
+               USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
+               USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11),
+               USR_REG_OFFSET(12),
+               REG_OFFSET(irq_regs[0]), /* r13 */
+               REG_OFFSET(irq_regs[1]), /* r14 */
+       },
+
+       /* SVC Registers */
+       {
+               USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2),
+               USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5),
+               USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
+               USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11),
+               USR_REG_OFFSET(12),
+               REG_OFFSET(svc_regs[0]), /* r13 */
+               REG_OFFSET(svc_regs[1]), /* r14 */
+       },
+
+       /* ABT Registers */
+       {
+               USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2),
+               USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5),
+               USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
+               USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11),
+               USR_REG_OFFSET(12),
+               REG_OFFSET(abt_regs[0]), /* r13 */
+               REG_OFFSET(abt_regs[1]), /* r14 */
+       },
+
+       /* UND Registers */
+       {
+               USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2),
+               USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5),
+               USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
+               USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11),
+               USR_REG_OFFSET(12),
+               REG_OFFSET(und_regs[0]), /* r13 */
+               REG_OFFSET(und_regs[1]), /* r14 */
+       },
+};
+
+/*
+ * Return a pointer to the register number valid in the current mode of
+ * the virtual CPU.
+ */
+u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num)
+{
+       u32 *reg_array = (u32 *)&vcpu->arch.regs;
+       u32 mode = *vcpu_cpsr(vcpu) & MODE_MASK;
+
+       switch (mode) {
+       case USR_MODE...SVC_MODE:
+               mode &= ~MODE32_BIT; /* 0 ... 3 */
+               break;
+
+       case ABT_MODE:
+               mode = 4;
+               break;
+
+       case UND_MODE:
+               mode = 5;
+               break;
+

arch/arm/kvm/emulate.c line 127

Quoted 10 lines
arch/arm/kvm/emulate.c new file +10 -0
+       case SYSTEM_MODE:
+               mode = 0;
+               break;
+
+       default:
+               BUG();
+       }
+
+       return reg_array + vcpu_reg_offsets[mode][reg_num];
+}

]
Have some file-local #defines for those magic mode numbers.

Quoted 18 lines
arch/arm/kvm/emulate.c new file +18 -0
+
+/*
+ * Return the SPSR for the current mode of the virtual CPU.
+ */
+u32 *vcpu_spsr(struct kvm_vcpu *vcpu)
+{
+       u32 mode = *vcpu_cpsr(vcpu) & MODE_MASK;
+       switch (mode) {
+       case SVC_MODE:
+               return &vcpu->arch.regs.svc_regs[2];
+       case ABT_MODE:
+               return &vcpu->arch.regs.abt_regs[2];
+       case UND_MODE:
+               return &vcpu->arch.regs.und_regs[2];
+       case IRQ_MODE:
+               return &vcpu->arch.regs.irq_regs[2];
+       case FIQ_MODE:
+               return &vcpu->arch.regs.fiq_regs[7];
Quoted 9 lines

arch/arm/kvm/emulate.c line 149

Quoted 4 lines
arch/arm/kvm/emulate.c new file +4 -0
+       default:
+               BUG();
+       }
+}

Same here: have some #defines for the reg indices. They would also be useful to userspace when manipulating the vcpu state (currently there is just a comment in the header).

Thanks:

Quoted 1 line

From cee1c41ab2022eab5df46ae43a95e32ad01020e7 Mon Sep 17 00:00:00 2001

From: Christoffer Dall <[email protected]>
Date: Thu, 29 Nov 2012 17:33:04 -0500
Subject: [PATCH] KVM: ARM: Fixup KVM/ARM skeleton patch nits

This patch addresses comments from Will Deacon:
- Make the kvm Makefile inclusion conditional
- Create register index defines for struct kvm_regs banked regs
- Support KVM_CAP_MAX_VCPUS and KVM_CAP_NR_VCPUS
- Use defines for vcpu reg modes offsets

---
 Documentation/virtual/kvm/api.txt |    1 -
 arch/arm/Makefile                 |    2 +-
 arch/arm/include/asm/kvm_host.h   |    2 +-
 arch/arm/include/uapi/asm/kvm.h   |   24 ++++++++++++++++++++++++
 arch/arm/kvm/Kconfig              |    1 -
 arch/arm/kvm/Makefile             |    6 +++---
 arch/arm/kvm/arm.c                |   12 +++++++++---
 arch/arm/kvm/emulate.c            |   36 +++++++++++++++++++++---------------
 8 files changed, 59 insertions(+), 25 deletions(-)
(quoted diff) +0 -0

b/Documentation/virtual/kvm/api.txt
index 35f6275..58c4345 100644

Documentation/virtual/kvm/api.txt +0 -0
@@ -1830,7 +1830,6 @@ The list of registers accessible using this

interface is identical to the
list in 4.68.

-
4.70 KVM_KVMCLOCK_CTRL

Capability: KVM_CAP_KVMCLOCK_CTRL

arch/arm/Makefile +1 -1
index 433becd..c87e089 100644
@@ -252,12 +252,12 @@ core-$(CONFIG_FPE_NWFPE)	+= arch/arm/nwfpe/
 core-$(CONFIG_FPE_FASTFPE)	+= $(FASTFPE_OBJ)
 core-$(CONFIG_VFP)		+= arch/arm/vfp/
 core-$(CONFIG_XEN)		+= arch/arm/xen/
+core-$(CONFIG_KVM_ARM_HOST) 	+= arch/arm/kvm/
 
 # 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/crypto/
-core-y 				+= arch/arm/kvm/
 core-y				+= $(machdirs) $(platdirs)
 
 drivers-$(CONFIG_OPROFILE)      += arch/arm/oprofile/
arch/arm/include/asm/kvm_host.h +1 -1
index 0a1a52b..ad1390f 100644
@@ -93,7 +93,7 @@ struct kvm_vcpu_arch {
 	u32 midr;
 
 	/* Exception Information */
-	u32 hsr;		/* Hyp Syndrom Register */
+	u32 hsr;		/* Hyp Syndrome Register */
 	u32 hxfar;		/* Hyp Data/Inst Fault Address Register */
 	u32 hpfar;		/* Hyp IPA Fault Address Register */
 
arch/arm/include/uapi/asm/kvm.h +24 -0
index b1c7871..09911a7 100644
@@ -28,6 +28,30 @@
 #define KVM_REG_SIZE(id)						\
 	(1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT))
 
+/* Valid for svc_regs, abt_regs, und_regs, irq_regs in struct kvm_regs */
+#define KVM_ARM_SVC_sp		svc_regs[0]
+#define KVM_ARM_SVC_lr		svc_regs[1]
+#define KVM_ARM_SVC_spsr	svc_regs[2]
+#define KVM_ARM_ABT_sp		abt_regs[0]
+#define KVM_ARM_ABT_lr		abt_regs[1]
+#define KVM_ARM_ABT_spsr	abt_regs[2]
+#define KVM_ARM_UND_sp		und_regs[0]
+#define KVM_ARM_UND_lr		und_regs[1]
+#define KVM_ARM_UND_spsr	und_regs[2]
+#define KVM_ARM_IRQ_sp		irq_regs[0]
+#define KVM_ARM_IRQ_lr		irq_regs[1]
+#define KVM_ARM_IRQ_spsr	irq_regs[2]
+
+/* Valid only for fiq_regs in struct kvm_regs */
+#define KVM_ARM_FIQ_r8		fiq_regs[0]
+#define KVM_ARM_FIQ_r9		fiq_regs[1]
+#define KVM_ARM_FIQ_r10		fiq_regs[2]
+#define KVM_ARM_FIQ_fp		fiq_regs[3]
+#define KVM_ARM_FIQ_ip		fiq_regs[4]
+#define KVM_ARM_FIQ_sp		fiq_regs[5]
+#define KVM_ARM_FIQ_lr		fiq_regs[6]
+#define KVM_ARM_FIQ_spsr	fiq_regs[7]
+
 struct kvm_regs {
 	struct pt_regs usr_regs;/* R0_usr - R14_usr, PC, CPSR */
 	__u32 svc_regs[3];	/* SP_svc, LR_svc, SPSR_svc */
arch/arm/kvm/Kconfig +0 -1
index eaecb9f..60d3d2a 100644
@@ -35,7 +35,6 @@ config KVM_ARM_HOST
 	bool "KVM host support for ARM cpus."
 	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/Makefile +3 -3
index 7de231d..49919c4 100644
@@ -16,8 +16,8 @@ AFLAGS_interrupts.o := -Wa,-march=armv7-a$(plus_virt)
 
 kvm-arm-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
 
-obj-$(CONFIG_KVM_ARM_HOST) += kvm-arm.o init.o interrupts.o
-obj-$(CONFIG_KVM_ARM_HOST) += arm.o guest.o mmu.o emulate.o reset.o
-obj-$(CONFIG_KVM_ARM_HOST) += coproc.o coproc_a15.o mmio.o decode.o
+obj-y += kvm-arm.o init.o interrupts.o
+obj-y += arm.o guest.o mmu.o emulate.o reset.o
+obj-y += coproc.o coproc_a15.o mmio.o decode.o
 obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o
 obj-$(CONFIG_KVM_ARM_TIMER) += timer.o
arch/arm/kvm/arm.c +9 -3
index 508cf07..9827a3d 100644
@@ -210,6 +210,12 @@ int kvm_dev_ioctl_check_extension(long ext)
 	case KVM_CAP_SET_DEVICE_ADDR:
 		r = 1;
 		break;
+	case KVM_CAP_NR_VCPUS:
+		r = num_online_cpus();
+		break;
+	case KVM_CAP_MAX_VCPUS:
+		r = KVM_MAX_VCPUS;
+		break;
 	default:
 		r = 0;
 		break;
@@ -834,7 +840,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 	case KVM_ARM_VCPU_INIT: {
 		struct kvm_vcpu_init init;
 
-		if (copy_from_user(&init, argp, sizeof init))
+		if (copy_from_user(&init, argp, sizeof(init)))
 			return -EFAULT;
 
 		return kvm_vcpu_set_target(vcpu, &init);
@@ -855,11 +861,11 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 		struct kvm_reg_list reg_list;
 		unsigned n;
 
-		if (copy_from_user(&reg_list, user_list, sizeof reg_list))
+		if (copy_from_user(&reg_list, user_list, sizeof(reg_list)))
 			return -EFAULT;
 		n = reg_list.n;
 		reg_list.n = kvm_arm_num_regs(vcpu);
-		if (copy_to_user(user_list, &reg_list, sizeof reg_list))
+		if (copy_to_user(user_list, &reg_list, sizeof(reg_list)))
 			return -EFAULT;
 		if (n < reg_list.n)
 			return -E2BIG;
arch/arm/kvm/emulate.c +8 -2
index 4e8a127..60e7ec0 100644
@@ -25,7 +25,13 @@
 
 #include "trace.h"
 
-#define VCPU_NR_MODES 6
+#define VCPU_NR_MODES		6
+#define VCPU_REG_OFFSET_USR	0
+#define VCPU_REG_OFFSET_FIQ	1
+#define VCPU_REG_OFFSET_IRQ	2
+#define VCPU_REG_OFFSET_SVC	3
+#define VCPU_REG_OFFSET_ABT	4
+#define VCPU_REG_OFFSET_UND	5
 #define REG_OFFSET(_reg) \
 	(offsetof(struct kvm_regs, _reg) / sizeof(u32))
 
@@ -33,7 +39,7 @@
 
 static const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][15] = {
 	/* USR/SYS Registers */
-	{
+	[VCPU_REG_OFFSET_USR] = {
 		USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2),
 		USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5),
 		USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
@@ -42,7 +48,7 @@ static const unsigned long

vcpu_reg_offsets[VCPU_NR_MODES][15] = {
},

/* FIQ Registers */
- {
+ [VCPU_REG_OFFSET_FIQ] = {
USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), USR_REG_OFFSET(6), USR_REG_OFFSET(7),
@@ -56,7 +62,7 @@ static const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][15] = {
},

/* IRQ Registers */
- {
+ [VCPU_REG_OFFSET_IRQ] = {
USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
@@ -67,7 +73,7 @@ static const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][15] = {
},

/* SVC Registers */
- {
+ [VCPU_REG_OFFSET_SVC] = {
USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
@@ -78,7 +84,7 @@ static const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][15] = {
},

/* ABT Registers */
- {
+ [VCPU_REG_OFFSET_ABT] = {
USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
@@ -89,7 +95,7 @@ static const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][15] = {
},

/* UND Registers */
- {
+ [VCPU_REG_OFFSET_UND] = {
USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8),
@@ -115,15 +121,15 @@ u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num)
break;

case ABT_MODE:
- mode = 4;
+ mode = VCPU_REG_OFFSET_ABT;
break;

case UND_MODE:
- mode = 5;
+ mode = VCPU_REG_OFFSET_UND;
break;

case SYSTEM_MODE:
- mode = 0;
+ mode = VCPU_REG_OFFSET_USR;
break;

default:
@@ -141,15 +147,15 @@ u32 *vcpu_spsr(struct kvm_vcpu *vcpu)
u32 mode = *vcpu_cpsr(vcpu) & MODE_MASK; switch (mode) {
case SVC_MODE:
- return &vcpu->arch.regs.svc_regs[2];
+ return &vcpu->arch.regs.KVM_ARM_SVC_spsr;
case ABT_MODE:
- return &vcpu->arch.regs.abt_regs[2];
+ return &vcpu->arch.regs.KVM_ARM_ABT_spsr;
case UND_MODE:
- return &vcpu->arch.regs.und_regs[2];
+ return &vcpu->arch.regs.KVM_ARM_UND_spsr;
case IRQ_MODE:
- return &vcpu->arch.regs.irq_regs[2];
+ return &vcpu->arch.regs.KVM_ARM_IRQ_spsr;
case FIQ_MODE:
- return &vcpu->arch.regs.fiq_regs[7];
+ return &vcpu->arch.regs.KVM_ARM_FIQ_spsr;
default:
BUG();
}

Signature · 4 lines
1.7.9.5


-Christoffer
#20 lore raw

PATCH v4 5/14 KVM: ARM: Hypervisor inititalization

In reply to Christoffer Dall

Sets up KVM code to handle all exceptions taken to Hyp mode.

When the kernel is booted in Hyp mode, calling "hvc #0xff" with r0 pointing to the new vectors, the HVBAR is changed to the the vector pointers. This allows subsystems (like KVM here) to execute code in Hyp-mode with the MMU disabled.

We initialize other Hyp-mode registers and enables the MMU for Hyp-mode from the id-mapped hyp initialization code. 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.

If the KVM module is unloaded we call "hvc #0xff" once more to disable the MMU in Hyp mode again and install a vector handler to change the HVBAR for a subsequent reload of KVM or another hypervisor.

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 comprises:
- create_hyp_mappings(from, to);
- create_hyp_io_mappings(from, to, phys_addr);
- free_hyp_pmds();

---
 arch/arm/include/asm/kvm_arm.h              |  107 ++++++++++++++
 arch/arm/include/asm/kvm_asm.h              |   20 +++
 arch/arm/include/asm/kvm_mmu.h              |   39 +++++
 arch/arm/include/asm/pgtable-3level-hwdef.h |    4 +
 arch/arm/kvm/arm.c                          |  172 ++++++++++++++++++++++
 arch/arm/kvm/init.S                         |  107 ++++++++++++++
 arch/arm/kvm/interrupts.S                   |   48 ++++++
 arch/arm/kvm/mmu.c                          |  210 +++++++++++++++++++++++++++
 mm/memory.c                                 |    2 
 9 files changed, 709 insertions(+)
 create mode 100644 arch/arm/include/asm/kvm_mmu.h
arch/arm/include/asm/kvm_arm.h +107 -0
index c196a22..f6e8f6f 100644
@@ -21,4 +21,111 @@
 
 #include <asm/types.h>
 
+/* Hyp Configuration Register (HCR) bits */
+#define HCR_TGE		(1 << 27)
+#define HCR_TVM		(1 << 26)
+#define HCR_TTLB	(1 << 25)
+#define HCR_TPU		(1 << 24)
+#define HCR_TPC		(1 << 23)
+#define HCR_TSW		(1 << 22)
+#define HCR_TAC		(1 << 21)
+#define HCR_TIDCP	(1 << 20)
+#define HCR_TSC		(1 << 19)
+#define HCR_TID3	(1 << 18)
+#define HCR_TID2	(1 << 17)
+#define HCR_TID1	(1 << 16)
+#define HCR_TID0	(1 << 15)
+#define HCR_TWE		(1 << 14)
+#define HCR_TWI		(1 << 13)
+#define HCR_DC		(1 << 12)
+#define HCR_BSU		(3 << 10)
+#define HCR_BSU_IS	(1 << 10)
+#define HCR_FB		(1 << 9)
+#define HCR_VA		(1 << 8)
+#define HCR_VI		(1 << 7)
+#define HCR_VF		(1 << 6)
+#define HCR_AMO		(1 << 5)
+#define HCR_IMO		(1 << 4)
+#define HCR_FMO		(1 << 3)
+#define HCR_PTW		(1 << 2)
+#define HCR_SWIO	(1 << 1)
+#define HCR_VM		1
+
+/*
+ * The bits we set in HCR:
+ * TAC:		Trap ACTLR
+ * TSC:		Trap SMC
+ * 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 +20 -0
index 48ddf99..954bf7c 100644
@@ -54,5 +54,25 @@
 #define ARM_EXCEPTION_DATA_ABORT  4
 #define ARM_EXCEPTION_IRQ	  5
 #define ARM_EXCEPTION_FIQ	  6
+#define ARM_EXCEPTION_HVC	  7
+
+#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 +39 -0
new file mode 100644
index 0000000..741ab8f
@@ -0,0 +1,39 @@
+/*
+ * 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);
+
+unsigned long kvm_mmu_get_httbr(void);
+int kvm_mmu_init(void);
+void kvm_mmu_exit(void);
+#endif /* __ARM_KVM_MMU_H__ */
arch/arm/include/asm/pgtable-3level-hwdef.h +4 -0
index a2d404e..18f5cef 100644
@@ -32,6 +32,9 @@
 #define PMD_TYPE_SECT		(_AT(pmdval_t, 1) << 0)
 #define PMD_BIT4		(_AT(pmdval_t, 0))
 #define PMD_DOMAIN(x)		(_AT(pmdval_t, 0))
+#define PMD_APTABLE_SHIFT	(61)
+#define PMD_APTABLE		(_AT(pgdval_t, 3) << PGD_APTABLE_SHIFT)
+#define PMD_PXNTABLE		(_AT(pgdval_t, 1) << 59)
 
 /*
  *   - section
@@ -41,6 +44,7 @@
 #define PMD_SECT_S		(_AT(pmdval_t, 3) << 8)
 #define PMD_SECT_AF		(_AT(pmdval_t, 1) << 10)
 #define PMD_SECT_nG		(_AT(pmdval_t, 1) << 11)
+#define PMD_SECT_PXN		(_AT(pmdval_t, 1) << 53)
 #define PMD_SECT_XN		(_AT(pmdval_t, 1) << 54)
 #define PMD_SECT_AP_WRITE	(_AT(pmdval_t, 0))
 #define PMD_SECT_AP_READ	(_AT(pmdval_t, 0))
arch/arm/kvm/arm.c +172 -0

192 lines not on this page · raw message

arch/arm/kvm/init.S +107 -0

112 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 +210 -0

213 lines not on this page · raw message

mm/memory.c +2 -0
index fb135ba..5ae0164 100644
@@ -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)
 {
#21 lore raw

Re: [PATCH v4 05/14] KVM: ARM: Hypervisor inititalization

Typo in subject, use one of initiali[sz]ation instead.

Quoted 6 lines · Christoffer Dall

Sets up KVM code to handle all exceptions taken to Hyp mode.

When the kernel is booted in Hyp mode, calling "hvc #0xff" with r0 pointing to the new vectors, the HVBAR is changed to the the vector pointers. This allows subsystems (like KVM here) to execute code in Hyp-mode with the MMU disabled.

Quoted 8 lines

We initialize other Hyp-mode registers and enables the MMU for Hyp-mode from the id-mapped hyp initialization code. 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.

If the KVM module is unloaded we call "hvc #0xff" once more to disable the MMU in Hyp mode again and install a vector handler to change the HVBAR for a subsequent reload of KVM or another hypervisor.

0xff might be a bit too simple. I notice Xen use 0xEA1, which is probably less likely to conflict with anything else. We should probably also put these numbers in the same header file so that any conflicts become immediately apparent.

Quoted 13 lines

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 comprises:
- create_hyp_mappings(from, to);
- create_hyp_io_mappings(from, to, phys_addr);
- free_hyp_pmds();

[...]

Quoted 103 lines
arch/arm/include/asm/kvm_arm.h +95 -0
index c196a22..f6e8f6f 100644
@@ -21,4 +21,111 @@
 
 #include <asm/types.h>
 
+/* Hyp Configuration Register (HCR) bits */
+#define HCR_TGE                (1 << 27)
+#define HCR_TVM                (1 << 26)
+#define HCR_TTLB       (1 << 25)
+#define HCR_TPU                (1 << 24)
+#define HCR_TPC                (1 << 23)
+#define HCR_TSW                (1 << 22)
+#define HCR_TAC                (1 << 21)
+#define HCR_TIDCP      (1 << 20)
+#define HCR_TSC                (1 << 19)
+#define HCR_TID3       (1 << 18)
+#define HCR_TID2       (1 << 17)
+#define HCR_TID1       (1 << 16)
+#define HCR_TID0       (1 << 15)
+#define HCR_TWE                (1 << 14)
+#define HCR_TWI                (1 << 13)
+#define HCR_DC         (1 << 12)
+#define HCR_BSU                (3 << 10)
+#define HCR_BSU_IS     (1 << 10)
+#define HCR_FB         (1 << 9)
+#define HCR_VA         (1 << 8)
+#define HCR_VI         (1 << 7)
+#define HCR_VF         (1 << 6)
+#define HCR_AMO                (1 << 5)
+#define HCR_IMO                (1 << 4)
+#define HCR_FMO                (1 << 3)
+#define HCR_PTW                (1 << 2)
+#define HCR_SWIO       (1 << 1)
+#define HCR_VM         1
+
+/*
+ * The bits we set in HCR:
+ * TAC:                Trap ACTLR
+ * TSC:                Trap SMC
+ * 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)

arch/arm/include/asm/kvm_arm.h line 128

Quoted 10 lines
arch/arm/include/asm/kvm_arm.h +10 -0
+#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

I'm fine with putting this here for now, but we need to keep an eye out for any future hyp-related code which can re-use these architectural definitions.

Quoted 26 lines
arch/arm/include/asm/kvm_mmu.h new file +20 -0
new file mode 100644
index 0000000..741ab8f
@@ -0,0 +1,39 @@
+/*
+ * 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__

arch/arm/include/asm/kvm_mmu.h line 30

Quoted 10 lines
arch/arm/include/asm/kvm_mmu.h new file +10 -0
+
+/*
+ * 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))

Can you follow the _S2_ style you used in the pgtable headers please?

Quoted 49 lines
arch/arm/kvm/arm.c +29 -0
index ead5a4e..8e1ea2b 100644
@@ -34,11 +34,21 @@
 #include <asm/ptrace.h>
 #include <asm/mman.h>
 #include <asm/cputype.h>
+#include <asm/tlbflush.h>
+#include <asm/virt.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
+#include <asm/kvm_mmu.h>
 
 #ifdef REQUIRES_VIRT
 __asm__(".arch_extension       virt");
 #endif
 
+static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
+static struct vfp_hard_struct __percpu *kvm_host_vfp_state;
+static unsigned long hyp_default_vectors;
+
+
 int kvm_arch_hardware_enable(void *garbage)
 {
        return 0;
@@ -324,9 +334,171 @@ long kvm_arch_vm_ioctl(struct file *filp,
        return -EINVAL;
 }
 
+static void cpu_init_hyp_mode(void *vector)
+{
+       unsigned long pgd_ptr;
+       unsigned long hyp_stack_ptr;
+       unsigned long stack_page;
+       unsigned long vector_ptr;
+
+       /* Switch from the HYP stub to our own HYP init vector */
+       __hyp_set_vectors((unsigned long)vector);
+
+       pgd_ptr = kvm_mmu_get_httbr();
+       stack_page = __get_cpu_var(kvm_arm_hyp_stack_page);
+       hyp_stack_ptr = stack_page + PAGE_SIZE;
+       vector_ptr = (unsigned long)__kvm_hyp_vector;
+
+       /*
+        * Call initialization code, and switch to the full blown
+        * HYP code. The init code corrupts r12, so set the clobber
+        * list accordingly.

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

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+        */
+       asm volatile (
+               "mov    r0, %[pgd_ptr]\n\t"
+               "mov    r1, %[hyp_stack_ptr]\n\t"
+               "mov    r2, %[vector_ptr]\n\t"
+               "hvc    #0\n\t" : :
+               [pgd_ptr] "r" (pgd_ptr),
+               [hyp_stack_ptr] "r" (hyp_stack_ptr),
+               [vector_ptr] "r" (vector_ptr) :
+               "r0", "r1", "r2", "r12");

This looks similar to the Xen PCS (at least, register-compatible). Could we have a separate function for issuing a hvc call which is shared between the two?

Quoted 41 lines
arch/arm/kvm/arm.c +41 -0
+}
+
+/**
+ * Inits Hyp-mode on all online CPUs
+ */
+static int init_hyp_mode(void)
+{
+       phys_addr_t init_phys_addr;
+       int cpu;
+       int err = 0;
+
+       /*
+        * Allocate Hyp PGD and setup Hyp identity mapping
+        */
+       err = kvm_mmu_init();
+       if (err)
+               return err;
+
+       /*
+        * It is probably enough to obtain the default on one
+        * CPU. It's unlikely to be different on the others.
+        */
+       hyp_default_vectors = __hyp_get_vectors();
+
+       /*
+        * Allocate stack pages for Hypervisor-mode
+        */
+       for_each_possible_cpu(cpu) {
+               unsigned long stack_page;
+
+               stack_page = __get_free_page(GFP_KERNEL);
+               if (!stack_page) {
+                       err = -ENOMEM;
+                       goto out_free_stack_pages;
+               }
+
+               per_cpu(kvm_arm_hyp_stack_page, cpu) = stack_page;
+       }
+
+       /*
+        * Execute the init code on each CPU.

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

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+        *
+        * Note: The stack is not mapped yet, so don't do anything else than
+        * initializing the hypervisor mode on each CPU using a local stack
+        * space for temporary storage.
+        */
+       init_phys_addr = virt_to_phys(__kvm_hyp_init);
+       for_each_online_cpu(cpu) {
+               smp_call_function_single(cpu, cpu_init_hyp_mode,
+                                        (void *)(long)init_phys_addr, 1);
+       }

Hmm, this will probably go wrong for platforms like keystone, where everything is above 4GB in physical memory. Actually, I'm not sure on the status of the patches so you could check with Cyril [CC'd].

Quoted 101 lines
arch/arm/kvm/init.S +91 -0
index 1dc8926..3e0690b 100644
@@ -15,5 +15,112 @@
  * 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)
+ *       r2 = pointer to hyp vectors
+ */
+
+       .text
+       .pushsection    .hyp.idmap.text,"ax"
+       .align 12
+__kvm_hyp_init:
+       .globl __kvm_hyp_init
+
+       @ Hyp-mode exception vector
+       W(b)    .
+       W(b)    .
+       W(b)    .
+       W(b)    .
+       W(b)    .
+       W(b)    __do_hyp_init
+       W(b)    .
+       W(b)    .
+
+__do_hyp_init:
+       @ Set the sp to end of this page and push data for later use
+ARM(   add     r12, pc, #(__kvm_init_sp - .)   )
+ARM(   sub     r12, r12, #8                    )
+THUMB( adr     r12, __kvm_init_sp              )
+       mov     sp, r12
+       push    {r1, r2}
+
+       @ 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
+
+       @ Set the HTCR and VTCR to the same shareability and cacheability
+       @ settings as the non-secure TTBCR and with T0SZ == 0.
+       mrc     p15, 4, r0, c2, c0, 2   @ HTCR
+       ldr     r12, =HTCR_MASK
+       bic     r0, r0, r12
+       mrc     p15, 0, r1, c2, c0, 2   @ TTBCR
+       and     r1, r1, #(HTCR_MASK & ~TTBCR_T0SZ)
+       orr     r0, r0, r1
+       mcr     p15, 4, r0, c2, c0, 2   @ HTCR
+
+       mrc     p15, 4, r1, c2, c1, 2   @ VTCR
+       bic     r1, r1, #(VTCR_HTCR_SH | VTCR_SL0)
+       bic     r0, r0, #(~VTCR_HTCR_SH)
+       orr     r1, r0, r1
+       orr     r1, r1, #(VTCR_SL_L1 | VTCR_GUEST_T0SZ)
+       mcr     p15, 4, r1, c2, c1, 2   @ VTCR
+
+       @ Use the same memory attributes for hyp. accesses as the kernel
+       @ (copy MAIRx ro HMAIRx).
+       mrc     p15, 0, r0, c10, c2, 0
+       mcr     p15, 4, r0, c10, c2, 0
+       mrc     p15, 0, r0, c10, c2, 1
+       mcr     p15, 4, r0, c10, c2, 1
+
+       @ Set the HSCTLR to:
+       @  - ARM/THUMB exceptions: Kernel config (Thumb-2 kernel)
+       @  - Endianness: Kernel config
+       @  - Fast Interrupt Features: Kernel config
+       @  - Write permission implies XN: disabled
+       @  - Instruction cache: enabled
+       @  - Data/Unified cache: enabled
+       @  - Memory alignment checks: enabled
+       @  - MMU: enabled (this code must be run from an identity mapping)
+       mrc     p15, 4, r0, c1, c0, 0   @ HSCR
+       ldr     r12, =HSCTLR_MASK
+       bic     r0, r0, r12
+       mrc     p15, 0, r1, c1, c0, 0   @ SCTLR
+       ldr     r12, =(HSCTLR_EE | HSCTLR_FI)
+       and     r1, r1, r12
+ ARM(  ldr     r12, =(HSCTLR_M | HSCTLR_A | HSCTLR_I)                  )
+ THUMB(        ldr     r12, =(HSCTLR_M | HSCTLR_A | HSCTLR_I | HSCTLR_TE)      )
+       orr     r1, r1, r12
+       orr     r0, r0, r1
+       isb
+       mcr     p15, 4, r0, c1, c0, 0   @ HSCR
+       isb
+
+       @ Set stack pointer and return to the kernel
+       pop     {r1, r2}

arch/arm/kvm/init.S line 120

Quoted 10 lines
arch/arm/kvm/init.S +10 -0
+       mov     sp, r1
+
+       @ Set HVBAR to point to the HYP vectors
+       mcr     p15, 4, r2, c12, c0, 0  @ HVBAR
+
+       eret
+
+       .ltorg
+
+       .align 12

Do you *have* to page-align the start and end of this small piece of code? What goes wrong if you end up including random other stuff in the hyp pgd?

Quoted 15 lines
arch/arm/kvm/init.S +6 -0
+
+       __kvm_init_sp:
+       .globl __kvm_hyp_init_end
+__kvm_hyp_init_end:
+
+       .popsection
arch/arm/kvm/interrupts.S +1 -0
index 1dc8926..98a67ca 100644
@@ -15,5 +15,53 @@
  * 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/interrupts.S line 28

Quoted 10 lines
arch/arm/kvm/interrupts.S +8 -0
+#include <linux/linkage.h>
+#include <linux/const.h>
+#include <asm/unified.h>
+#include <asm/page.h>
 #include <asm/asm-offsets.h>
 #include <asm/kvm_asm.h>
+#include <asm/kvm_arm.h>
+
+       .text
+       .align  PAGE_SHIFT

I guess you could also use PAGE_SHIFT in the previous file if you do have to keep the align directives. Why do you need the alignment here?

Quoted 62 lines
arch/arm/kvm/mmu.c +54 -0
index 10ed464..17c2bf5 100644
@@ -15,3 +15,213 @@
  * along with this program; if not, write to the Free Software
  * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+
+#include <linux/mman.h>
+#include <linux/kvm_host.h>
+#include <linux/io.h>
+#include <asm/idmap.h>
+#include <asm/pgalloc.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_mmu.h>
+#include <asm/mach/map.h>
+
+static DEFINE_MUTEX(kvm_hyp_pgd_mutex);
+static pgd_t *hyp_pgd;
+
+static void free_ptes(pmd_t *pmd, unsigned long addr)
+{
+       pte_t *pte;
+       unsigned int i;
+
+       for (i = 0; i < PTRS_PER_PMD; i++, addr += PMD_SIZE) {
+               if (!pmd_none(*pmd) && pmd_table(*pmd)) {
+                       pte = pte_offset_kernel(pmd, addr);
+                       pte_free_kernel(NULL, pte);
+               }
+               pmd++;
+       }
+}
+
+/**
+ * free_hyp_pmds - free a Hyp-mode level-2 tables and child level-3 tables
+ *
+ * Assumes this is a page table used strictly in Hyp-mode and therefore contains
+ * only mappings in the kernel memory area, which is above PAGE_OFFSET.
+ */
+void free_hyp_pmds(void)
+{
+       pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmd;
+       unsigned long addr;
+
+       mutex_lock(&kvm_hyp_pgd_mutex);
+       for (addr = PAGE_OFFSET; addr != 0; addr += PGDIR_SIZE) {
+               pgd = hyp_pgd + pgd_index(addr);
+               pud = pud_offset(pgd, addr);
+
+               if (pud_none(*pud))
+                       continue;
+               BUG_ON(pud_bad(*pud));
+
+               pmd = pmd_offset(pud, addr);
+               free_ptes(pmd, addr);
+               pmd_free(NULL, pmd);
+               pud_clear(pud);
+       }

arch/arm/kvm/mmu.c line 81

Quoted 10 lines
arch/arm/kvm/mmu.c +10 -0
+       mutex_unlock(&kvm_hyp_pgd_mutex);
+}
+
+/*
+ * Create a HYP pte mapping.
+ *
+ * If pfn_base is NULL, we map kernel pages into HYP with the virtual
+ * address. Otherwise, this is considered an I/O mapping and we map
+ * the physical region starting at *pfn_base to [start, end[.
+ */

Seems fairly counter-intuitive to me. Why can't you have two separate functions?

Quoted 6 lines
mm/memory.c +0 -0
index fb135ba..5ae0164 100644
@@ -383,12 +383,14 @@ void pgd_clear_bad(pgd_t *pgd)
        pgd_ERROR(*pgd);

mm/memory.c void pgd_clear_bad(pgd_t *pgd) · line 393

Quoted 10 lines
mm/memory.c +2 -0
        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);

Do we really need these? If so, they should be a separate patch which needs sending to linux-mm with akpm on CC.

Will

#22 lore raw

Re: [PATCH v4 05/14] KVM: ARM: Hypervisor inititalization

Quoted 38 lines · Will Deacon

Typo in subject, use one of initiali[sz]ation instead.

Quoted 6 lines · Christoffer Dall

Sets up KVM code to handle all exceptions taken to Hyp mode.

When the kernel is booted in Hyp mode, calling "hvc #0xff" with r0 pointing to the new vectors, the HVBAR is changed to the the vector pointers. This allows subsystems (like KVM here) to execute code in Hyp-mode with the MMU disabled.

Quoted 8 lines

We initialize other Hyp-mode registers and enables the MMU for Hyp-mode from the id-mapped hyp initialization code. 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.

If the KVM module is unloaded we call "hvc #0xff" once more to disable the MMU in Hyp mode again and install a vector handler to change the HVBAR for a subsequent reload of KVM or another hypervisor.

0xff might be a bit too simple. I notice Xen use 0xEA1, which is probably less likely to conflict with anything else. We should probably also put these numbers in the same header file so that any conflicts become immediately apparent.

Quoted 13 lines

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 comprises:
- create_hyp_mappings(from, to);
- create_hyp_io_mappings(from, to, phys_addr);
- free_hyp_pmds();

[...]

Quoted 42 lines
Quoted 42 lines
arch/arm/kvm/arm.c +42 -0
+static int init_hyp_mode(void)
+{
+       phys_addr_t init_phys_addr;
+       int cpu;
+       int err = 0;
+
+       /*
+        * Allocate Hyp PGD and setup Hyp identity mapping
+        */
+       err = kvm_mmu_init();
+       if (err)
+               return err;
+
+       /*
+        * It is probably enough to obtain the default on one
+        * CPU. It's unlikely to be different on the others.
+        */
+       hyp_default_vectors = __hyp_get_vectors();
+
+       /*
+        * Allocate stack pages for Hypervisor-mode
+        */
+       for_each_possible_cpu(cpu) {
+               unsigned long stack_page;
+
+               stack_page = __get_free_page(GFP_KERNEL);
+               if (!stack_page) {
+                       err = -ENOMEM;
+                       goto out_free_stack_pages;
+               }
+
+               per_cpu(kvm_arm_hyp_stack_page, cpu) = stack_page;
+       }
+
+       /*
+        * Execute the init code on each CPU.
+        *
+        * Note: The stack is not mapped yet, so don't do anything else than
+        * initializing the hypervisor mode on each CPU using a local stack
+        * space for temporary storage.
+        */
+       init_phys_addr = virt_to_phys(__kvm_hyp_init);
Quoted 9 lines

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

Quoted 4 lines
arch/arm/kvm/arm.c +4 -0
+       for_each_online_cpu(cpu) {
+               smp_call_function_single(cpu, cpu_init_hyp_mode,
+                                        (void *)(long)init_phys_addr, 1);
+       }

Hmm, this will probably go wrong for platforms like keystone, where everything is above 4GB in physical memory. Actually, I'm not sure on the status of the patches so you could check with Cyril [CC'd].

Thanks for the heads up.

Looks like __kvm_hyp_init is idmap'ed. This should be ok on keystone, but we'll need to replace the virt_to_phys() with a virt_to_idmap() when we get to this.

Thanks
-- Cyril.

#23 lore raw

Re: [PATCH v4 05/14] KVM: ARM: Hypervisor inititalization

In reply to Will Deacon
Quoted 15 lines · Will Deacon

Typo in subject, use one of initiali[sz]ation instead.

Quoted 11 lines · Christoffer Dall

Sets up KVM code to handle all exceptions taken to Hyp mode.

When the kernel is booted in Hyp mode, calling "hvc #0xff" with r0 pointing to the new vectors, the HVBAR is changed to the the vector pointers. This allows subsystems (like KVM here) to execute code in Hyp-mode with the MMU disabled.

We initialize other Hyp-mode registers and enables the MMU for Hyp-mode from the id-mapped hyp initialization code. 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.

Quoted 9 lines
Quoted 3 lines

If the KVM module is unloaded we call "hvc #0xff" once more to disable the MMU in Hyp mode again and install a vector handler to change the HVBAR for a subsequent reload of KVM or another hypervisor.

0xff might be a bit too simple. I notice Xen use 0xEA1, which is probably less likely to conflict with anything else. We should probably also put these numbers in the same header file so that any conflicts become immediately apparent.

actually we don't even do this anymore, the commit text is dated, so this is a non issue.

Quoted 164 lines
Quoted 13 lines

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 comprises:
- create_hyp_mappings(from, to);
- create_hyp_io_mappings(from, to, phys_addr);
- free_hyp_pmds();

[...]

Quoted 103 lines
arch/arm/include/asm/kvm_arm.h +95 -0
index c196a22..f6e8f6f 100644
@@ -21,4 +21,111 @@
 
 #include <asm/types.h>
 
+/* Hyp Configuration Register (HCR) bits */
+#define HCR_TGE                (1 << 27)
+#define HCR_TVM                (1 << 26)
+#define HCR_TTLB       (1 << 25)
+#define HCR_TPU                (1 << 24)
+#define HCR_TPC                (1 << 23)
+#define HCR_TSW                (1 << 22)
+#define HCR_TAC                (1 << 21)
+#define HCR_TIDCP      (1 << 20)
+#define HCR_TSC                (1 << 19)
+#define HCR_TID3       (1 << 18)
+#define HCR_TID2       (1 << 17)
+#define HCR_TID1       (1 << 16)
+#define HCR_TID0       (1 << 15)
+#define HCR_TWE                (1 << 14)
+#define HCR_TWI                (1 << 13)
+#define HCR_DC         (1 << 12)
+#define HCR_BSU                (3 << 10)
+#define HCR_BSU_IS     (1 << 10)
+#define HCR_FB         (1 << 9)
+#define HCR_VA         (1 << 8)
+#define HCR_VI         (1 << 7)
+#define HCR_VF         (1 << 6)
+#define HCR_AMO                (1 << 5)
+#define HCR_IMO                (1 << 4)
+#define HCR_FMO                (1 << 3)
+#define HCR_PTW                (1 << 2)
+#define HCR_SWIO       (1 << 1)
+#define HCR_VM         1
+
+/*
+ * The bits we set in HCR:
+ * TAC:                Trap ACTLR
+ * TSC:                Trap SMC
+ * 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)

arch/arm/include/asm/kvm_arm.h line 128

Quoted 10 lines
arch/arm/include/asm/kvm_arm.h +10 -0
+#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

I'm fine with putting this here for now, but we need to keep an eye out for any future hyp-related code which can re-use these architectural definitions.

Quoted 30 lines
arch/arm/include/asm/kvm_mmu.h new file +24 -0
new file mode 100644
index 0000000..741ab8f
@@ -0,0 +1,39 @@
+/*
+ * 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.
Quoted 9 lines

arch/arm/include/asm/kvm_mmu.h line 30

Quoted 6 lines
arch/arm/include/asm/kvm_mmu.h new file +6 -0
+ *
+ * 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))

Can you follow the _S2_ style you used in the pgtable headers please?

yes

Quoted 55 lines
Quoted 55 lines
arch/arm/kvm/arm.c +35 -0
index ead5a4e..8e1ea2b 100644
@@ -34,11 +34,21 @@
 #include <asm/ptrace.h>
 #include <asm/mman.h>
 #include <asm/cputype.h>
+#include <asm/tlbflush.h>
+#include <asm/virt.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
+#include <asm/kvm_mmu.h>
 
 #ifdef REQUIRES_VIRT
 __asm__(".arch_extension       virt");
 #endif
 
+static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
+static struct vfp_hard_struct __percpu *kvm_host_vfp_state;
+static unsigned long hyp_default_vectors;
+
+
 int kvm_arch_hardware_enable(void *garbage)
 {
        return 0;
@@ -324,9 +334,171 @@ long kvm_arch_vm_ioctl(struct file *filp,
        return -EINVAL;
 }
 
+static void cpu_init_hyp_mode(void *vector)
+{
+       unsigned long pgd_ptr;
+       unsigned long hyp_stack_ptr;
+       unsigned long stack_page;
+       unsigned long vector_ptr;
+
+       /* Switch from the HYP stub to our own HYP init vector */
+       __hyp_set_vectors((unsigned long)vector);
+
+       pgd_ptr = kvm_mmu_get_httbr();
+       stack_page = __get_cpu_var(kvm_arm_hyp_stack_page);
+       hyp_stack_ptr = stack_page + PAGE_SIZE;
+       vector_ptr = (unsigned long)__kvm_hyp_vector;
+
+       /*
+        * Call initialization code, and switch to the full blown
+        * HYP code. The init code corrupts r12, so set the clobber
+        * list accordingly.
+        */
+       asm volatile (
+               "mov    r0, %[pgd_ptr]\n\t"
+               "mov    r1, %[hyp_stack_ptr]\n\t"
+               "mov    r2, %[vector_ptr]\n\t"
+               "hvc    #0\n\t" : :
Quoted 9 lines

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

Quoted 4 lines
arch/arm/kvm/arm.c +4 -0
+               [pgd_ptr] "r" (pgd_ptr),
+               [hyp_stack_ptr] "r" (hyp_stack_ptr),
+               [vector_ptr] "r" (vector_ptr) :
+               "r0", "r1", "r2", "r12");

This looks similar to the Xen PCS (at least, register-compatible). Could we have a separate function for issuing a hvc call which is shared between the two?

I don't think there's any guarantee that these will remain the same, necessarily. Also, since the immediate value of the hvc can't really be set at runtime, it's not really pretty, it would look something like this (let me know):

Quoted 1 line

From cf9a0f1c2ffa04911cf4e98cebe46f1bc6907f6b Mon Sep 17 00:00:00 2001

From: Christoffer Dall <[email protected]>
Date: Fri, 30 Nov 2012 00:42:14 -0500
Subject: [PATCH] KVM: ARM: Add generic hypercall function

Add a generic hypercall wrapper that can be used to call the KVM init code, and could also be leveraged by otherusers such as Xen.

---
 arch/arm/include/asm/hvc.h |   23 ++++++++++++++++
 arch/arm/kernel/Makefile   |    2 +-
 arch/arm/kernel/hvc.S      |   63 ++++++++++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/arm.c         |   18 +++----------
 4 files changed, 90 insertions(+), 16 deletions(-)
 create mode 100644 arch/arm/include/asm/hvc.h
 create mode 100644 arch/arm/kernel/hvc.S
arch/arm/include/asm/hvc.h new file +23 -0
new file mode 100644
index 0000000..55f9d52
@@ -0,0 +1,23 @@
+/*
+ * 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+extern int hvc_call(unsigned num, unsigned long arg0,
+		    unsigned long arg1, unsigned long arg2,
+		    unsigned long arg3, unsigned long arg4);
arch/arm/kernel/Makefile +1 -1
index 5bbec7b..3dab040 100644
@@ -81,6 +81,6 @@ head-y			:= head$(MMUEXT).o
 obj-$(CONFIG_DEBUG_LL)	+= debug.o
 obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o
 
-obj-$(CONFIG_ARM_VIRT_EXT)	+= hyp-stub.o
+obj-$(CONFIG_ARM_VIRT_EXT)	+= hyp-stub.o hvc.o
 
 extra-y := $(head-y) vmlinux.lds
arch/arm/kernel/hvc.S new file +63 -0

63 lines not on this page · raw message

arch/arm/kvm/arm.c +3 -15
index 9827a3d..042692b 100644
@@ -44,10 +44,7 @@
 #include <asm/kvm_emulate.h>
 #include <asm/kvm_coproc.h>
 #include <asm/opcodes.h>
-
-#ifdef REQUIRES_VIRT
-__asm__(".arch_extension	virt");
-#endif
+#include <asm/hvc.h>
 
 static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
 static struct vfp_hard_struct __percpu *kvm_host_vfp_state;
@@ -943,18 +940,9 @@ static void cpu_init_hyp_mode(void *vector)
 
 	/*
 	 * Call initialization code, and switch to the full blown
-	 * HYP code. The init code corrupts r12, so set the clobber
-	 * list accordingly.
+	 * HYP code.
 	 */
-	asm volatile (
-		"mov	r0, %[pgd_ptr]\n\t"
-		"mov	r1, %[hyp_stack_ptr]\n\t"
-		"mov	r2, %[vector_ptr]\n\t"
-		"hvc	#0\n\t" : :
-		[pgd_ptr] "r" (pgd_ptr),
-		[hyp_stack_ptr] "r" (hyp_stack_ptr),
-		[vector_ptr] "r" (vector_ptr) :
-		"r0", "r1", "r2", "r12");
+	hvc_call(0, pgd_ptr, hyp_stack_ptr, vector_ptr, 0, 0);
 }
 
 /**
Signature · 1 line
1.7.9.5
Quoted 47 lines
Quoted 47 lines
arch/arm/kvm/arm.c +47 -0
+}
+
+/**
+ * Inits Hyp-mode on all online CPUs
+ */
+static int init_hyp_mode(void)
+{
+       phys_addr_t init_phys_addr;
+       int cpu;
+       int err = 0;
+
+       /*
+        * Allocate Hyp PGD and setup Hyp identity mapping
+        */
+       err = kvm_mmu_init();
+       if (err)
+               return err;
+
+       /*
+        * It is probably enough to obtain the default on one
+        * CPU. It's unlikely to be different on the others.
+        */
+       hyp_default_vectors = __hyp_get_vectors();
+
+       /*
+        * Allocate stack pages for Hypervisor-mode
+        */
+       for_each_possible_cpu(cpu) {
+               unsigned long stack_page;
+
+               stack_page = __get_free_page(GFP_KERNEL);
+               if (!stack_page) {
+                       err = -ENOMEM;
+                       goto out_free_stack_pages;
+               }
+
+               per_cpu(kvm_arm_hyp_stack_page, cpu) = stack_page;
+       }
+
+       /*
+        * Execute the init code on each CPU.
+        *
+        * Note: The stack is not mapped yet, so don't do anything else than
+        * initializing the hypervisor mode on each CPU using a local stack
+        * space for temporary storage.
+        */
+       init_phys_addr = virt_to_phys(__kvm_hyp_init);
Quoted 9 lines

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

Quoted 4 lines
arch/arm/kvm/arm.c +4 -0
+       for_each_online_cpu(cpu) {
+               smp_call_function_single(cpu, cpu_init_hyp_mode,
+                                        (void *)(long)init_phys_addr, 1);
+       }

Hmm, this will probably go wrong for platforms like keystone, where everything is above 4GB in physical memory. Actually, I'm not sure on the status of the patches so you could check with Cyril [CC'd].

first, this made me realize that the httbr actually accepts a 40-bit value, and the init code becomes a lot nicer as a result of that, so I've attached a patch to address that.

However, I don't understand how we at all support Hyp mode on a platform where all ram is above 4GB, because the HVBAR only takes a 32-bit value and Hyp mode is entered with the MMU disabled (from __hyp_stub_install). On such a platform monitor mode must setup the HTTBR and the HVBAR to even get to Hyp mode, but we clear the MMU enable flag for Hyp mode during Hyp boot, so entering it again, will, eh, be bad? Me confused...

Can we cross this bridge when we get there?

Quoted 107 lines
Quoted 107 lines
arch/arm/kvm/init.S +97 -0
index 1dc8926..3e0690b 100644
@@ -15,5 +15,112 @@
  * 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)
+ *       r2 = pointer to hyp vectors
+ */
+
+       .text
+       .pushsection    .hyp.idmap.text,"ax"
+       .align 12
+__kvm_hyp_init:
+       .globl __kvm_hyp_init
+
+       @ Hyp-mode exception vector
+       W(b)    .
+       W(b)    .
+       W(b)    .
+       W(b)    .
+       W(b)    .
+       W(b)    __do_hyp_init
+       W(b)    .
+       W(b)    .
+
+__do_hyp_init:
+       @ Set the sp to end of this page and push data for later use
+ARM(   add     r12, pc, #(__kvm_init_sp - .)   )
+ARM(   sub     r12, r12, #8                    )
+THUMB( adr     r12, __kvm_init_sp              )
+       mov     sp, r12
+       push    {r1, r2}
+
+       @ 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
+
+       @ Set the HTCR and VTCR to the same shareability and cacheability
+       @ settings as the non-secure TTBCR and with T0SZ == 0.
+       mrc     p15, 4, r0, c2, c0, 2   @ HTCR
+       ldr     r12, =HTCR_MASK
+       bic     r0, r0, r12
+       mrc     p15, 0, r1, c2, c0, 2   @ TTBCR
+       and     r1, r1, #(HTCR_MASK & ~TTBCR_T0SZ)
+       orr     r0, r0, r1
+       mcr     p15, 4, r0, c2, c0, 2   @ HTCR
+
+       mrc     p15, 4, r1, c2, c1, 2   @ VTCR
+       bic     r1, r1, #(VTCR_HTCR_SH | VTCR_SL0)
+       bic     r0, r0, #(~VTCR_HTCR_SH)
+       orr     r1, r0, r1
+       orr     r1, r1, #(VTCR_SL_L1 | VTCR_GUEST_T0SZ)
+       mcr     p15, 4, r1, c2, c1, 2   @ VTCR
+
+       @ Use the same memory attributes for hyp. accesses as the kernel
+       @ (copy MAIRx ro HMAIRx).
+       mrc     p15, 0, r0, c10, c2, 0
+       mcr     p15, 4, r0, c10, c2, 0
+       mrc     p15, 0, r0, c10, c2, 1
+       mcr     p15, 4, r0, c10, c2, 1
+
+       @ Set the HSCTLR to:
+       @  - ARM/THUMB exceptions: Kernel config (Thumb-2 kernel)
+       @  - Endianness: Kernel config
+       @  - Fast Interrupt Features: Kernel config
+       @  - Write permission implies XN: disabled
+       @  - Instruction cache: enabled
+       @  - Data/Unified cache: enabled
+       @  - Memory alignment checks: enabled
+       @  - MMU: enabled (this code must be run from an identity mapping)
+       mrc     p15, 4, r0, c1, c0, 0   @ HSCR
+       ldr     r12, =HSCTLR_MASK
+       bic     r0, r0, r12
+       mrc     p15, 0, r1, c1, c0, 0   @ SCTLR
+       ldr     r12, =(HSCTLR_EE | HSCTLR_FI)
+       and     r1, r1, r12
+ ARM(  ldr     r12, =(HSCTLR_M | HSCTLR_A | HSCTLR_I)                  )
+ THUMB(        ldr     r12, =(HSCTLR_M | HSCTLR_A | HSCTLR_I | HSCTLR_TE)      )
+       orr     r1, r1, r12
+       orr     r0, r0, r1
+       isb
+       mcr     p15, 4, r0, c1, c0, 0   @ HSCR
+       isb
+
+       @ Set stack pointer and return to the kernel
+       pop     {r1, r2}
+       mov     sp, r1
+
+       @ Set HVBAR to point to the HYP vectors
+       mcr     p15, 4, r2, c12, c0, 0  @ HVBAR
+
+       eret
Quoted 9 lines

arch/arm/kvm/init.S line 120

Quoted 4 lines
arch/arm/kvm/init.S +4 -0
+
+       .ltorg
+
+       .align 12

Do you *have* to page-align the start and end of this small piece of code? What goes wrong if you end up including random other stuff in the hyp pgd?

we did this to have stack space at the end of the page, but I got rid of all this, see the patch at the end of this mail.

Quoted 20 lines
Quoted 20 lines
arch/arm/kvm/init.S +6 -0
+
+       __kvm_init_sp:
+       .globl __kvm_hyp_init_end
+__kvm_hyp_init_end:
+
+       .popsection
arch/arm/kvm/interrupts.S +5 -0
index 1dc8926..98a67ca 100644
@@ -15,5 +15,53 @@
  * 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 <linux/const.h>
+#include <asm/unified.h>
+#include <asm/page.h>
 #include <asm/asm-offsets.h>
Quoted 9 lines

arch/arm/kvm/interrupts.S line 28

Quoted 5 lines
arch/arm/kvm/interrupts.S +4 -0
 #include <asm/kvm_asm.h>
+#include <asm/kvm_arm.h>
+
+       .text
+       .align  PAGE_SHIFT

I guess you could also use PAGE_SHIFT in the previous file if you do have to keep the align directives. Why do you need the alignment here?

eh, we don't. Historical reasons. (except trying to keep this all in one page might be good for performance to use only a single TLB entry).

Quoted 67 lines
Quoted 67 lines
arch/arm/kvm/mmu.c +59 -0
index 10ed464..17c2bf5 100644
@@ -15,3 +15,213 @@
  * along with this program; if not, write to the Free Software
  * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+
+#include <linux/mman.h>
+#include <linux/kvm_host.h>
+#include <linux/io.h>
+#include <asm/idmap.h>
+#include <asm/pgalloc.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_mmu.h>
+#include <asm/mach/map.h>
+
+static DEFINE_MUTEX(kvm_hyp_pgd_mutex);
+static pgd_t *hyp_pgd;
+
+static void free_ptes(pmd_t *pmd, unsigned long addr)
+{
+       pte_t *pte;
+       unsigned int i;
+
+       for (i = 0; i < PTRS_PER_PMD; i++, addr += PMD_SIZE) {
+               if (!pmd_none(*pmd) && pmd_table(*pmd)) {
+                       pte = pte_offset_kernel(pmd, addr);
+                       pte_free_kernel(NULL, pte);
+               }
+               pmd++;
+       }
+}
+
+/**
+ * free_hyp_pmds - free a Hyp-mode level-2 tables and child level-3 tables
+ *
+ * Assumes this is a page table used strictly in Hyp-mode and therefore contains
+ * only mappings in the kernel memory area, which is above PAGE_OFFSET.
+ */
+void free_hyp_pmds(void)
+{
+       pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmd;
+       unsigned long addr;
+
+       mutex_lock(&kvm_hyp_pgd_mutex);
+       for (addr = PAGE_OFFSET; addr != 0; addr += PGDIR_SIZE) {
+               pgd = hyp_pgd + pgd_index(addr);
+               pud = pud_offset(pgd, addr);
+
+               if (pud_none(*pud))
+                       continue;
+               BUG_ON(pud_bad(*pud));
+
+               pmd = pmd_offset(pud, addr);
+               free_ptes(pmd, addr);
+               pmd_free(NULL, pmd);
+               pud_clear(pud);
+       }
+       mutex_unlock(&kvm_hyp_pgd_mutex);
+}
+
+/*
+ * Create a HYP pte mapping.
Quoted 9 lines

arch/arm/kvm/mmu.c line 81

Quoted 5 lines
arch/arm/kvm/mmu.c +5 -0
+ *
+ * If pfn_base is NULL, we map kernel pages into HYP with the virtual
+ * address. Otherwise, this is considered an I/O mapping and we map
+ * the physical region starting at *pfn_base to [start, end[.
+ */

Seems fairly counter-intuitive to me. Why can't you have two separate functions?

I really don't want to replicate the whole set of functions, but I split up the pte function and perform the pfn_base check before calling the appropriate new one.

Quoted 10 lines
Quoted 10 lines
mm/memory.c +1 -0
index fb135ba..5ae0164 100644
@@ -383,12 +383,14 @@ void pgd_clear_bad(pgd_t *pgd)
        pgd_ERROR(*pgd);
        pgd_clear(pgd);
 }
+EXPORT_SYMBOL_GPL(pgd_clear_bad);
 
Quoted 10 lines

mm/memory.c void pgd_clear_bad(pgd_t *pgd) · line 393

Quoted 6 lines
mm/memory.c +1 -0
 void pud_clear_bad(pud_t *pud)
 {
        pud_ERROR(*pud);
        pud_clear(pud);
 }
+EXPORT_SYMBOL_GPL(pud_clear_bad);

Do we really need these? If so, they should be a separate patch which needs sending to linux-mm with akpm on CC.

we sure don't, no module any more.

I am attaching the patches addressing your comments:

Quoted 1 line

From 5695da0494b0991f59b011ea00ca8a3b4f23f110 Mon Sep 17 00:00:00 2001

From: Christoffer Dall <[email protected]>
Date: Thu, 29 Nov 2012 21:28:27 -0500
Subject: [PATCH 1/5] KVM: ARM: Fix PGD2 reminiscence

Some kvm-local defines were still named according to the PGD2 naming scheme, so let's move this to S2_PGD...

Applied to: KVM: ARM: Hypervisor inititalization

---
 arch/arm/include/asm/kvm_arm.h |    8 ++++----
 arch/arm/kvm/mmu.c             |   12 ++++++------
 2 files changed, 10 insertions(+), 10 deletions(-)
arch/arm/include/asm/kvm_arm.h +4 -4
index 8d31a08..b4bdd0c 100644
@@ -127,15 +127,15 @@
 
 /*
  * The architecture supports 40-bit IPA as input to the 2nd stage translations
- * and PTRS_PER_PGD2 becomes 1024, because each entry covers 1GB of address
+ * and PTRS_PER_S2_PGD becomes 1024, because each entry covers 1GB of address
  * space.
  */
 #define KVM_PHYS_SHIFT	(40)
 #define KVM_PHYS_SIZE	(1ULL << KVM_PHYS_SHIFT)
 #define KVM_PHYS_MASK	(KVM_PHYS_SIZE - 1ULL)
-#define PTRS_PER_PGD2	(1ULL << (KVM_PHYS_SHIFT - 30))
-#define PGD2_ORDER	get_order(PTRS_PER_PGD2 * sizeof(pgd_t))
-#define PGD2_SIZE	(1 << PGD2_ORDER)
+#define PTRS_PER_S2_PGD	(1ULL << (KVM_PHYS_SHIFT - 30))
+#define S2_PGD_ORDER	get_order(PTRS_PER_S2_PGD * sizeof(pgd_t))
+#define S2_PGD_SIZE	(1 << S2_PGD_ORDER)
 
 /* Virtualization Translation Control Register (VTCR) bits */
 #define VTCR_SH0	(3 << 12)
arch/arm/kvm/mmu.c +0 -0
index 59d422f..aac671d 100644
@@ -260,7 +260,7 @@ int create_hyp_io_mappings(void *from, void *to,

phys_addr_t addr)
* 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
+ * Allocates the 1st level table only of size defined by S2_PGD_ORDER (can
* support either full 40-bit input addresses or limited to 32-bit input
* addresses). Clears the allocated pages.
*
@@ -276,15 +276,15 @@ int kvm_alloc_stage2_pgd(struct kvm *kvm)
return -EINVAL;
}

- pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, PGD2_ORDER);
+ pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, S2_PGD_ORDER);
if (!pgd)
return -ENOMEM;

/* stage-2 pgd must be aligned to its size */
- VM_BUG_ON((unsigned long)pgd & (PGD2_SIZE - 1));
+ VM_BUG_ON((unsigned long)pgd & (S2_PGD_SIZE - 1));

- memset(pgd, 0, PTRS_PER_PGD2 * sizeof(pgd_t));
- clean_dcache_area(pgd, PTRS_PER_PGD2 * sizeof(pgd_t));
+ memset(pgd, 0, PTRS_PER_S2_PGD * sizeof(pgd_t));
+ clean_dcache_area(pgd, PTRS_PER_S2_PGD * sizeof(pgd_t));
kvm->arch.pgd = pgd;

return 0;
@@ -395,7 +395,7 @@ void kvm_free_stage2_pgd(struct kvm *kvm)
return;

unmap_stage2_range(kvm, 0, KVM_PHYS_SIZE);
- free_pages((unsigned long)kvm->arch.pgd, PGD2_ORDER);
+ free_pages((unsigned long)kvm->arch.pgd, S2_PGD_ORDER);
kvm->arch.pgd = NULL;
}

Signature · 1 line
1.7.9.5
Quoted 1 line

From 80c1c64ad5ec6d320afc3523d988b26d5fb05c29 Mon Sep 17 00:00:00 2001

From: Christoffer Dall <[email protected]>
Date: Fri, 30 Nov 2012 01:57:47 -0500
Subject: [PATCH 2/5] KVM: ARM: Fixup httbr initialization

Use 64 bit value passed to the hyp mode init code which simplifies that code and allows using a physical address for the HTTBR which is above the 4G limit.

Applies to: KVM: ARM: Hypervisor inititalization

---
 arch/arm/include/asm/kvm_mmu.h |    2 +-
 arch/arm/kvm/arm.c             |   16 +++++++++-------
 arch/arm/kvm/init.S            |   33 ++++++++++-----------------------
 arch/arm/kvm/mmu.c             |    3 ++-
 4 files changed, 22 insertions(+), 32 deletions(-)
arch/arm/include/asm/kvm_mmu.h +0 -0
index 4d55218..ab4272f 100644
@@ -32,7 +32,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu,

struct kvm_run *run);

void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);

-unsigned long kvm_mmu_get_httbr(void);
+phys_addr_t kvm_mmu_get_httbr(void);
int kvm_mmu_init(void);
void kvm_mmu_exit(void);
#endif /* __ARM_KVM_MMU_H__ */

arch/arm/kvm/arm.c +9 -7
index 9827a3d..39d5f51 100644
@@ -928,7 +928,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
 
 static void cpu_init_hyp_mode(void *vector)
 {
-	unsigned long pgd_ptr;
+	unsigned long long pgd_ptr;
 	unsigned long hyp_stack_ptr;
 	unsigned long stack_page;
 	unsigned long vector_ptr;
@@ -936,7 +936,7 @@ static void cpu_init_hyp_mode(void *vector)
 	/* Switch from the HYP stub to our own HYP init vector */
 	__hyp_set_vectors((unsigned long)vector);
 
-	pgd_ptr = kvm_mmu_get_httbr();
+	pgd_ptr = (unsigned long long)kvm_mmu_get_httbr();
 	stack_page = __get_cpu_var(kvm_arm_hyp_stack_page);
 	hyp_stack_ptr = stack_page + PAGE_SIZE;
 	vector_ptr = (unsigned long)__kvm_hyp_vector;
@@ -947,14 +947,16 @@ static void cpu_init_hyp_mode(void *vector)
 	 * list accordingly.
 	 */
 	asm volatile (
-		"mov	r0, %[pgd_ptr]\n\t"
-		"mov	r1, %[hyp_stack_ptr]\n\t"
-		"mov	r2, %[vector_ptr]\n\t"
+		"mov	r0, %[pgd_ptr_low]\n\t"
+		"mov	r1, %[pgd_ptr_high]\n\t"
+		"mov	r2, %[hyp_stack_ptr]\n\t"
+		"mov	r3, %[vector_ptr]\n\t"
 		"hvc	#0\n\t" : :
-		[pgd_ptr] "r" (pgd_ptr),
+		[pgd_ptr_low] "r" ((unsigned long)(pgd_ptr & 0xffffffff)),
+		[pgd_ptr_high] "r" ((unsigned long)(pgd_ptr >> 32ULL)),
 		[hyp_stack_ptr] "r" (hyp_stack_ptr),
 		[vector_ptr] "r" (vector_ptr) :
-		"r0", "r1", "r2", "r12");
+		"r0", "r1", "r2", "r3", "r12");
 }
 
 /**
arch/arm/kvm/init.S +10 -23
index 5b52851..f179f10 100644
@@ -25,14 +25,14 @@
 /********************************************************************
  * Hypervisor initialization
  *   - should be called with:
- *       r0 = Hypervisor pgd pointer
- *       r1 = top of Hyp stack (kernel VA)
- *       r2 = pointer to hyp vectors
+ *       r0,r1 = Hypervisor pgd pointer
+ *       r2 = top of Hyp stack (kernel VA)
+ *       r3 = pointer to hyp vectors
  */
 
 	.text
 	.pushsection    .hyp.idmap.text,"ax"
-	.align 12
+	.align 5
 __kvm_hyp_init:
 	.globl __kvm_hyp_init
 
@@ -47,17 +47,8 @@ __kvm_hyp_init:
 	W(b)	.
 
 __do_hyp_init:
-	@ Set the sp to end of this page and push data for later use
-ARM(	add	r12, pc, #(__kvm_init_sp - .)	)
-ARM(	sub	r12, r12, #8			)
-THUMB(	adr	r12, __kvm_init_sp		)
-	mov	sp, r12
-	push	{r1, r2}
-
-	@ 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
+	@ Set the HTTBR to point to the hypervisor PGD pointer passed
+	mcrr	p15, 4, r0, r1, c2
 
 	@ Set the HTCR and VTCR to the same shareability and cacheability
 	@ settings as the non-secure TTBCR and with T0SZ == 0.
@@ -70,8 +61,8 @@ THUMB(	adr	r12, __kvm_init_sp		)
 	mcr	p15, 4, r0, c2, c0, 2	@ HTCR
 
 	mrc	p15, 4, r1, c2, c1, 2	@ VTCR
-	ldr	r2, =VTCR_MASK
-	bic	r1, r1, r2
+	ldr	r12, =VTCR_MASK
+	bic	r1, r1, r12
 	bic	r0, r0, #(~VTCR_HTCR_SH)	@ clear non-reusable HTCR bits
 	orr	r1, r0, r1
 	orr	r1, r1, #(KVM_VTCR_SL0 | KVM_VTCR_T0SZ | KVM_VTCR_S)
@@ -108,19 +99,15 @@ THUMB(	adr	r12, __kvm_init_sp		)
 	isb
 
 	@ Set stack pointer and return to the kernel
-	pop	{r1, r2}
-	mov	sp, r1
+	mov	sp, r2
 
 	@ Set HVBAR to point to the HYP vectors
-	mcr	p15, 4, r2, c12, c0, 0	@ HVBAR
+	mcr	p15, 4, r3, c12, c0, 0	@ HVBAR
 
 	eret
 
 	.ltorg
 
-	.align 12
-
-	__kvm_init_sp:
 	.globl __kvm_hyp_init_end
 __kvm_hyp_init_end:
 
arch/arm/kvm/mmu.c +2 -1
index aac671d..6fc9489 100644
@@ -718,8 +718,9 @@ void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu)
 	mmu_free_memory_cache(&vcpu->arch.mmu_page_cache);
 }
 
-unsigned long kvm_mmu_get_httbr(void)
+phys_addr_t kvm_mmu_get_httbr(void)
 {
+	VM_BUG_ON(!virt_addr_valid(hyp_pgd));
 	return virt_to_phys(hyp_pgd);
 }
 
Signature · 1 line
1.7.9.5
Quoted 1 line

From 875a67eadd0a95b4cd2fd279ff4533fec1142b32 Mon Sep 17 00:00:00 2001

From: Christoffer Dall <[email protected]>
Date: Fri, 30 Nov 2012 02:01:26 -0500
Subject: [PATCH 3/5] KVM: ARM: Remove page requirements for hyp code

We can allocate over physically non-contigous pages so remove the alignment and page size requirement from arch/arm/kvm/interrupts.S.

Applies to: KVM: ARM: Hypervisor inititalization

---
 arch/arm/kvm/interrupts.S |    9 ---------
 1 file changed, 9 deletions(-)
arch/arm/kvm/interrupts.S +0 -9
index f62c621..8a1f2df 100644
@@ -28,7 +28,6 @@
 #include "interrupts_head.S"
 
 	.text
-	.align	PAGE_SHIFT
 
 __kvm_hyp_code_start:
 	.globl __kvm_hyp_code_start
@@ -490,13 +489,5 @@ dabt_die_str:
 svc_die_str:
 	.ascii	"unexpected HVC/SVC trap in Hyp mode at: %#08x"
 
-/*
- * The below lines makes sure the HYP mode code fits in a single page (the
- * assembler will bark at you if it doesn't). Please keep them together. If
- * you plan to restructure the code or increase its size over a page, you'll
- * have to fix the code in init_hyp_mode().
- */
 __kvm_hyp_code_end:
 	.globl	__kvm_hyp_code_end
-
-	.org	__kvm_hyp_code_start + PAGE_SIZE
Signature · 1 line
1.7.9.5
Quoted 1 line

From 713f0c91c2c881e14c8aa5e3a9b019e0ad07e4a3 Mon Sep 17 00:00:00 2001

From: Christoffer Dall <[email protected]>
Date: Fri, 30 Nov 2012 02:02:42 -0500
Subject: [PATCH 4/5] KVM: ARM: Revert unnecessary exported mm symbols

We don't support compiling KVM as a module anymore so let's get rid of these previously introduced exports.

---
 mm/memory.c |    2 --
 1 file changed, 2 deletions(-)
mm/memory.c +0 -2
index 5ae0164..fb135ba 100644
@@ -383,14 +383,12 @@ 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)
 {
Signature · 1 line
1.7.9.5
Quoted 1 line

From ac1eec2f0121850ddbf2b2a027cbcfb069ac74c3 Mon Sep 17 00:00:00 2001

From: Christoffer Dall <[email protected]>
Date: Fri, 30 Nov 2012 02:08:58 -0500
Subject: [PATCH 5/5] KVM: ARM: Split create_hyp_pte_mappings in io/non-io
versions

create_hyp_pte_mappings basically has all its logic in equally balanced if-else clauses, so splitting up the function makes things slightly more clear.

Applies to: KVM: ARM: Hypervisor inititalization

---
 arch/arm/kvm/mmu.c |   55 ++++++++++++++++++++++++++++------------------------
 1 file changed, 30 insertions(+), 25 deletions(-)
arch/arm/kvm/mmu.c +19 -24

57 lines not on this page · raw message

unsigned long start,
}

next = pmd_addr_end(addr, end);
- create_hyp_pte_mappings(pmd, addr, next, pfn_base);
+
+ /*
+ * If pfn_base is NULL, we map kernel pages into HYP with the
+ * virtual address. Otherwise, this is considered an I/O
+ * mapping and we map the physical region starting at
+ * *pfn_base to [start, end[.
+ */
+ if (!pfn_base)
+ create_hyp_pte_mappings(pmd, addr, next);
+ else
+ create_hyp_io_pte_mappings(pmd, addr, next, pfn_base);
}

return 0;

Signature · 6 lines
1.7.9.5



Thanks!!
-Christoffer
#24 lore raw

PATCH v4 6/14 KVM: ARM: Memory virtualization setup

In reply to 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 in arch/arm/kvm/arm_mmu.c:
- kvm_alloc_stage2_pgd(struct kvm *kvm);
- kvm_free_stage2_pgd(struct kvm *kvm);

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 |   19 ++
 arch/arm/include/asm/kvm_mmu.h  |    9 +
 arch/arm/kvm/Kconfig            |    1 
 arch/arm/kvm/arm.c              |   37 ++++
 arch/arm/kvm/interrupts.S       |   10 +
 arch/arm/kvm/mmu.c              |  393 +++++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/trace.h            |   46 +++++
 8 files changed, 515 insertions(+), 2 deletions(-)
arch/arm/include/asm/kvm_asm.h +2 -0
index 954bf7c..47a0e57 100644
@@ -57,6 +57,7 @@
 #define ARM_EXCEPTION_HVC	  7
 
 #ifndef __ASSEMBLY__
+struct kvm;
 struct kvm_vcpu;
 
 extern char __kvm_hyp_init[];
@@ -71,6 +72,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 +19 -0
index 67826b2..2fc5591 100644
@@ -111,4 +111,23 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices);
 struct kvm_one_reg;
 int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
 int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
+u64 kvm_call_hyp(void *hypfn, ...);
+
+#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
index 741ab8f..9bd0508 100644
@@ -33,6 +33,15 @@ 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);
+
 unsigned long kvm_mmu_get_httbr(void);
 int kvm_mmu_init(void);
 void kvm_mmu_exit(void);
arch/arm/kvm/Kconfig +1 -0
index c7eb178..bfa3174 100644
@@ -36,6 +36,7 @@ config KVM_ARM_HOST
 	depends on KVM
 	depends on MMU
 	depends on CPU_V7 && ARM_VIRT_EXT
+	select	MMU_NOTIFIER
 	---help---
 	  Provides host support for ARM processors.
 
arch/arm/kvm/arm.c +36 -1
index 8e1ea2b..5ac3132 100644
@@ -81,12 +81,33 @@ 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;
+
+	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)
@@ -104,10 +125,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]);
@@ -189,7 +216,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:
@@ -198,6 +231,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/interrupts.S +9 -1
index 98a67ca..1c83022 100644
@@ -33,7 +33,13 @@ __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
+ */
 ENTRY(__kvm_flush_vm_context)
 	bx	lr
 ENDPROC(__kvm_flush_vm_context)
@@ -41,10 +47,12 @@ ENDPROC(__kvm_flush_vm_context)
 /********************************************************************
  *  Hypervisor world-switch code
  */
-
 ENTRY(__kvm_vcpu_run)
 	bx	lr
 
+ENTRY(kvm_call_hyp)
+	bx	lr
+
 
 /********************************************************************
  * Hypervisor exception vector and handlers
arch/arm/kvm/mmu.c +393 -0

415 lines not on this page · raw message

arch/arm/kvm/trace.h +46 -0

53 lines not on this page · raw message

#25 lore raw

Re: [PATCH v4 06/14] KVM: ARM: Memory virtualization setup

Quoted 28 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 in arch/arm/kvm/arm_mmu.c:
- kvm_alloc_stage2_pgd(struct kvm *kvm);
- kvm_free_stage2_pgd(struct kvm *kvm);

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.

Quoted 7 lines

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.

Modulo my other comments, this patch looks alright to me. I'll go through with acks for the next series. In the meantime, I'll just skip over the patches that look ok.

Cheers,

Will

#26 lore raw

Re: [PATCH v4 06/14] KVM: ARM: Memory virtualization setup

Quoted 33 lines · Will Deacon
Quoted 32 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 in arch/arm/kvm/arm_mmu.c:
- kvm_alloc_stage2_pgd(struct kvm *kvm);
- kvm_free_stage2_pgd(struct kvm *kvm);

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.

Quoted 8 lines
Quoted 3 lines

Modulo my other comments, this patch looks alright to me. I'll go through with acks for the next series. In the meantime, I'll just skip over the patches that look ok.

thanks a lot for the review! I'll be getting back to you on all this at the end of this week.

-Christoffer

#27 lore raw

PATCH v4 7/14 KVM: ARM: Inject IRQs and FIQs from userspace

In reply to Christoffer Dall

From: Christoffer Dall <[email protected]>

All interrupt injection is now based on the VM ioctl KVM_IRQ_LINE. This works semantically well for the GIC as we in fact raise/lower a line on a machine component (the gic). The IOCTL uses the follwing struct.

struct kvm_irq_level {
	union {
		__u32 irq;     /* GSI */
		__s32 status;  /* not used for KVM_IRQ_LEVEL */
	};
	__u32 level;           /* 0 or 1 */
};

ARM can signal an interrupt either at the CPU level, or at the in-kernel irqchip (GIC), and for in-kernel irqchip can tell the GIC to use PPIs designated for specific cpus. The irq field is interpreted like this:

 ?bits:  | 31 ... 24 | 23  ... 16 | 15    ...    0 |
  field: | irq_type  | vcpu_index |   irq_number   |

The irq_type field has the following values:
- irq_type[0]: out-of-kernel GIC: irq_number 0 is IRQ, irq_number 1 is FIQ
- irq_type[1]: in-kernel GIC: SPI, irq_number between 32 and 1019 (incl.) (the vcpu_index field is ignored)
- irq_type[2]: in-kernel GIC: PPI, irq_number between 16 and 31 (incl.)

The irq_number thus corresponds to the irq ID in as in the GICv2 specs.

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

---
 Documentation/virtual/kvm/api.txt |   25 +++++++++++--
 arch/arm/include/asm/kvm_arm.h    |    1 +
 arch/arm/include/uapi/asm/kvm.h   |   21 +++++++++++
 arch/arm/kvm/arm.c                |   70 +++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/trace.h              |   25 +++++++++++++
 include/uapi/linux/kvm.h          |    1 +
 6 files changed, 139 insertions(+), 4 deletions(-)
Documentation/virtual/kvm/api.txt +21 -4
index 2a5dc41..845ceda 100644
@@ -615,15 +615,32 @@ 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 can signal an interrupt either at the CPU level, or at the in-kernel irqchip
+(GIC), and for in-kernel irqchip can tell the GIC to use PPIs designated for
+specific cpus.  The irq field is interpreted like this:
+
+ ?bits:  | 31 ... 24 | 23  ... 16 | 15    ...    0 |
+  field: | irq_type  | vcpu_index |     irq_id     |
+
+The irq_type field has the following values:
+- irq_type[0]: out-of-kernel GIC: irq_id 0 is IRQ, irq_id 1 is FIQ
+- irq_type[1]: in-kernel GIC: SPI, irq_id between 32 and 1019 (incl.)
+               (the vcpu_index field is ignored)
+- irq_type[2]: in-kernel GIC: PPI, irq_id between 16 and 31 (incl.)
+
+(The irq_id field thus corresponds nicely to the IRQ ID in the ARM GIC specs)
+
+In both cases, level is used to raise/lower the line.
 
 struct kvm_irq_level {
 	union {
arch/arm/include/asm/kvm_arm.h +1 -0
index f6e8f6f..4f54cda 100644
@@ -68,6 +68,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/include/uapi/asm/kvm.h +21 -0
index 00d61a6..a807d9a 100644
@@ -23,6 +23,7 @@
 #include <asm/ptrace.h>
 
 #define __KVM_HAVE_GUEST_DEBUG
+#define __KVM_HAVE_IRQ_LINE
 
 #define KVM_REG_SIZE(id)						\
 	(1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT))
@@ -79,4 +80,24 @@ struct kvm_arch_memory_slot {
 #define KVM_REG_ARM_CORE		(0x0010 << KVM_REG_ARM_COPROC_SHIFT)
 #define KVM_REG_ARM_CORE_REG(name)	(offsetof(struct kvm_regs, name) / 4)
 
+/* KVM_IRQ_LINE irq field index values */
+#define KVM_ARM_IRQ_TYPE_SHIFT		24
+#define KVM_ARM_IRQ_TYPE_MASK		0xff
+#define KVM_ARM_IRQ_VCPU_SHIFT		16
+#define KVM_ARM_IRQ_VCPU_MASK		0xff
+#define KVM_ARM_IRQ_NUM_SHIFT		0
+#define KVM_ARM_IRQ_NUM_MASK		0xffff
+
+/* irq_type field */
+#define KVM_ARM_IRQ_TYPE_CPU		0
+#define KVM_ARM_IRQ_TYPE_SPI		1
+#define KVM_ARM_IRQ_TYPE_PPI		2
+
+/* out-of-kernel GIC cpu interrupt injection irq_number field */
+#define KVM_ARM_IRQ_CPU_IRQ		0
+#define KVM_ARM_IRQ_CPU_FIQ		1
+
+/* Highest supported SPI, from VGIC_NR_IRQS */
+#define KVM_ARM_IRQ_GIC_MAX		127
+
 #endif /* __ARM_KVM_H__ */
arch/arm/kvm/arm.c +70 -0

88 lines not on this page · raw message

arch/arm/kvm/trace.h +25 -0
index 862b2cc..105d1f7 100644
@@ -39,6 +39,31 @@ TRACE_EVENT(kvm_exit,
 	TP_printk("PC: 0x%08lx", __entry->vcpu_pc)
 );
 
+TRACE_EVENT(kvm_irq_line,
+	TP_PROTO(unsigned int type, int vcpu_idx, int irq_num, int level),
+	TP_ARGS(type, vcpu_idx, irq_num, level),
+
+	TP_STRUCT__entry(
+		__field(	unsigned int,	type		)
+		__field(	int,		vcpu_idx	)
+		__field(	int,		irq_num		)
+		__field(	int,		level		)
+	),
+
+	TP_fast_assign(
+		__entry->type		= type;
+		__entry->vcpu_idx	= vcpu_idx;
+		__entry->irq_num	= irq_num;
+		__entry->level		= level;
+	),
+
+	TP_printk("Inject %s interrupt (%d), vcpu->idx: %d, num: %d, level: %d",
+		  (__entry->type == KVM_ARM_IRQ_TYPE_CPU) ? "CPU" :
+		  (__entry->type == KVM_ARM_IRQ_TYPE_PPI) ? "VGIC PPI" :
+		  (__entry->type == KVM_ARM_IRQ_TYPE_SPI) ? "VGIC SPI" : "UNKNOWN",
+		  __entry->type, __entry->vcpu_idx, __entry->irq_num, __entry->level)
+);
+
 TRACE_EVENT(kvm_unmap_hva,
 	TP_PROTO(unsigned long hva),
 	TP_ARGS(hva),
include/uapi/linux/kvm.h +1 -0
index ad15f10..1db0460 100644
@@ -115,6 +115,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: See Documentation/virtual/kvm/api.txt
 	 */
 	union {
 		__u32 irq;
#28 lore raw

Re: [PATCH v4 07/14] KVM: ARM: Inject IRQs and FIQs from userspace

Quoted 33 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

All interrupt injection is now based on the VM ioctl KVM_IRQ_LINE. This works semantically well for the GIC as we in fact raise/lower a line on a machine component (the gic). The IOCTL uses the follwing struct.

struct kvm_irq_level {
        union {
                __u32 irq;     /* GSI */
                __s32 status;  /* not used for KVM_IRQ_LEVEL */
        };
        __u32 level;           /* 0 or 1 */
};

ARM can signal an interrupt either at the CPU level, or at the in-kernel irqchip (GIC), and for in-kernel irqchip can tell the GIC to use PPIs designated for specific cpus. The irq field is interpreted like this:

  bits:  | 31 ... 24 | 23  ... 16 | 15    ...    0 |
  field: | irq_type  | vcpu_index |   irq_number   |

The irq_type field has the following values:
- irq_type[0]: out-of-kernel GIC: irq_number 0 is IRQ, irq_number 1 is FIQ
- irq_type[1]: in-kernel GIC: SPI, irq_number between 32 and 1019 (incl.) (the vcpu_index field is ignored)
- irq_type[2]: in-kernel GIC: PPI, irq_number between 16 and 31 (incl.)

The irq_number thus corresponds to the irq ID in as in the GICv2 specs.

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

[...]

Quoted 81 lines
arch/arm/kvm/arm.c +59 -0
index 5ac3132..15e2ab1 100644
@@ -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
@@ -272,6 +273,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)
@@ -312,6 +314,74 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
        return -EINVAL;
 }
 
+static int vcpu_interrupt_line(struct kvm_vcpu *vcpu, int number, bool level)
+{
+       int bit_index;
+       bool set;
+       unsigned long *ptr;
+
+       if (number == KVM_ARM_IRQ_CPU_IRQ)
+               bit_index = __ffs(HCR_VI);
+       else /* KVM_ARM_IRQ_CPU_FIQ */
+               bit_index = __ffs(HCR_VF);
+
+       ptr = (unsigned long *)&vcpu->arch.irq_lines;
+       if (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 == 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;
+}
+
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
+{
+       u32 irq = irq_level->irq;
+       unsigned int irq_type, vcpu_idx, irq_num;
+       int nrcpus = atomic_read(&kvm->online_vcpus);
+       struct kvm_vcpu *vcpu = NULL;
+       bool level = irq_level->level;
+
+       irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK;
+       vcpu_idx = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK;
+       irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK;
+
+       trace_kvm_irq_line(irq_type, vcpu_idx, irq_num, irq_level->level);
+
+       if (irq_type == KVM_ARM_IRQ_TYPE_CPU ||
+           irq_type == KVM_ARM_IRQ_TYPE_PPI) {
+               if (vcpu_idx >= nrcpus)
+                       return -EINVAL;
+
+               vcpu = kvm_get_vcpu(kvm, vcpu_idx);
+               if (!vcpu)
+                       return -EINVAL;
+       }
+

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

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+       switch (irq_type) {
+       case KVM_ARM_IRQ_TYPE_CPU:
+               if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
+                       return -EINVAL;
+
+               return vcpu_interrupt_line(vcpu, irq_num, level);
+       }
+
+       return -EINVAL;
+}

Holy cyclomatic complexity Batman! Any way this can be cleaned up?

Will

#29 lore raw

Re: [PATCH v4 07/14] KVM: ARM: Inject IRQs and FIQs from userspace

Quoted 122 lines · Will Deacon
Quoted 33 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

All interrupt injection is now based on the VM ioctl KVM_IRQ_LINE. This works semantically well for the GIC as we in fact raise/lower a line on a machine component (the gic). The IOCTL uses the follwing struct.

struct kvm_irq_level {
        union {
                __u32 irq;     /* GSI */
                __s32 status;  /* not used for KVM_IRQ_LEVEL */
        };
        __u32 level;           /* 0 or 1 */
};

ARM can signal an interrupt either at the CPU level, or at the in-kernel irqchip (GIC), and for in-kernel irqchip can tell the GIC to use PPIs designated for specific cpus. The irq field is interpreted like this:

  bits:  | 31 ... 24 | 23  ... 16 | 15    ...    0 |
  field: | irq_type  | vcpu_index |   irq_number   |

The irq_type field has the following values:
- irq_type[0]: out-of-kernel GIC: irq_number 0 is IRQ, irq_number 1 is FIQ
- irq_type[1]: in-kernel GIC: SPI, irq_number between 32 and 1019 (incl.) (the vcpu_index field is ignored)
- irq_type[2]: in-kernel GIC: PPI, irq_number between 16 and 31 (incl.)

The irq_number thus corresponds to the irq ID in as in the GICv2 specs.

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

[...]

Quoted 85 lines
arch/arm/kvm/arm.c +63 -0
index 5ac3132..15e2ab1 100644
@@ -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
@@ -272,6 +273,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)
@@ -312,6 +314,74 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
        return -EINVAL;
 }
 
+static int vcpu_interrupt_line(struct kvm_vcpu *vcpu, int number, bool level)
+{
+       int bit_index;
+       bool set;
+       unsigned long *ptr;
+
+       if (number == KVM_ARM_IRQ_CPU_IRQ)
+               bit_index = __ffs(HCR_VI);
+       else /* KVM_ARM_IRQ_CPU_FIQ */
+               bit_index = __ffs(HCR_VF);
+
+       ptr = (unsigned long *)&vcpu->arch.irq_lines;
+       if (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 == 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;
+}
+
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
+{
+       u32 irq = irq_level->irq;
+       unsigned int irq_type, vcpu_idx, irq_num;
+       int nrcpus = atomic_read(&kvm->online_vcpus);
+       struct kvm_vcpu *vcpu = NULL;
+       bool level = irq_level->level;
+
+       irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK;
+       vcpu_idx = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK;
+       irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK;
+
+       trace_kvm_irq_line(irq_type, vcpu_idx, irq_num, irq_level->level);
+
+       if (irq_type == KVM_ARM_IRQ_TYPE_CPU ||
+           irq_type == KVM_ARM_IRQ_TYPE_PPI) {
+               if (vcpu_idx >= nrcpus)
+                       return -EINVAL;
+
+               vcpu = kvm_get_vcpu(kvm, vcpu_idx);
+               if (!vcpu)
+                       return -EINVAL;
+       }
+
+       switch (irq_type) {
+       case KVM_ARM_IRQ_TYPE_CPU:
+               if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
+                       return -EINVAL;
Quoted 9 lines

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

Quoted 6 lines
arch/arm/kvm/arm.c +6 -0
+
+               return vcpu_interrupt_line(vcpu, irq_num, level);
+       }
+
+       return -EINVAL;
+}

Holy cyclomatic complexity Batman! Any way this can be cleaned up?

you mean the interface or the implementation of kvm_vm_ioctl_irq_line? If the latter, there's just a lot of bits to decode here.

Thanks,
-Christoffer

#30 lore raw

Re: [PATCH v4 07/14] KVM: ARM: Inject IRQs and FIQs from userspace

Quoted 39 lines · Christoffer Dall
Quoted 29 lines · Will Deacon
Quoted 28 lines · Christoffer Dall
arch/arm/kvm/arm.c +28 -0
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
+{
+       u32 irq = irq_level->irq;
+       unsigned int irq_type, vcpu_idx, irq_num;
+       int nrcpus = atomic_read(&kvm->online_vcpus);
+       struct kvm_vcpu *vcpu = NULL;
+       bool level = irq_level->level;
+
+       irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK;
+       vcpu_idx = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK;
+       irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK;
+
+       trace_kvm_irq_line(irq_type, vcpu_idx, irq_num, irq_level->level);
+
+       if (irq_type == KVM_ARM_IRQ_TYPE_CPU ||
+           irq_type == KVM_ARM_IRQ_TYPE_PPI) {
+               if (vcpu_idx >= nrcpus)
+                       return -EINVAL;
+
+               vcpu = kvm_get_vcpu(kvm, vcpu_idx);
+               if (!vcpu)
+                       return -EINVAL;
+       }
+
+       switch (irq_type) {
+       case KVM_ARM_IRQ_TYPE_CPU:
+               if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
+                       return -EINVAL;
Quoted 9 lines

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

Quoted 6 lines
arch/arm/kvm/arm.c +6 -0
+
+               return vcpu_interrupt_line(vcpu, irq_num, level);
+       }
+
+       return -EINVAL;
+}

Holy cyclomatic complexity Batman! Any way this can be cleaned up?

Quoted 2 lines

you mean the interface or the implementation of kvm_vm_ioctl_irq_line? If the latter, there's just a lot of bits to decode here.

I just think that this function is incredibly hard to read: different nested conditions under duplicate checks of the same variables which differ between an if(...) and a switch(...). I appreciate that it's a complex problem, which is why I helpfully didn't suggest an alternative! There must be *something* we can do though.

Will

#31 lore raw

Re: [PATCH v4 07/14] KVM: ARM: Inject IRQs and FIQs from userspace

Quoted 40 lines · Will Deacon
Quoted 39 lines · Christoffer Dall
Quoted 38 lines · Will Deacon
Quoted 24 lines · Christoffer Dall
arch/arm/kvm/arm.c +24 -0
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
+{
+       u32 irq = irq_level->irq;
+       unsigned int irq_type, vcpu_idx, irq_num;
+       int nrcpus = atomic_read(&kvm->online_vcpus);
+       struct kvm_vcpu *vcpu = NULL;
+       bool level = irq_level->level;
+
+       irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK;
+       vcpu_idx = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK;
+       irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK;
+
+       trace_kvm_irq_line(irq_type, vcpu_idx, irq_num, irq_level->level);
+
+       if (irq_type == KVM_ARM_IRQ_TYPE_CPU ||
+           irq_type == KVM_ARM_IRQ_TYPE_PPI) {
+               if (vcpu_idx >= nrcpus)
+                       return -EINVAL;
+
+               vcpu = kvm_get_vcpu(kvm, vcpu_idx);
+               if (!vcpu)
+                       return -EINVAL;
+       }
+

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

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+       switch (irq_type) {
+       case KVM_ARM_IRQ_TYPE_CPU:
+               if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
+                       return -EINVAL;
+
+               return vcpu_interrupt_line(vcpu, irq_num, level);
+       }
+
+       return -EINVAL;
+}

Holy cyclomatic complexity Batman! Any way this can be cleaned up?

Quoted 9 lines
Quoted 2 lines

you mean the interface or the implementation of kvm_vm_ioctl_irq_line? If the latter, there's just a lot of bits to decode here.

I just think that this function is incredibly hard to read: different nested conditions under duplicate checks of the same variables which differ between an if(...) and a switch(...). I appreciate that it's a complex problem, which is why I helpfully didn't suggest an alternative! There must be *something* we can do though.

It is indeed a bit hard to read, but then wait to see the vgic code ! :)

In all seriousness, I will unite myself with an alcoholic beverage one of these evenings and try to see what I can do about it, maybe split it up somehow, I'll give it a shot.

Cheers,
-Christoffer

#32 lore raw

Re: [PATCH v4 07/14] KVM: ARM: Inject IRQs and FIQs from userspace

Quoted 5 lines · Christoffer Dall
Quoted 1 line · Will Deacon

On Mon, Nov 19, 2012 at 03:04:38PM +0000, Christoffer Dall wrote:

In all seriousness, I will unite myself with an alcoholic beverage one of these evenings and try to see what I can do about it, maybe split it up somehow, I'll give it a shot.

So this might be to do with the way you've split up the patches (likely I'll have similar complaints against the vGIC code, but at least it will make sense there!)...

Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
arch/arm/kvm/arm.c +26 -0
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
+{
+       u32 irq = irq_level->irq;
+       unsigned int irq_type, vcpu_idx, irq_num;
+       int nrcpus = atomic_read(&kvm->online_vcpus);
+       struct kvm_vcpu *vcpu = NULL;
+       bool level = irq_level->level;
+
+       irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK;
+       vcpu_idx = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK;
+       irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK;
+
+       trace_kvm_irq_line(irq_type, vcpu_idx, irq_num, irq_level->level);
+
+       if (irq_type == KVM_ARM_IRQ_TYPE_CPU ||
+           irq_type == KVM_ARM_IRQ_TYPE_PPI) {
+               if (vcpu_idx >= nrcpus)
+                       return -EINVAL;
+
+               vcpu = kvm_get_vcpu(kvm, vcpu_idx);
+               if (!vcpu)
+                       return -EINVAL;
+       }
+
+       switch (irq_type) {
+       case KVM_ARM_IRQ_TYPE_CPU:
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

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

Quoted 8 lines
arch/arm/kvm/arm.c +8 -0
+               if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
+                       return -EINVAL;
+
+               return vcpu_interrupt_line(vcpu, irq_num, level);
+       }
+
+       return -EINVAL;
+}

This obviously doesn't handle PPIs, which is where the confusion lies. You can just as easily write it as:

int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)

{
	u32 irq = irq_level->irq;
	unsigned int irq_type, vcpu_idx, irq_num;
	int nrcpus = atomic_read(&kvm->online_vcpus);
	struct kvm_vcpu *vcpu = NULL;
	bool level = irq_level->level;

	irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK;
	vcpu_idx = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK;
	irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK;

	trace_kvm_irq_line(irq_type, vcpu_idx, irq_num, irq_level->level);

	if (irq_type != KVM_ARM_IRQ_TYPE_CPU)
		return -EINVAL;

	if (vcpu_idx >= nrcpus)
		return -EINVAL;

	vcpu = kvm_get_vcpu(kvm, vcpu_idx);
	if (!vcpu)
		return -EINVAL;

	if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
		return -EINVAL;

	return vcpu_interrupt_line(vcpu, irq_num, level);
}

Then add all the IRQ complexity in a later patch!

Will

#33 lore raw

Re: [PATCH v4 07/14] KVM: ARM: Inject IRQs and FIQs from userspace

Quoted 79 lines · Will Deacon
Quoted 5 lines · Christoffer Dall
Quoted 1 line · Will Deacon

On Mon, Nov 19, 2012 at 03:04:38PM +0000, Christoffer Dall wrote:

In all seriousness, I will unite myself with an alcoholic beverage one of these evenings and try to see what I can do about it, maybe split it up somehow, I'll give it a shot.

So this might be to do with the way you've split up the patches (likely I'll have similar complaints against the vGIC code, but at least it will make sense there!)...

Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
arch/arm/kvm/arm.c +26 -0
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
+{
+       u32 irq = irq_level->irq;
+       unsigned int irq_type, vcpu_idx, irq_num;
+       int nrcpus = atomic_read(&kvm->online_vcpus);
+       struct kvm_vcpu *vcpu = NULL;
+       bool level = irq_level->level;
+
+       irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK;
+       vcpu_idx = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK;
+       irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK;
+
+       trace_kvm_irq_line(irq_type, vcpu_idx, irq_num, irq_level->level);
+
+       if (irq_type == KVM_ARM_IRQ_TYPE_CPU ||
+           irq_type == KVM_ARM_IRQ_TYPE_PPI) {
+               if (vcpu_idx >= nrcpus)
+                       return -EINVAL;
+
+               vcpu = kvm_get_vcpu(kvm, vcpu_idx);
+               if (!vcpu)
+                       return -EINVAL;
+       }
+
+       switch (irq_type) {
+       case KVM_ARM_IRQ_TYPE_CPU:
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

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

Quoted 8 lines
arch/arm/kvm/arm.c +8 -0
+               if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
+                       return -EINVAL;
+
+               return vcpu_interrupt_line(vcpu, irq_num, level);
+       }
+
+       return -EINVAL;
+}

This obviously doesn't handle PPIs, which is where the confusion lies. You can just as easily write it as:

int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)

{
        u32 irq = irq_level->irq;
        unsigned int irq_type, vcpu_idx, irq_num;
        int nrcpus = atomic_read(&kvm->online_vcpus);
        struct kvm_vcpu *vcpu = NULL;
        bool level = irq_level->level;

        irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK;
        vcpu_idx = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK;
        irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK;

        trace_kvm_irq_line(irq_type, vcpu_idx, irq_num, irq_level->level);

        if (irq_type != KVM_ARM_IRQ_TYPE_CPU)
                return -EINVAL;

        if (vcpu_idx >= nrcpus)
                return -EINVAL;

        vcpu = kvm_get_vcpu(kvm, vcpu_idx);
        if (!vcpu)
                return -EINVAL;

        if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
                return -EINVAL;

        return vcpu_interrupt_line(vcpu, irq_num, level);
}
Quoted 2 lines

Then add all the IRQ complexity in a later patch!

that was pretty constructive, I did just that. Thanks.

#34 lore raw

PATCH v4 8/14 KVM: ARM: World-switch implementation

In reply to Christoffer Dall

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

The following Hyp-ABI is also documented in the code:

Hyp-ABI: Calling HYP-mode functions from host (in SVC mode):
Switching to Hyp mode is done through a simple HVC #0 instruction. The exception vector code will check that the HVC comes from VMID==0 and if so will push the necessary state (SPSR, lr_usr) on the Hyp stack.
- r0 contains a pointer to a HYP function
- r1, r2, and r3 contain arguments to the above function.
- The HYP function will be called with its arguments in r0, r1 and r2. On HYP function return, we return directly to SVC.

A call to a function executing in Hyp mode is performed like the following:

<svc code>
ldr r0, =BSYM(my_hyp_fn)
ldr r1, =my_param
hvc #0 ; Call my_hyp_fn(my_param) from HYP mode <svc code>

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  |   42 ++++
 arch/arm/include/asm/kvm_host.h |    9 +
 arch/arm/kernel/asm-offsets.c   |   23 ++
 arch/arm/kvm/arm.c              |  166 ++++++++++++++++
 arch/arm/kvm/interrupts.S       |  352 +++++++++++++++++++++++++++++++++-
 arch/arm/kvm/interrupts_head.S  |  409 +++++++++++++++++++++++++++++++++++++++
 6 files changed, 998 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/kvm/interrupts_head.S
arch/arm/include/asm/kvm_arm.h +42 -0
index 4f54cda..2254bd8 100644
@@ -98,6 +98,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)
@@ -127,6 +139,36 @@
 #else
 #define VTTBR_X		(5 - VTCR_GUEST_T0SZ)
 #endif
+#define VTTBR_BADDR_SHIFT (VTTBR_X - 1)
+#define VTTBR_BADDR_MASK  (((1LLU << (40 - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
+#define VTTBR_VMID_SHIFT  (48LLU)
+#define VTTBR_VMID_MASK	  (0xffLLU << VTTBR_VMID_SHIFT)
+
+/* 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 +9 -0
index 2fc5591..20e0254 100644
@@ -21,6 +21,7 @@
 
 #include <asm/kvm.h>
 #include <asm/kvm_asm.h>
+#include <asm/fpstate.h>
 
 #define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS
 #define KVM_MEMORY_SLOTS 32
@@ -85,6 +86,14 @@ struct kvm_vcpu_arch {
 	u32 hxfar;		/* Hyp Data/Inst Fault Address Register */
 	u32 hpfar;		/* Hyp IPA Fault Address Register */
 
+	/* 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.
+	 */
 	/* Interrupt related fields */
 	u32 irq_lines;		/* IRQ and FIQ levels */
 
arch/arm/kernel/asm-offsets.c +23 -0
index c985b48..95cab37 100644
@@ -13,6 +13,7 @@
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/dma-mapping.h>
+#include <linux/kvm_host.h>
 #include <asm/cacheflush.h>
 #include <asm/glue-df.h>
 #include <asm/glue-pf.h>
@@ -146,5 +147,27 @@ int main(void)
   DEFINE(DMA_BIDIRECTIONAL,	DMA_BIDIRECTIONAL);
   DEFINE(DMA_TO_DEVICE,		DMA_TO_DEVICE);
   DEFINE(DMA_FROM_DEVICE,	DMA_FROM_DEVICE);
+#ifdef CONFIG_KVM_ARM_HOST
+  DEFINE(VCPU_KVM,		offsetof(struct kvm_vcpu, kvm));
+  DEFINE(VCPU_MIDR,		offsetof(struct kvm_vcpu, arch.midr));
+  DEFINE(VCPU_CP15,		offsetof(struct kvm_vcpu, arch.cp15));
+  DEFINE(VCPU_VFP_GUEST,	offsetof(struct kvm_vcpu, arch.vfp_guest));
+  DEFINE(VCPU_VFP_HOST,		offsetof(struct kvm_vcpu, arch.vfp_host));
+  DEFINE(VCPU_REGS,		offsetof(struct kvm_vcpu, arch.regs));
+  DEFINE(VCPU_USR_REGS,		offsetof(struct kvm_vcpu, arch.regs.usr_regs));
+  DEFINE(VCPU_SVC_REGS,		offsetof(struct kvm_vcpu, arch.regs.svc_regs));
+  DEFINE(VCPU_ABT_REGS,		offsetof(struct kvm_vcpu, arch.regs.abt_regs));
+  DEFINE(VCPU_UND_REGS,		offsetof(struct kvm_vcpu, arch.regs.und_regs));
+  DEFINE(VCPU_IRQ_REGS,		offsetof(struct kvm_vcpu, arch.regs.irq_regs));
+  DEFINE(VCPU_FIQ_REGS,		offsetof(struct kvm_vcpu, arch.regs.fiq_regs));
+  DEFINE(VCPU_PC,		offsetof(struct kvm_vcpu, arch.regs.usr_regs.ARM_pc));
+  DEFINE(VCPU_CPSR,		offsetof(struct kvm_vcpu, arch.regs.usr_regs.ARM_cpsr));
+  DEFINE(VCPU_IRQ_LINES,	offsetof(struct kvm_vcpu, arch.irq_lines));
+  DEFINE(VCPU_HSR,		offsetof(struct kvm_vcpu, arch.hsr));
+  DEFINE(VCPU_HxFAR,		offsetof(struct kvm_vcpu, arch.hxfar));
+  DEFINE(VCPU_HPFAR,		offsetof(struct kvm_vcpu, arch.hpfar));
+  DEFINE(VCPU_HYP_PC,		offsetof(struct kvm_vcpu, arch.hyp_pc));
+  DEFINE(KVM_VTTBR,		offsetof(struct kvm, arch.vttbr));
+#endif
   return 0; 
 }
arch/arm/kvm/arm.c +165 -1

201 lines not on this page · raw message

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

395 lines not on this page · raw message

arch/arm/kvm/interrupts_head.S new file +409 -0

409 lines not on this page · raw message

#35 lore raw

Re: [PATCH v4 08/14] KVM: ARM: World-switch implementation

Quoted 4 lines · Christoffer Dall

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

[...]

Quoted 102 lines
arch/arm/kvm/arm.c +66 -0
index 15e2ab1..d8f8c60 100644
@@ -40,6 +40,7 @@
 #include <asm/kvm_arm.h>
 #include <asm/kvm_asm.h>
 #include <asm/kvm_mmu.h>
+#include <asm/kvm_emulate.h>
 
 #ifdef REQUIRES_VIRT
 __asm__(".arch_extension       virt");
@@ -49,6 +50,10 @@ static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
 static struct vfp_hard_struct __percpu *kvm_host_vfp_state;
 static unsigned long hyp_default_vectors;
 
+/* The VMID used in the VTTBR */
+static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1);
+static u8 kvm_next_vmid;
+static DEFINE_SPINLOCK(kvm_vmid_lock);
 
 int kvm_arch_hardware_enable(void *garbage)
 {
@@ -264,6 +269,8 @@ int __attribute_const__ kvm_target_cpu(void)
 
 int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
 {
+       /* Force users to call KVM_ARM_VCPU_INIT */
+       vcpu->arch.target = -1;
        return 0;
 }
 
@@ -274,6 +281,7 @@ void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)
 void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 {
        vcpu->cpu = cpu;
+       vcpu->arch.vfp_host = this_cpu_ptr(kvm_host_vfp_state);
 }
 
 void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
@@ -306,12 +314,168 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
 
 int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v)
 {
+       return v->mode == IN_GUEST_MODE;
+}
+
+static void reset_vm_context(void *info)
+{
+       kvm_call_hyp(__kvm_flush_vm_context);
+}
+
+/**
+ * need_new_vmid_gen - check that the VMID is still valid
+ * @kvm: The VM's VMID to checkt
+ *
+ * return true if there is a new generation of VMIDs being used
+ *
+ * The hardware supports only 256 values with the value zero reserved for the
+ * host, so we check if an assigned value belongs to a previous generation,
+ * which which requires us to assign a new value. If we're the first to use a
+ * VMID for the new generation, we must flush necessary caches and TLBs on all
+ * CPUs.
+ */
+static bool need_new_vmid_gen(struct kvm *kvm)
+{
+       return unlikely(kvm->arch.vmid_gen != atomic64_read(&kvm_vmid_gen));
+}
+
+/**
+ * update_vttbr - Update the VTTBR with a valid VMID before the guest runs
+ * @kvm        The guest that we are about to run
+ *
+ * Called from kvm_arch_vcpu_ioctl_run before entering the guest to ensure the
+ * VM has a valid VMID, otherwise assigns a new one and flushes corresponding
+ * caches and TLBs.
+ */
+static void update_vttbr(struct kvm *kvm)
+{
+       phys_addr_t pgd_phys;
+       u64 vmid;
+
+       if (!need_new_vmid_gen(kvm))
+               return;
+
+       spin_lock(&kvm_vmid_lock);
+
+       /* First user of a new VMID generation? */
+       if (unlikely(kvm_next_vmid == 0)) {
+               atomic64_inc(&kvm_vmid_gen);
+               kvm_next_vmid = 1;
+
+               /*
+                * On SMP we know no other CPUs can use this CPU's or
+                * each other's VMID since the kvm_vmid_lock blocks
+                * them from reentry to the guest.
+                */
+               on_each_cpu(reset_vm_context, NULL, 1);
+       }
+
+       kvm->arch.vmid_gen = atomic64_read(&kvm_vmid_gen);
+       kvm->arch.vmid = kvm_next_vmid;

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

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+       kvm_next_vmid++;
+
+       /* update vttbr to be used with the new vmid */
+       pgd_phys = virt_to_phys(kvm->arch.pgd);
+       vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK;
+       kvm->arch.vttbr = pgd_phys & VTTBR_BADDR_MASK;
+       kvm->arch.vttbr |= vmid;
+
+       spin_unlock(&kvm_vmid_lock);
+}

I must be missing something here: how do you ensure that a guest running on multiple CPUs continues to have the same VMID across them after a rollover?

Quoted 50 lines
arch/arm/kvm/arm.c +44 -1
+
+/*
+ * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on
+ * proper exit to QEMU.
+ */
+static int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
+                      int exception_index)
+{
+       run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
        return 0;
 }
 
+/**
+ * kvm_arch_vcpu_ioctl_run - the main VCPU run function to execute guest code
+ * @vcpu:      The VCPU pointer
+ * @run:       The kvm_run structure pointer used for userspace state exchange
+ *
+ * This function is called through the VCPU_RUN ioctl called from user space. It
+ * will execute VM code in a loop until the time slice for the process is used
+ * or some emulation is needed from user space in which case the function will
+ * return with return value 0 and with the kvm_run structure filled in with the
+ * required data for the requested emulation.
+ */
 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
-       return -EINVAL;
+       int ret;
+       sigset_t sigsaved;
+
+       /* Make sure they initialize the vcpu with KVM_ARM_VCPU_INIT */
+       if (unlikely(vcpu->arch.target < 0))
+               return -ENOEXEC;
+
+       if (vcpu->sigset_active)
+               sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
+
+       ret = 1;
+       run->exit_reason = KVM_EXIT_UNKNOWN;
+       while (ret > 0) {
+               /*
+                * Check conditions before entering the guest
+                */
+               cond_resched();
+
+               update_vttbr(vcpu->kvm);
+
+               local_irq_disable();
+
+               /*
+                * Re-check atomic conditions

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

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+                */
+               if (signal_pending(current)) {
+                       ret = -EINTR;
+                       run->exit_reason = KVM_EXIT_INTR;
+               }
+
+               if (ret <= 0 || need_new_vmid_gen(vcpu->kvm)) {
+                       local_irq_enable();
+                       continue;
+               }

I don't see what the VMID generation check buys you here as you're not holding the vmid lock, so rollover can occur at any time. If rollover *does* occur, you'll get signalled when re-enabling interrupts during guest entry, right?

Is it because your rollover handling doesn't actually update the vttbr directly and relies on guest sched in to do it? If so, this all feels pretty racy to me.

Will

#36 lore raw

Re: [PATCH v4 08/14] KVM: ARM: World-switch implementation

Quoted 116 lines · Will Deacon
Quoted 4 lines · Christoffer Dall

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

[...]

Quoted 108 lines
arch/arm/kvm/arm.c +72 -0
index 15e2ab1..d8f8c60 100644
@@ -40,6 +40,7 @@
 #include <asm/kvm_arm.h>
 #include <asm/kvm_asm.h>
 #include <asm/kvm_mmu.h>
+#include <asm/kvm_emulate.h>
 
 #ifdef REQUIRES_VIRT
 __asm__(".arch_extension       virt");
@@ -49,6 +50,10 @@ static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
 static struct vfp_hard_struct __percpu *kvm_host_vfp_state;
 static unsigned long hyp_default_vectors;
 
+/* The VMID used in the VTTBR */
+static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1);
+static u8 kvm_next_vmid;
+static DEFINE_SPINLOCK(kvm_vmid_lock);
 
 int kvm_arch_hardware_enable(void *garbage)
 {
@@ -264,6 +269,8 @@ int __attribute_const__ kvm_target_cpu(void)
 
 int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
 {
+       /* Force users to call KVM_ARM_VCPU_INIT */
+       vcpu->arch.target = -1;
        return 0;
 }
 
@@ -274,6 +281,7 @@ void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)
 void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 {
        vcpu->cpu = cpu;
+       vcpu->arch.vfp_host = this_cpu_ptr(kvm_host_vfp_state);
 }
 
 void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
@@ -306,12 +314,168 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
 
 int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v)
 {
+       return v->mode == IN_GUEST_MODE;
+}
+
+static void reset_vm_context(void *info)
+{
+       kvm_call_hyp(__kvm_flush_vm_context);
+}
+
+/**
+ * need_new_vmid_gen - check that the VMID is still valid
+ * @kvm: The VM's VMID to checkt
+ *
+ * return true if there is a new generation of VMIDs being used
+ *
+ * The hardware supports only 256 values with the value zero reserved for the
+ * host, so we check if an assigned value belongs to a previous generation,
+ * which which requires us to assign a new value. If we're the first to use a
+ * VMID for the new generation, we must flush necessary caches and TLBs on all
+ * CPUs.
+ */
+static bool need_new_vmid_gen(struct kvm *kvm)
+{
+       return unlikely(kvm->arch.vmid_gen != atomic64_read(&kvm_vmid_gen));
+}
+
+/**
+ * update_vttbr - Update the VTTBR with a valid VMID before the guest runs
+ * @kvm        The guest that we are about to run
+ *
+ * Called from kvm_arch_vcpu_ioctl_run before entering the guest to ensure the
+ * VM has a valid VMID, otherwise assigns a new one and flushes corresponding
+ * caches and TLBs.
+ */
+static void update_vttbr(struct kvm *kvm)
+{
+       phys_addr_t pgd_phys;
+       u64 vmid;
+
+       if (!need_new_vmid_gen(kvm))
+               return;
+
+       spin_lock(&kvm_vmid_lock);
+
+       /* First user of a new VMID generation? */
+       if (unlikely(kvm_next_vmid == 0)) {
+               atomic64_inc(&kvm_vmid_gen);
+               kvm_next_vmid = 1;
+
+               /*
+                * On SMP we know no other CPUs can use this CPU's or
+                * each other's VMID since the kvm_vmid_lock blocks
+                * them from reentry to the guest.
+                */
+               on_each_cpu(reset_vm_context, NULL, 1);
+       }
+
+       kvm->arch.vmid_gen = atomic64_read(&kvm_vmid_gen);
+       kvm->arch.vmid = kvm_next_vmid;
+       kvm_next_vmid++;
+
+       /* update vttbr to be used with the new vmid */
+       pgd_phys = virt_to_phys(kvm->arch.pgd);
+       vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK;
+       kvm->arch.vttbr = pgd_phys & VTTBR_BADDR_MASK;
Quoted 9 lines

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

Quoted 4 lines
arch/arm/kvm/arm.c +4 -0
+       kvm->arch.vttbr |= vmid;
+
+       spin_unlock(&kvm_vmid_lock);
+}

I must be missing something here: how do you ensure that a guest running on multiple CPUs continues to have the same VMID across them after a rollover?

when a roll over occurs, there's no problem until someone comes along that doesn't have a valid vmid (need_new_vmid_gen will return true).

In this case, to assign a vmid, we need to start a new generation of id's to assign one, and must ensure that all old vmid's are no longer used. So how do we ensure that?

Well, we increment the kvm_vmid_gen, causing all other cpus who try to run a VM to hit the spin_lock if they exit the VMs. We reserve the vmid 1 for the new cpu, and we call on_each_cpu, which causes an ipi to all other physical cpus, and waits until the other physical cpus actually complete reset_vm_context.

At this point, once on_each_cpu(reset_vm_context) returns, all other physical CPUs have cleared their data structures for occurences of old vmids, and the kvm_vmid_gen has been incremented, so no other vcpus can come and claim other vmids until we unlock the spinlock, and everything starts over.

Makes sense?

Quoted 66 lines
Quoted 50 lines
arch/arm/kvm/arm.c +44 -1
+
+/*
+ * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on
+ * proper exit to QEMU.
+ */
+static int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
+                      int exception_index)
+{
+       run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
        return 0;
 }
 
+/**
+ * kvm_arch_vcpu_ioctl_run - the main VCPU run function to execute guest code
+ * @vcpu:      The VCPU pointer
+ * @run:       The kvm_run structure pointer used for userspace state exchange
+ *
+ * This function is called through the VCPU_RUN ioctl called from user space. It
+ * will execute VM code in a loop until the time slice for the process is used
+ * or some emulation is needed from user space in which case the function will
+ * return with return value 0 and with the kvm_run structure filled in with the
+ * required data for the requested emulation.
+ */
 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
-       return -EINVAL;
+       int ret;
+       sigset_t sigsaved;
+
+       /* Make sure they initialize the vcpu with KVM_ARM_VCPU_INIT */
+       if (unlikely(vcpu->arch.target < 0))
+               return -ENOEXEC;
+
+       if (vcpu->sigset_active)
+               sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
+
+       ret = 1;
+       run->exit_reason = KVM_EXIT_UNKNOWN;
+       while (ret > 0) {
+               /*
+                * Check conditions before entering the guest
+                */
+               cond_resched();
+
+               update_vttbr(vcpu->kvm);
+
+               local_irq_disable();
+
+               /*
+                * Re-check atomic conditions

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

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+                */
+               if (signal_pending(current)) {
+                       ret = -EINTR;
+                       run->exit_reason = KVM_EXIT_INTR;
+               }
+
+               if (ret <= 0 || need_new_vmid_gen(vcpu->kvm)) {
+                       local_irq_enable();
+                       continue;
+               }

I don't see what the VMID generation check buys you here as you're not holding the vmid lock, so rollover can occur at any time. If rollover *does* occur, you'll get signalled when re-enabling interrupts during guest entry, right?

Quoted 4 lines

Is it because your rollover handling doesn't actually update the vttbr directly and relies on guest sched in to do it? If so, this all feels pretty racy to me.

The point is that if we didn't do this re-check after local_irq_disable, we could receive the ipi from on_each_cpu in update_vttbr after the update_vttbr and before local_irq_disable, and even though that would clear CPU data structures, we would still use the old vmid. Once we enter the guest, the IPI will force a complete round-trip and thus a second call to update_vttbr, which would then cause us to assign a new vmid in the new generation.

We ensure this doesn't happen by explicitly forcing a re-check of the vmid generation.

In other words, there's a hole between spin_unlock and local_irq_disable.

See this previous discussion of the matter:
https://lists.cs.columbia.edu/pipermail/kvmarm/2012-June/002452.html

-Christoffer

#37 lore raw

Re: [PATCH v4 08/14] KVM: ARM: World-switch implementation

Quoted 9 lines · Christoffer Dall
Quoted 5 lines · Will Deacon

I must be missing something here: how do you ensure that a guest running on multiple CPUs continues to have the same VMID across them after a rollover?

when a roll over occurs, there's no problem until someone comes along that doesn't have a valid vmid (need_new_vmid_gen will return true).

Well a roll-over is triggered by somebody not having a valid VMID and us failing to allocate a new one from the current generation.

Quoted 10 lines

In this case, to assign a vmid, we need to start a new generation of id's to assign one, and must ensure that all old vmid's are no longer used. So how do we ensure that?

Well, we increment the kvm_vmid_gen, causing all other cpus who try to run a VM to hit the spin_lock if they exit the VMs. We reserve the vmid 1 for the new cpu, and we call on_each_cpu, which causes an ipi to all other physical cpus, and waits until the other physical cpus actually complete reset_vm_context.

Quoted 7 lines

At this point, once on_each_cpu(reset_vm_context) returns, all other physical CPUs have cleared their data structures for occurences of old vmids, and the kvm_vmid_gen has been incremented, so no other vcpus can come and claim other vmids until we unlock the spinlock, and everything starts over.

Makes sense?

Yes, but I still don't understand how you ensure VMID consistency across different vcpus of the same vm. Imagine the following scenario:

We have two VMs:

VM0: VCPU0 on physical CPU0, VCPU1 on physical CPU1
VM1: VCPU0 on physical CPU2

Also assume that VM0 is happily running and we want to schedule VM1 for the first time. Finally, also assume that kvm_next_vmid is zero (that is, the next allocation will trigger a roll-over).

Now, we want to schedule VM1:

	kvm_arch_init_vm
		kvm->arch.vmid_gen = 0;
	kvm_arch_vcpu_ioctl_run
		update_vttbr
			need_new_vmid_gen == true
			lock(kvm_vmid_lock)
			kvm_vmid_gen++;
			kvm_next_vmid = 1;
			on_each_cpu(reset_vm_context);

At this point, the other two (physical) CPUs exit the guest:

	kvm_guest_exit					// Received IRQ from cross-call
	local_irq_enable
		kvm_call_hyp(__kvm_flush_vm_context);	// Invalidate TLB (this is overkill as should be bcast)
	cond_resched;
	update_vttbr
		need_new_vmid_gen == true
		/* spin on kvm_vmid_lock */

I think the __kvm_flush_vm_context is overkill -- you should check tlb_ops_need_broadcast (which is currently only true for 11MPCore). However, continuing with this, the other CPU gets its vmid and releases the lock:

			/* receives vmid 1, kvm_next_vmid = 2 */
			unlock(kvm_vmid_lock)
			/* Back to the guest */

Now, let's say that CPU0 takes an interrupt (which it goes off to handle) and CPU1 grabs the lock:

		lock(kvm_vmid_lock)
		/* CPU1 receives vmid 2, bumps vmid counter */
		unlock(kvm_vmid_lock)
		/* Back to the guest */

At this point, VM1 is running and VM0:VCPU1 is running. VM0:VCPU0 is not running because physical CPU0 is handling an interrupt. The problem is that when VCPU0 *is* resumed, it will update the VMID of VM0 and could be scheduled in parallel with VCPU1 but with a different VMID.

How do you avoid this in the current code?

Will

#38 lore raw

Re: [PATCH v4 08/14] KVM: ARM: World-switch implementation

Quoted 90 lines · Will Deacon
Quoted 9 lines · Christoffer Dall
Quoted 5 lines · Will Deacon

I must be missing something here: how do you ensure that a guest running on multiple CPUs continues to have the same VMID across them after a rollover?

when a roll over occurs, there's no problem until someone comes along that doesn't have a valid vmid (need_new_vmid_gen will return true).

Well a roll-over is triggered by somebody not having a valid VMID and us failing to allocate a new one from the current generation.

Quoted 10 lines

In this case, to assign a vmid, we need to start a new generation of id's to assign one, and must ensure that all old vmid's are no longer used. So how do we ensure that?

Well, we increment the kvm_vmid_gen, causing all other cpus who try to run a VM to hit the spin_lock if they exit the VMs. We reserve the vmid 1 for the new cpu, and we call on_each_cpu, which causes an ipi to all other physical cpus, and waits until the other physical cpus actually complete reset_vm_context.

Quoted 7 lines

At this point, once on_each_cpu(reset_vm_context) returns, all other physical CPUs have cleared their data structures for occurences of old vmids, and the kvm_vmid_gen has been incremented, so no other vcpus can come and claim other vmids until we unlock the spinlock, and everything starts over.

Makes sense?

Yes, but I still don't understand how you ensure VMID consistency across different vcpus of the same vm. Imagine the following scenario:

We have two VMs:

VM0: VCPU0 on physical CPU0, VCPU1 on physical CPU1
VM1: VCPU0 on physical CPU2

Also assume that VM0 is happily running and we want to schedule VM1 for the first time. Finally, also assume that kvm_next_vmid is zero (that is, the next allocation will trigger a roll-over).

Now, we want to schedule VM1:

        kvm_arch_init_vm
                kvm->arch.vmid_gen = 0;
        kvm_arch_vcpu_ioctl_run
                update_vttbr
                        need_new_vmid_gen == true
                        lock(kvm_vmid_lock)
                        kvm_vmid_gen++;
                        kvm_next_vmid = 1;
                        on_each_cpu(reset_vm_context);

At this point, the other two (physical) CPUs exit the guest:

        kvm_guest_exit                                  // Received IRQ from cross-call
        local_irq_enable
                kvm_call_hyp(__kvm_flush_vm_context);   // Invalidate TLB (this is overkill as should be bcast)
        cond_resched;
        update_vttbr
                need_new_vmid_gen == true
                /* spin on kvm_vmid_lock */

I think the __kvm_flush_vm_context is overkill -- you should check tlb_ops_need_broadcast (which is currently only true for 11MPCore). However, continuing with this, the other CPU gets its vmid and releases the lock:

                        /* receives vmid 1, kvm_next_vmid = 2 */
                        unlock(kvm_vmid_lock)
                        /* Back to the guest */

Now, let's say that CPU0 takes an interrupt (which it goes off to handle) and CPU1 grabs the lock:

                lock(kvm_vmid_lock)
                /* CPU1 receives vmid 2, bumps vmid counter */
                unlock(kvm_vmid_lock)
                /* Back to the guest */
Quoted 7 lines

At this point, VM1 is running and VM0:VCPU1 is running. VM0:VCPU0 is not running because physical CPU0 is handling an interrupt. The problem is that when VCPU0 *is* resumed, it will update the VMID of VM0 and could be scheduled in parallel with VCPU1 but with a different VMID.

How do you avoid this in the current code?

I don't. Nice catch. Please apply your interesting brain to the following fix:)

Quoted 1 line

From 5ba0481b78b5b5e9321cb2bb05d611b74149461c Mon Sep 17 00:00:00 2001

From: Christoffer Dall <[email protected]>
Date: Fri, 30 Nov 2012 11:43:24 -0500
Subject: [PATCH] KVM: ARM: Fix race condition in update_vttbr

Will Deacon pointed out another funny race in the update_vttbr code, where two VCPUs belonging to the same VM could end up using different VMIDs during a roll over of the VMID space.

To preserve the lockless vcpu_run loop in the common case, we re-check the vmid generation after grabbing the spin lock.

---
 arch/arm/kvm/arm.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
arch/arm/kvm/arm.c +10 -0
index c4f631e..dbdee30 100644
@@ -445,6 +445,16 @@ static void update_vttbr(struct kvm *kvm)
 
 	spin_lock(&kvm_vmid_lock);
 
+	/*
+	 * We need to re-check the vmid_gen here to ensure that if another vcpu
+	 * already allocated a valid vmid for this vm, then this vcpu should
+	 * use the same vmid.
+	 */
+	if (!need_new_vmid_gen(kvm)) {
+		spin_unlock(&kvm_vmid_lock);
+		return;
+	}
+
 	/* First user of a new VMID generation? */
 	if (unlikely(kvm_next_vmid == 0)) {
 		atomic64_inc(&kvm_vmid_gen);
Signature · 5 lines
1.7.9.5


Thanks!
-Christoffer
#39 lore raw

Re: [PATCH v4 08/14] KVM: ARM: World-switch implementation

Quoted 9 lines · Christoffer Dall
Quoted 7 lines · Will Deacon

At this point, VM1 is running and VM0:VCPU1 is running. VM0:VCPU0 is not running because physical CPU0 is handling an interrupt. The problem is that when VCPU0 *is* resumed, it will update the VMID of VM0 and could be scheduled in parallel with VCPU1 but with a different VMID.

How do you avoid this in the current code?

I don't. Nice catch. Please apply your interesting brain to the following fix:)

I'm far too sober to look at your patch right now, but I'll think about it over the weekend [I can't break it at a quick glance] :)

In the meantime, can you think about whether the TLB operations need to run on every CPU please?

Will

#40 lore raw

Re: [PATCH v4 08/14] KVM: ARM: World-switch implementation

Quoted 9 lines · Will Deacon
Quoted 8 lines · Christoffer Dall
Quoted 7 lines · Will Deacon

At this point, VM1 is running and VM0:VCPU1 is running. VM0:VCPU0 is not running because physical CPU0 is handling an interrupt. The problem is that when VCPU0 *is* resumed, it will update the VMID of VM0 and could be scheduled in parallel with VCPU1 but with a different VMID.

How do you avoid this in the current code?

Quoted 8 lines
Quoted 1 line

I don't. Nice catch. Please apply your interesting brain to the following fix:)

I'm far too sober to look at your patch right now, but I'll think about it over the weekend [I can't break it at a quick glance] :)

In the meantime, can you think about whether the TLB operations need to run on every CPU please?

they don't we can invalidate the TLB and the icache using the inner shareability domain. Here's a patch:

arch/arm/include/asm/kvm_host.h +1 -0
index ad1390f..df1b753 100644
@@ -146,6 +146,7 @@ struct kvm_one_reg;
 int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
 int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
 u64 kvm_call_hyp(void *hypfn, ...);
+void force_vm_exit(const cpumask_t *mask);
 
 #define KVM_ARCH_WANT_MMU_NOTIFIER
 struct kvm;
arch/arm/kvm/arm.c +27 -6
index c4f631e..674592e 100644
@@ -405,9 +405,14 @@ int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v)
 	return v->mode == IN_GUEST_MODE;
 }
 
-static void reset_vm_context(void *info)
+/* Just ensure a guest exit from a particular CPU */
+static void exit_vm_noop(void *info)
 {
-	kvm_call_hyp(__kvm_flush_vm_context);
+}
+
+void force_vm_exit(const cpumask_t *mask)
+{
+	smp_call_function_many(mask, exit_vm_noop, NULL, true);
 }
 
 /**
@@ -445,17 +450,33 @@ static void update_vttbr(struct kvm *kvm)
 
 	spin_lock(&kvm_vmid_lock);
 
+	/*
+	 * We need to re-check the vmid_gen here to ensure that if another vcpu
+	 * already allocated a valid vmid for this vm, then this vcpu should
+	 * use the same vmid.
+	 */
+	if (!need_new_vmid_gen(kvm)) {
+		spin_unlock(&kvm_vmid_lock);
+		return;
+	}
+
 	/* First user of a new VMID generation? */
 	if (unlikely(kvm_next_vmid == 0)) {
 		atomic64_inc(&kvm_vmid_gen);
 		kvm_next_vmid = 1;
 
 		/*
-		 * On SMP we know no other CPUs can use this CPU's or
-		 * each other's VMID since the kvm_vmid_lock blocks
-		 * them from reentry to the guest.
+		 * On SMP we know no other CPUs can use this CPU's or each
+		 * other's VMID after force_vm_exit returns since the
+		 * kvm_vmid_lock blocks them from reentry to the guest.
+		 */
+		force_vm_exit(cpu_all_mask);
+		/*
+		 * Now broadcast TLB + ICACHE invalidation over the inner
+		 * shareable domain to make sure all data structures are
+		 * clean.
 		 */
-		on_each_cpu(reset_vm_context, NULL, 1);
+		kvm_call_hyp(__kvm_flush_vm_context);
 	}
 
 	kvm->arch.vmid_gen = atomic64_read(&kvm_vmid_gen);
arch/arm/kvm/interrupts.S +7 -3
index 8a1f2df..91bb9c5 100644
@@ -63,14 +63,18 @@ ENTRY(__kvm_tlb_flush_vmid)
 ENDPROC(__kvm_tlb_flush_vmid)
 
 /********************************************************************
- * Flush TLBs and instruction caches of current CPU for all VMIDs
+ * Flush TLBs and instruction caches of all CPUs inside the inner-shareable
+ * domain, for all VMIDs
  *
  * void __kvm_flush_vm_context(void);
  */
 ENTRY(__kvm_flush_vm_context)
 	mov	r0, #0			@ rn parameter for c15 flushes is SBZ
-	mcr     p15, 4, r0, c8, c7, 4   @ Invalidate Non-secure Non-Hyp TLB
-	mcr     p15, 0, r0, c7, c5, 0   @ Invalidate instruction caches
+
+	/* Invalidate NS Non-Hyp TLB Inner Shareable (TLBIALLNSNHIS) */
+	mcr     p15, 4, r0, c8, c3, 4
+	/* Invalidate instruction caches Inner Shareable (ICIALLUIS) */
+	mcr     p15, 0, r0, c7, c1, 0
 	dsb
 	isb				@ Not necessary if followed by eret
 
Signature · 4 lines
1.7.9.5


-Christoffer
#41 lore raw

Re: [PATCH v4 08/14] KVM: ARM: World-switch implementation

Quoted 42 lines · Christoffer Dall
Quoted 9 lines · Will Deacon
Quoted 8 lines · Christoffer Dall
Quoted 7 lines · Will Deacon

At this point, VM1 is running and VM0:VCPU1 is running. VM0:VCPU0 is not running because physical CPU0 is handling an interrupt. The problem is that when VCPU0 *is* resumed, it will update the VMID of VM0 and could be scheduled in parallel with VCPU1 but with a different VMID.

How do you avoid this in the current code?

Quoted 8 lines
Quoted 1 line

I don't. Nice catch. Please apply your interesting brain to the following fix:)

I'm far too sober to look at your patch right now, but I'll think about it over the weekend [I can't break it at a quick glance] :)

In the meantime, can you think about whether the TLB operations need to run on every CPU please?

they don't we can invalidate the TLB and the icache using the inner shareability domain. Here's a patch:

arch/arm/include/asm/kvm_host.h +1 -0
index ad1390f..df1b753 100644
@@ -146,6 +146,7 @@ struct kvm_one_reg;
 int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
 int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
 u64 kvm_call_hyp(void *hypfn, ...);
+void force_vm_exit(const cpumask_t *mask);
 
 #define KVM_ARCH_WANT_MMU_NOTIFIER
 struct kvm;
arch/arm/kvm/arm.c +0 -1
index c4f631e..674592e 100644
@@ -405,9 +405,14 @@ int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v)
 	return v->mode == IN_GUEST_MODE;
 }
 
-static void reset_vm_context(void *info)

arch/arm/kvm/arm.c int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v) · line 416

Quoted 10 lines
arch/arm/kvm/arm.c +7 -1
+/* Just ensure a guest exit from a particular CPU */
+static void exit_vm_noop(void *info)
 {
-	kvm_call_hyp(__kvm_flush_vm_context);
+}
+
+void force_vm_exit(const cpumask_t *mask)
+{
+	smp_call_function_many(mask, exit_vm_noop, NULL, true);
 }

Care to update the do_nothing() call in emulate.c to use this as well?

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#42 lore raw

Re: [PATCH v4 08/14] KVM: ARM: World-switch implementation

Quoted 42 lines · Marc Zyngier
Quoted 41 lines · Christoffer Dall
Quoted 9 lines · Will Deacon
Quoted 8 lines · Christoffer Dall
Quoted 7 lines · Will Deacon

At this point, VM1 is running and VM0:VCPU1 is running. VM0:VCPU0 is not running because physical CPU0 is handling an interrupt. The problem is that when VCPU0 *is* resumed, it will update the VMID of VM0 and could be scheduled in parallel with VCPU1 but with a different VMID.

How do you avoid this in the current code?

Quoted 8 lines
Quoted 1 line

I don't. Nice catch. Please apply your interesting brain to the following fix:)

I'm far too sober to look at your patch right now, but I'll think about it over the weekend [I can't break it at a quick glance] :)

In the meantime, can you think about whether the TLB operations need to run on every CPU please?

they don't we can invalidate the TLB and the icache using the inner shareability domain. Here's a patch:

arch/arm/include/asm/kvm_host.h +1 -0
index ad1390f..df1b753 100644
@@ -146,6 +146,7 @@ struct kvm_one_reg;
 int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
 int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
 u64 kvm_call_hyp(void *hypfn, ...);
+void force_vm_exit(const cpumask_t *mask);
 
 #define KVM_ARCH_WANT_MMU_NOTIFIER
 struct kvm;
arch/arm/kvm/arm.c +0 -0
index c4f631e..674592e 100644
@@ -405,9 +405,14 @@ int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v)
      return v->mode == IN_GUEST_MODE;
 }
 
Quoted 14 lines

arch/arm/kvm/arm.c int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v) · line 416

Quoted 11 lines
arch/arm/kvm/arm.c +7 -2
-static void reset_vm_context(void *info)
+/* Just ensure a guest exit from a particular CPU */
+static void exit_vm_noop(void *info)
 {
-     kvm_call_hyp(__kvm_flush_vm_context);
+}
+
+void force_vm_exit(const cpumask_t *mask)
+{
+     smp_call_function_many(mask, exit_vm_noop, NULL, true);
 }

Care to update the do_nothing() call in emulate.c to use this as well?

yes, was actually already done:

commit d1ff14417724e9062ec9d585bf3ef0537e12b54d

Author: Christoffer Dall <[email protected]>
Date:   Fri Nov 30 13:37:01 2012 -0500

KVM: ARM: Reuse force_vm_exit from copy_current_insn

The VMID management code also needs to force VM exits, so reuse that functionality from both places.

arch/arm/kvm/emulate.c +0 -5
index 60e7ec0..ad743b7 100644
@@ -221,11 +221,6 @@ static bool copy_from_guest_va(struct kvm_vcpu *vcpu,
 	return true;
 }
 
-/* Just ensure we're not running the guest. */
-static void do_nothing(void *info)
-{
-}
-
 /*
  * We have to be very careful copying memory from a running (ie. SMP) guest.
  * Another CPU may remap the page (eg. swap out a userspace text page) as we
@@ -257,8 +252,9 @@ static bool copy_current_insn(struct kvm_vcpu

*vcpu, unsigned long *instr)
/* Kick out any which are still running. */ kvm_for_each_vcpu(i, v, vcpu->kvm) {
/* Guest could exit now, making cpu wrong. That's OK. */
- if (kvm_vcpu_exiting_guest_mode(v) == IN_GUEST_MODE)
- smp_call_function_single(v->cpu, do_nothing, NULL, 1);
+ if (kvm_vcpu_exiting_guest_mode(v) == IN_GUEST_MODE) {
+ force_vm_exit(get_cpu_mask(v->cpu));
+ }
}

--

#43 lore raw

PATCH v4 9/14 KVM: ARM: Emulation framework and CP15 emulation

In reply to Christoffer Dall

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

Some of the reasons for an exit are CP15 accesses, which are not allowed from the guest and this commit handles these exits by emulating the intended operation in software and skipping 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  |   14 +
 arch/arm/include/asm/kvm_emulate.h |    6 +
 arch/arm/include/asm/kvm_host.h    |    4 
 arch/arm/kvm/Makefile              |    3 
 arch/arm/kvm/arm.c                 |  175 +++++++++++++++++
 arch/arm/kvm/coproc.c              |  363 ++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/coproc.h              |  153 +++++++++++++++
 arch/arm/kvm/coproc_a15.c          |  164 ++++++++++++++++
 arch/arm/kvm/emulate.c             |  218 ++++++++++++++++++++++
 arch/arm/kvm/trace.h               |   45 ++++
 11 files changed, 1149 insertions(+), 5 deletions(-)
 create mode 100644 arch/arm/kvm/coproc.h
 create mode 100644 arch/arm/kvm/coproc_a15.c
arch/arm/include/asm/kvm_arm.h +9 -0
index 2254bd8..e2cf681 100644
@@ -70,6 +70,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)
@@ -151,6 +156,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 +14 -0
index b6d023d..bd1ace0 100644
@@ -21,4 +21,18 @@
 
 void kvm_reset_coprocs(struct kvm_vcpu *vcpu);
 
+struct kvm_coproc_target_table {
+	unsigned target;
+	const struct coproc_reg *table;
+	size_t num;
+};
+void kvm_register_target_coproc_table(struct kvm_coproc_target_table *table);
+
+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 +6 -0
index 17dad67..01a755b 100644
@@ -25,6 +25,12 @@
 u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num);
 u32 *vcpu_spsr(struct kvm_vcpu *vcpu);
 
+int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run);
+void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr);
+void kvm_inject_undefined(struct kvm_vcpu *vcpu);
+void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr);
+void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr);
+
 static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu)
 {
 	return (u32 *)&vcpu->arch.regs.usr_regs.ARM_pc;
arch/arm/include/asm/kvm_host.h +4 -0
index 20e0254..feeb1ac 100644
@@ -94,6 +94,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;
+
 	/* Interrupt related fields */
 	u32 irq_lines;		/* IRQ and FIQ levels */
 
arch/arm/kvm/Makefile +2 -1
index 7acf3ea..ea5b282 100644
@@ -18,4 +18,5 @@ obj-$(CONFIG_KVM_ARM_HOST) += init.o interrupts.o
 
 obj-$(CONFIG_KVM_ARM_HOST) += $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
 
-obj-$(CONFIG_KVM_ARM_HOST) += arm.o guest.o mmu.o emulate.o reset.o coproc.o
+obj-$(CONFIG_KVM_ARM_HOST) += arm.o guest.o mmu.o emulate.o reset.o
+obj-$(CONFIG_KVM_ARM_HOST) += coproc.o coproc_a15.o
arch/arm/kvm/arm.c +172 -3

224 lines not on this page · raw message

arch/arm/kvm/coproc.c +362 -1

376 lines not on this page · raw message

arch/arm/kvm/coproc.h new file +153 -0

153 lines not on this page · raw message

arch/arm/kvm/coproc_a15.c new file +164 -0

164 lines not on this page · raw message

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

228 lines not on this page · raw message

arch/arm/kvm/trace.h +45 -0

51 lines not on this page · raw message

#44 lore raw

Re: [PATCH v4 09/14] KVM: ARM: Emulation framework and CP15 emulation

Quoted 21 lines · Christoffer Dall

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

Some of the reasons for an exit are CP15 accesses, which are not allowed from the guest and this commit handles these exits by emulating the intended operation in software and skipping 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.

[...]

Quoted 23 lines
arch/arm/kvm/coproc_a15.c new file +17 -0
new file mode 100644
index 0000000..55cb4a3
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Authors: Rusty Russell <[email protected]>
+ *          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/coproc_a15.c line 27

Quoted 10 lines
arch/arm/kvm/coproc_a15.c new file +10 -0
+ */
+#include <linux/kvm_host.h>
+#include <asm/cputype.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_host.h>
+#include <asm/kvm_emulate.h>
+#include <asm/kvm_coproc.h>
+#include <linux/init.h>
+
+#define MPIDR_CPUID    0x3

This should probably be 0xff and also use the macros that Lorenzo is introducing:

http://lists.infradead.org/pipermail/linux-arm-kernel/2012-November/132977.html

Quoted 91 lines
arch/arm/kvm/coproc_a15.c new file +91 -0
+
+static void reset_mpidr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
+{
+       /*
+        * Compute guest MPIDR:
+        * (Even if we present only one VCPU to the guest on an SMP
+        * host we don't set the U bit in the MPIDR, or vice versa, as
+        * revealing the underlying hardware properties is likely to
+        * be the best choice).
+        */
+       vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & ~MPIDR_CPUID)
+               | (vcpu->vcpu_id & MPIDR_CPUID);
+}
+
+#include "coproc.h"
+
+/* A15 TRM 4.3.28: RO WI */
+static bool access_actlr(struct kvm_vcpu *vcpu,
+                        const struct coproc_params *p,
+                        const struct coproc_reg *r)
+{
+       if (p->is_write)
+               return ignore_write(vcpu, p);
+
+       *vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c1_ACTLR];
+       return true;
+}
+
+/* A15 TRM 4.3.60: R/O. */
+static bool access_cbar(struct kvm_vcpu *vcpu,
+                       const struct coproc_params *p,
+                       const struct coproc_reg *r)
+{
+       if (p->is_write)
+               return write_to_read_only(vcpu, p);
+       return read_zero(vcpu, p);
+}
+
+/* A15 TRM 4.3.48: R/O WI. */
+static bool access_l2ctlr(struct kvm_vcpu *vcpu,
+                         const struct coproc_params *p,
+                         const struct coproc_reg *r)
+{
+       if (p->is_write)
+               return ignore_write(vcpu, p);
+
+       *vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c9_L2CTLR];
+       return true;
+}
+
+static void reset_l2ctlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
+{
+       u32 l2ctlr, ncores;
+
+       asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr));
+       l2ctlr &= ~(3 << 24);
+       ncores = atomic_read(&vcpu->kvm->online_vcpus) - 1;
+       l2ctlr |= (ncores & 3) << 24;
+
+       vcpu->arch.cp15[c9_L2CTLR] = l2ctlr;
+}
+
+static void reset_actlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
+{
+       u32 actlr;
+
+       /* ACTLR contains SMP bit: make sure you create all cpus first! */
+       asm volatile("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr));
+       /* Make the SMP bit consistent with the guest configuration */
+       if (atomic_read(&vcpu->kvm->online_vcpus) > 1)
+               actlr |= 1U << 6;
+       else
+               actlr &= ~(1U << 6);
+
+       vcpu->arch.cp15[c1_ACTLR] = actlr;
+}
+
+/* A15 TRM 4.3.49: R/O WI (even if NSACR.NS_L2ERR, a write of 1 is ignored). */
+static bool access_l2ectlr(struct kvm_vcpu *vcpu,
+                          const struct coproc_params *p,
+                          const struct coproc_reg *r)
+{
+       if (p->is_write)
+               return ignore_write(vcpu, p);
+
+       *vcpu_reg(vcpu, p->Rt1) = 0;
+       return true;
+}
+
+/*
+ * A15-specific CP15 registers.

arch/arm/kvm/coproc_a15.c line 128

Quoted 10 lines
arch/arm/kvm/coproc_a15.c new file +10 -0
+ * Important: Must be sorted ascending by CRn, CRM, Op1, Op2
+ */
+static const struct coproc_reg a15_regs[] = {
+       /* MPIDR: we use VMPIDR for guest access. */
+       { CRn( 0), CRm( 0), Op1( 0), Op2( 5), is32,
+                       NULL, reset_mpidr, c0_MPIDR },
+
+       /* SCTLR: swapped by interrupt.S. */
+       { CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
+                       NULL, reset_val, c1_SCTLR, 0x00C50078 },

Why is the SCTLR included here as an A15-specific register?

Will

#45 lore raw

Re: [kvmarm] [PATCH v4 09/14] KVM: ARM: Emulation framework and CP15 emulation

Quoted 15 lines · Will Deacon

arch/arm/kvm/coproc_a15.c line 128

Quoted 12 lines · Christoffer Dall
arch/arm/kvm/coproc_a15.c new file +12 -0
+/*
+ * A15-specific CP15 registers.
+ * Important: Must be sorted ascending by CRn, CRM, Op1, Op2
+ */
+static const struct coproc_reg a15_regs[] = {
+       /* MPIDR: we use VMPIDR for guest access. */
+       { CRn( 0), CRm( 0), Op1( 0), Op2( 5), is32,
+                       NULL, reset_mpidr, c0_MPIDR },
+
+       /* SCTLR: swapped by interrupt.S. */
+       { CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
+                       NULL, reset_val, c1_SCTLR, 0x00C50078 },

Why is the SCTLR included here as an A15-specific register?

Rusty might remember the exact answer, but probably because the SCTLR reset value is IMPDEF.

-- PMM

#46 lore raw

Re: [kvmarm] [PATCH v4 09/14] KVM: ARM: Emulation framework and CP15 emulation

Quoted 17 lines · Peter Maydell
Quoted 15 lines · Will Deacon

arch/arm/kvm/coproc_a15.c line 128

Quoted 12 lines · Christoffer Dall
arch/arm/kvm/coproc_a15.c new file +12 -0
+/*
+ * A15-specific CP15 registers.
+ * Important: Must be sorted ascending by CRn, CRM, Op1, Op2
+ */
+static const struct coproc_reg a15_regs[] = {
+       /* MPIDR: we use VMPIDR for guest access. */
+       { CRn( 0), CRm( 0), Op1( 0), Op2( 5), is32,
+                       NULL, reset_mpidr, c0_MPIDR },
+
+       /* SCTLR: swapped by interrupt.S. */
+       { CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
+                       NULL, reset_val, c1_SCTLR, 0x00C50078 },

Why is the SCTLR included here as an A15-specific register?

Quoted 2 lines

Rusty might remember the exact answer, but probably because the SCTLR reset value is IMPDEF.

Indeed...

Cheers,
Rusty.

#47 lore raw

Re: [PATCH v4 09/14] KVM: ARM: Emulation framework and CP15 emulation

In reply to Will Deacon
Quoted 62 lines · Will Deacon
Quoted 21 lines · Christoffer Dall

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

Some of the reasons for an exit are CP15 accesses, which are not allowed from the guest and this commit handles these exits by emulating the intended operation in software and skipping 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.

[...]

Quoted 23 lines
arch/arm/kvm/coproc_a15.c new file +17 -0
new file mode 100644
index 0000000..55cb4a3
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Authors: Rusty Russell <[email protected]>
+ *          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/coproc_a15.c line 27

Quoted 10 lines
arch/arm/kvm/coproc_a15.c new file +10 -0
+ */
+#include <linux/kvm_host.h>
+#include <asm/cputype.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_host.h>
+#include <asm/kvm_emulate.h>
+#include <asm/kvm_coproc.h>
+#include <linux/init.h>
+
+#define MPIDR_CPUID    0x3

This should probably be 0xff and also use the macros that Lorenzo is introducing:

Quoted 2 lines

http://lists.infradead.org/pipermail/linux-arm-kernel/2012-November/132977.html

in the A15 TRM bits [7:2] are reserved, so we really only do care about bits [1:0], and this file is A15 specific, but if you prefer, I can merge this:

arch/arm/kvm/coproc_a15.c +0 -2
index 55cb4a3..685063a 100644
@@ -24,8 +24,6 @@
 #include <asm/kvm_coproc.h>
 #include <linux/init.h>
 
-#define MPIDR_CPUID    0x3
-
 static void reset_mpidr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
 {
 	/*
@@ -35,8 +33,8 @@ static void reset_mpidr(struct kvm_vcpu *vcpu, const

struct coproc_reg *r)
* revealing the underlying hardware properties is likely to
* be the best choice).
*/
- vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & ~MPIDR_CPUID)
- | (vcpu->vcpu_id & MPIDR_CPUID);
+ vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & ~MPIDR_LEVEL_MASK)
+ | (vcpu->vcpu_id & MPIDR_LEVEL_MASK);
}

#include "coproc.h"
--

-Christoffer

#48 lore raw

Re: [PATCH v4 09/14] KVM: ARM: Emulation framework and CP15 emulation

Quoted 11 lines · Christoffer Dall
Quoted 6 lines · Will Deacon

On Sat, Nov 10, 2012 at 03:43:13PM +0000, Christoffer Dall wrote:
This should probably be 0xff and also use the macros that Lorenzo is introducing:

http://lists.infradead.org/pipermail/linux-arm-kernel/2012-November/132977.html

in the A15 TRM bits [7:2] are reserved, so we really only do care about bits [1:0], and this file is A15 specific, but if you prefer, I can merge this:

The upper bits are RAZ, so yes, please do merge something like this.

Quoted 24 lines
arch/arm/kvm/coproc_a15.c +2 -4
index 55cb4a3..685063a 100644
@@ -24,8 +24,6 @@
 #include <asm/kvm_coproc.h>
 #include <linux/init.h>
 
-#define MPIDR_CPUID    0x3
-
 static void reset_mpidr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
 {
 	/*
@@ -35,8 +33,8 @@ static void reset_mpidr(struct kvm_vcpu *vcpu, const
 struct coproc_reg *r)
 	 * revealing the underlying hardware properties is likely to
 	 * be the best choice).
 	 */
-	vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & ~MPIDR_CPUID)
-		| (vcpu->vcpu_id & MPIDR_CPUID);
+	vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & ~MPIDR_LEVEL_MASK)
+		| (vcpu->vcpu_id & MPIDR_LEVEL_MASK);
 }
 
Quoted 1 line

#include "coproc.h"

Will

#49 lore raw

Re: [PATCH v4 09/14] KVM: ARM: Emulation framework and CP15 emulation

Quoted 6 lines · Will Deacon
Quoted 5 lines · Christoffer Dall
Quoted 4 lines · Will Deacon

On Sat, Nov 10, 2012 at 03:43:13PM +0000, Christoffer Dall wrote:
This should probably be 0xff and also use the macros that Lorenzo is introducing:

Quoted 9 lines
Quoted 6 lines
Quoted 2 lines

http://lists.infradead.org/pipermail/linux-arm-kernel/2012-November/132977.html

in the A15 TRM bits [7:2] are reserved, so we really only do care about bits [1:0], and this file is A15 specific, but if you prefer, I can merge this:

The upper bits are RAZ, so yes, please do merge something like this.

I did, thanks!

#50 lore raw

PATCH v4 10/14 KVM: ARM: User space API for getting/setting co-proc registers

In reply to Christoffer Dall

The following three ioctls are implemented:

 -  KVM_GET_REG_LIST
 -  KVM_GET_ONE_REG
 -  KVM_SET_ONE_REG

Now we have a table for all the cp15 registers, we can drive a generic API.

The register IDs carry the following encoding:

ARM registers are mapped using the lower 32 bits. The upper 16 of that is the register group type, or coprocessor number:

ARM 32-bit CP15 registers have the following id bit patterns:
0x4002 0000 000F <zero:1> <crn:4> <crm:4> <opc1:4> <opc2:3>

ARM 64-bit CP15 registers have the following id bit patterns:
0x4003 0000 000F <zero:1> <zero:4> <crm:4> <opc1:4> <zero:3>

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 |   44 +++++
 arch/arm/include/asm/kvm_coproc.h |    9 +
 arch/arm/include/asm/kvm_host.h   |    4 
 arch/arm/kvm/coproc.c             |  327 +++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/guest.c              |    9 +
 5 files changed, 389 insertions(+), 4 deletions(-)
Documentation/virtual/kvm/api.txt +44 -0
index 845ceda..9671cd2 100644
@@ -1798,6 +1798,11 @@ is the register group type, or coprocessor number:
 ARM core registers have the following id bit patterns:
   0x4002 0000 0010 <index into the kvm_regs struct:16>
 
+ARM 32-bit CP15 registers have the following id bit patterns:
+  0x4002 0000 000F <zero:1> <crn:4> <crm:4> <opc1:4> <opc2:3>
+
+ARM 64-bit CP15 registers have the following id bit patterns:
+  0x4003 0000 000F <zero:1> <zero:4> <crm:4> <opc1:4> <zero:3>
 
 
 4.69 KVM_GET_ONE_REG
@@ -2139,6 +2144,45 @@ This ioctl returns the guest registers that are supported for the
 KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
 
 
+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.
+
+4.78 KVM_GET_REG_LIST
+
+Capability: basic
+Architectures: arm
+Type: vcpu ioctl
+Parameters: struct kvm_reg_list (in/out)
+Returns: 0 on success; -1 on error
+Errors:
+ ?E2BIG: ????the reg index list is too big to fit in the array specified by
+ ????????????the user (the number required will be written into n).
+
+struct kvm_reg_list {
+	__u64 n; /* number of registers in reg[] */
+	__u64 reg[0];
+};
+
+This ioctl returns the guest registers that are supported for the
+KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
+
+
 5. The kvm_run structure
 ------------------------
 
arch/arm/include/asm/kvm_coproc.h +9 -0
index bd1ace0..4917c2f 100644
@@ -34,5 +34,14 @@ 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);
+
+unsigned long kvm_arm_num_guest_msrs(struct kvm_vcpu *vcpu);
+int kvm_arm_copy_msrindices(struct kvm_vcpu *vcpu, u64 __user *uindices);
 void kvm_coproc_table_init(void);
+
+struct kvm_one_reg;
+int kvm_arm_copy_coproc_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
+int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *);
+int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *);
+unsigned long kvm_arm_num_coproc_regs(struct kvm_vcpu *vcpu);
 #endif /* __ARM_KVM_COPROC_H__ */
arch/arm/include/asm/kvm_host.h +4 -0
index feeb1ac..1eab4c5 100644
@@ -27,6 +27,7 @@
 #define KVM_MEMORY_SLOTS 32
 #define KVM_PRIVATE_MEM_SLOTS 4
 #define KVM_COALESCED_MMIO_PAGE_OFFSET 1
+#define KVM_HAVE_ONE_REG
 
 #define KVM_VCPU_MAX_FEATURES 0
 
@@ -133,6 +134,9 @@ 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);
 
+unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu);
+int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices);
+
 /* We do not have shadow page tables, hence the empty hooks */
 static inline int kvm_age_hva(struct kvm *kvm, unsigned long hva)
 {
arch/arm/kvm/coproc.c +327 -0

345 lines not on this page · raw message

arch/arm/kvm/guest.c +5 -4
index 8db3811..65ae563 100644
@@ -26,6 +26,7 @@
 #include <asm/kvm.h>
 #include <asm/kvm_asm.h>
 #include <asm/kvm_emulate.h>
+#include <asm/kvm_coproc.h>
 
 #define VM_STAT(x) { #x, offsetof(struct kvm, stat.x), KVM_STAT_VM }
 #define VCPU_STAT(x) { #x, offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU }
@@ -119,7 +120,7 @@ static unsigned long num_core_regs(void)
  */
 unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu)
 {
-	return num_core_regs();
+	return num_core_regs() + kvm_arm_num_coproc_regs(vcpu);
 }
 
 /**
@@ -138,7 +139,7 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 		uindices++;
 	}
 
-	return 0;
+	return kvm_arm_copy_coproc_indices(vcpu, uindices);
 }
 
 int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
@@ -151,7 +152,7 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
 		return get_core_reg(vcpu, reg);
 
-	return -EINVAL;
+	return kvm_arm_coproc_get_reg(vcpu, reg);
 }
 
 int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
@@ -164,7 +165,7 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
 		return set_core_reg(vcpu, reg);
 
-	return -EINVAL;
+	return kvm_arm_coproc_set_reg(vcpu, reg);
 }
 
 int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
#51 lore raw

Re: [PATCH v4 10/14] KVM: ARM: User space API for getting/setting co-proc registers

Quoted 4 lines · Christoffer Dall

The following three ioctls are implemented:

 -  KVM_GET_REG_LIST
 -  KVM_GET_ONE_REG
 -  KVM_SET_ONE_REG

[...]

Quoted 49 lines
Documentation/virtual/kvm/api.txt +34 -0
index 845ceda..9671cd2 100644
@@ -1798,6 +1798,11 @@ is the register group type, or coprocessor number:
 ARM core registers have the following id bit patterns:
   0x4002 0000 0010 <index into the kvm_regs struct:16>
 
+ARM 32-bit CP15 registers have the following id bit patterns:
+  0x4002 0000 000F <zero:1> <crn:4> <crm:4> <opc1:4> <opc2:3>
+
+ARM 64-bit CP15 registers have the following id bit patterns:
+  0x4003 0000 000F <zero:1> <zero:4> <crm:4> <opc1:4> <zero:3>
 
 
 4.69 KVM_GET_ONE_REG
@@ -2139,6 +2144,45 @@ This ioctl returns the guest registers that are supported for the
 KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
 
 
+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.
+
+4.78 KVM_GET_REG_LIST
+
+Capability: basic
+Architectures: arm
+Type: vcpu ioctl
+Parameters: struct kvm_reg_list (in/out)
+Returns: 0 on success; -1 on error
+Errors:
+  E2BIG:     the reg index list is too big to fit in the array specified by
+             the user (the number required will be written into n).

Documentation/virtual/kvm/api.txt This ioctl returns the guest registers that are supported for the · line 2185

Quoted 10 lines
Documentation/virtual/kvm/api.txt +10 -0
+
+struct kvm_reg_list {
+       __u64 n; /* number of registers in reg[] */
+       __u64 reg[0];
+};
+
+This ioctl returns the guest registers that are supported for the
+KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
+
+

You already added this hunk earlier (and looking at the final result, you do end up wih two entries for 4.77 and 4.78).

Will

#52 lore raw

Re: [PATCH v4 10/14] KVM: ARM: User space API for getting/setting co-proc registers

Quoted 62 lines · Will Deacon
Quoted 4 lines · Christoffer Dall

The following three ioctls are implemented:

 -  KVM_GET_REG_LIST
 -  KVM_GET_ONE_REG
 -  KVM_SET_ONE_REG

[...]

Quoted 54 lines
Documentation/virtual/kvm/api.txt +39 -0
index 845ceda..9671cd2 100644
@@ -1798,6 +1798,11 @@ is the register group type, or coprocessor number:
 ARM core registers have the following id bit patterns:
   0x4002 0000 0010 <index into the kvm_regs struct:16>
 
+ARM 32-bit CP15 registers have the following id bit patterns:
+  0x4002 0000 000F <zero:1> <crn:4> <crm:4> <opc1:4> <opc2:3>
+
+ARM 64-bit CP15 registers have the following id bit patterns:
+  0x4003 0000 000F <zero:1> <zero:4> <crm:4> <opc1:4> <zero:3>
 
 
 4.69 KVM_GET_ONE_REG
@@ -2139,6 +2144,45 @@ This ioctl returns the guest registers that are supported for the
 KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
 
 
+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.
+
+4.78 KVM_GET_REG_LIST
+
+Capability: basic
+Architectures: arm
+Type: vcpu ioctl
+Parameters: struct kvm_reg_list (in/out)
+Returns: 0 on success; -1 on error
+Errors:
+  E2BIG:     the reg index list is too big to fit in the array specified by
+             the user (the number required will be written into n).
+
+struct kvm_reg_list {
+       __u64 n; /* number of registers in reg[] */
+       __u64 reg[0];
+};
Quoted 9 lines

Documentation/virtual/kvm/api.txt This ioctl returns the guest registers that are supported for the · line 2185

Quoted 5 lines
Documentation/virtual/kvm/api.txt +5 -0
+
+This ioctl returns the guest registers that are supported for the
+KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
+
+

You already added this hunk earlier (and looking at the final result, you do end up wih two entries for 4.77 and 4.78).

duh, thanks.

#53 lore raw

PATCH v4 11/14 KVM: ARM: Demux CCSIDR in the userspace API

In reply to Christoffer Dall

The Cache Size Selection Register (CSSELR) selects the current Cache Size ID Register (CCSIDR). You write which cache you are interested in to CSSELR, and read the information out of CCSIDR.

Which cache numbers are valid is known by reading the Cache Level ID Register (CLIDR).

To export this state to userspace, we add a KVM_REG_ARM_DEMUX numberspace (17), which uses 8 bits to represent which register is being demultiplexed (0 for CCSIDR), and the lower 8 bits to represent this demultiplexing (in our case, the CSSELR value, which is 4 bits).

---
 Documentation/virtual/kvm/api.txt |    2 
 arch/arm/include/uapi/asm/kvm.h   |    9 ++
 arch/arm/kvm/coproc.c             |  163 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 171 insertions(+), 3 deletions(-)
Documentation/virtual/kvm/api.txt +2 -0
index 9671cd2..bf8f99c 100644
@@ -1804,6 +1804,8 @@ ARM 32-bit CP15 registers have the following id bit patterns:
 ARM 64-bit CP15 registers have the following id bit patterns:
   0x4003 0000 000F <zero:1> <zero:4> <crm:4> <opc1:4> <zero:3>
 
+ARM CCSIDR registers are demultiplexed by CSSELR value:
+  0x4002 0000 0011 00 <csselr:8>
 
 4.69 KVM_GET_ONE_REG
 
arch/arm/include/uapi/asm/kvm.h +9 -0
index a807d9a..78a629c 100644
@@ -80,6 +80,15 @@ struct kvm_arch_memory_slot {
 #define KVM_REG_ARM_CORE		(0x0010 << KVM_REG_ARM_COPROC_SHIFT)
 #define KVM_REG_ARM_CORE_REG(name)	(offsetof(struct kvm_regs, name) / 4)
 
+/* Some registers need more space to represent values. */
+#define KVM_REG_ARM_DEMUX		(0x0011 << KVM_REG_ARM_COPROC_SHIFT)
+#define KVM_REG_ARM_DEMUX_ID_MASK	0x000000000000FF00
+#define KVM_REG_ARM_DEMUX_ID_SHIFT	8
+#define KVM_REG_ARM_DEMUX_ID_CCSIDR	(0x00 << KVM_REG_ARM_DEMUX_ID_SHIFT)
+#define KVM_REG_ARM_DEMUX_VAL_MASK	0x00000000000000FF
+#define KVM_REG_ARM_DEMUX_VAL_SHIFT	0
+
+
 /* KVM_IRQ_LINE irq field index values */
 #define KVM_ARM_IRQ_TYPE_SHIFT		24
 #define KVM_ARM_IRQ_TYPE_MASK		0xff
arch/arm/kvm/coproc.c +160 -3

210 lines not on this page · raw message

#54 lore raw

Re: [PATCH v4 11/14] KVM: ARM: Demux CCSIDR in the userspace API

Quoted 14 lines · Christoffer Dall

The Cache Size Selection Register (CSSELR) selects the current Cache Size ID Register (CCSIDR). You write which cache you are interested in to CSSELR, and read the information out of CCSIDR.

Which cache numbers are valid is known by reading the Cache Level ID Register (CLIDR).

To export this state to userspace, we add a KVM_REG_ARM_DEMUX numberspace (17), which uses 8 bits to represent which register is being demultiplexed (0 for CCSIDR), and the lower 8 bits to represent this demultiplexing (in our case, the CSSELR value, which is 4 bits).

[...]

Quoted 84 lines
 Documentation/virtual/kvm/api.txt |    2 
 arch/arm/include/uapi/asm/kvm.h   |    9 ++
 arch/arm/kvm/coproc.c             |  163 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 171 insertions(+), 3 deletions(-)
Documentation/virtual/kvm/api.txt +2 -0
index 9671cd2..bf8f99c 100644
@@ -1804,6 +1804,8 @@ ARM 32-bit CP15 registers have the following id bit patterns:
 ARM 64-bit CP15 registers have the following id bit patterns:
   0x4003 0000 000F <zero:1> <zero:4> <crm:4> <opc1:4> <zero:3>
 
+ARM CCSIDR registers are demultiplexed by CSSELR value:
+  0x4002 0000 0011 00 <csselr:8>
 
 4.69 KVM_GET_ONE_REG
 
arch/arm/include/uapi/asm/kvm.h +9 -0
index a807d9a..78a629c 100644
@@ -80,6 +80,15 @@ struct kvm_arch_memory_slot {
 #define KVM_REG_ARM_CORE		(0x0010 << KVM_REG_ARM_COPROC_SHIFT)
 #define KVM_REG_ARM_CORE_REG(name)	(offsetof(struct kvm_regs, name) / 4)
 
+/* Some registers need more space to represent values. */
+#define KVM_REG_ARM_DEMUX		(0x0011 << KVM_REG_ARM_COPROC_SHIFT)
+#define KVM_REG_ARM_DEMUX_ID_MASK	0x000000000000FF00
+#define KVM_REG_ARM_DEMUX_ID_SHIFT	8
+#define KVM_REG_ARM_DEMUX_ID_CCSIDR	(0x00 << KVM_REG_ARM_DEMUX_ID_SHIFT)
+#define KVM_REG_ARM_DEMUX_VAL_MASK	0x00000000000000FF
+#define KVM_REG_ARM_DEMUX_VAL_SHIFT	0
+
+
 /* KVM_IRQ_LINE irq field index values */
 #define KVM_ARM_IRQ_TYPE_SHIFT		24
 #define KVM_ARM_IRQ_TYPE_MASK		0xff
arch/arm/kvm/coproc.c +31 -0
index 95a0f5e..9ce5861 100644
@@ -35,6 +35,12 @@
  * Co-processor emulation
  *****************************************************************************/
 
+/* 3 bits per cache level, as per CLIDR, but non-existent caches always 0 */
+static u32 cache_levels;
+
+/* CSSELR values; used to index KVM_REG_ARM_DEMUX_ID_CCSIDR */
+#define CSSELR_MAX 12
+
 int kvm_handle_cp10_id(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
 	kvm_inject_undefined(vcpu);
@@ -548,11 +554,112 @@ static int set_invariant_cp15(u64 id, void __user *uaddr)
 	return 0;
 }
 
+static bool is_valid_cache(u32 val)
+{
+	u32 level, ctype;
+
+	if (val >= CSSELR_MAX)
+		return -ENOENT;
+
+	/* Bottom bit is Instruction or Data bit.  Next 3 bits are level. */
+        level = (val >> 1);
+        ctype = (cache_levels >> (level * 3)) & 7;
+
+	switch (ctype) {
+	case 0: /* No cache */
+		return false;
+	case 1: /* Instruction cache only */
+		return (val & 1);
+	case 2: /* Data cache only */
+	case 4: /* Unified cache */
+		return !(val & 1);
+	case 3: /* Separate instruction and data caches */
+		return true;
+	default: /* Reserved: we can't know instruction or data. */
+		return false;
+	}
+}

arch/arm/kvm/coproc.c static int set_invariant_cp15(u64 id, void __user *uaddr) · line 591

Quoted 10 lines
arch/arm/kvm/coproc.c +10 -0
+
+/* Which cache CCSIDR represents depends on CSSELR value. */
+static u32 get_ccsidr(u32 csselr)
+{
+	u32 ccsidr;
+
+	/* Make sure noone else changes CSSELR during this! */
+	local_irq_disable();
+	/* Put value into CSSELR */
+	asm volatile("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr));

You need an isb() here, otherwise you might still address the wrong cache (the two accesses don't hazard in the CPU).

Will

#55 lore raw

Re: [PATCH v4 11/14] KVM: ARM: Demux CCSIDR in the userspace API

Quoted 107 lines · Will Deacon
Quoted 14 lines · Christoffer Dall

The Cache Size Selection Register (CSSELR) selects the current Cache Size ID Register (CCSIDR). You write which cache you are interested in to CSSELR, and read the information out of CCSIDR.

Which cache numbers are valid is known by reading the Cache Level ID Register (CLIDR).

To export this state to userspace, we add a KVM_REG_ARM_DEMUX numberspace (17), which uses 8 bits to represent which register is being demultiplexed (0 for CCSIDR), and the lower 8 bits to represent this demultiplexing (in our case, the CSSELR value, which is 4 bits).

[...]

Quoted 89 lines
 Documentation/virtual/kvm/api.txt |    2
 arch/arm/include/uapi/asm/kvm.h   |    9 ++
 arch/arm/kvm/coproc.c             |  163 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 171 insertions(+), 3 deletions(-)
Documentation/virtual/kvm/api.txt +2 -0
index 9671cd2..bf8f99c 100644
@@ -1804,6 +1804,8 @@ ARM 32-bit CP15 registers have the following id bit patterns:
 ARM 64-bit CP15 registers have the following id bit patterns:
   0x4003 0000 000F <zero:1> <zero:4> <crm:4> <opc1:4> <zero:3>
 
+ARM CCSIDR registers are demultiplexed by CSSELR value:
+  0x4002 0000 0011 00 <csselr:8>
 
 4.69 KVM_GET_ONE_REG
 
arch/arm/include/uapi/asm/kvm.h +9 -0
index a807d9a..78a629c 100644
@@ -80,6 +80,15 @@ struct kvm_arch_memory_slot {
 #define KVM_REG_ARM_CORE             (0x0010 << KVM_REG_ARM_COPROC_SHIFT)
 #define KVM_REG_ARM_CORE_REG(name)   (offsetof(struct kvm_regs, name) / 4)
 
+/* Some registers need more space to represent values. */
+#define KVM_REG_ARM_DEMUX            (0x0011 << KVM_REG_ARM_COPROC_SHIFT)
+#define KVM_REG_ARM_DEMUX_ID_MASK    0x000000000000FF00
+#define KVM_REG_ARM_DEMUX_ID_SHIFT   8
+#define KVM_REG_ARM_DEMUX_ID_CCSIDR  (0x00 << KVM_REG_ARM_DEMUX_ID_SHIFT)
+#define KVM_REG_ARM_DEMUX_VAL_MASK   0x00000000000000FF
+#define KVM_REG_ARM_DEMUX_VAL_SHIFT  0
+
+
 /* KVM_IRQ_LINE irq field index values */
 #define KVM_ARM_IRQ_TYPE_SHIFT               24
 #define KVM_ARM_IRQ_TYPE_MASK                0xff
arch/arm/kvm/coproc.c +36 -0
index 95a0f5e..9ce5861 100644
@@ -35,6 +35,12 @@
  * Co-processor emulation
  *****************************************************************************/
 
+/* 3 bits per cache level, as per CLIDR, but non-existent caches always 0 */
+static u32 cache_levels;
+
+/* CSSELR values; used to index KVM_REG_ARM_DEMUX_ID_CCSIDR */
+#define CSSELR_MAX 12
+
 int kvm_handle_cp10_id(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
      kvm_inject_undefined(vcpu);
@@ -548,11 +554,112 @@ static int set_invariant_cp15(u64 id, void __user *uaddr)
      return 0;
 }
 
+static bool is_valid_cache(u32 val)
+{
+     u32 level, ctype;
+
+     if (val >= CSSELR_MAX)
+             return -ENOENT;
+
+     /* Bottom bit is Instruction or Data bit.  Next 3 bits are level. */
+        level = (val >> 1);
+        ctype = (cache_levels >> (level * 3)) & 7;
+
+     switch (ctype) {
+     case 0: /* No cache */
+             return false;
+     case 1: /* Instruction cache only */
+             return (val & 1);
+     case 2: /* Data cache only */
+     case 4: /* Unified cache */
+             return !(val & 1);
+     case 3: /* Separate instruction and data caches */
+             return true;
+     default: /* Reserved: we can't know instruction or data. */
+             return false;
+     }
+}
+
+/* Which cache CCSIDR represents depends on CSSELR value. */
+static u32 get_ccsidr(u32 csselr)
+{
+     u32 ccsidr;
Quoted 9 lines

arch/arm/kvm/coproc.c static int set_invariant_cp15(u64 id, void __user *uaddr) · line 591

Quoted 5 lines
arch/arm/kvm/coproc.c +5 -0
+
+     /* Make sure noone else changes CSSELR during this! */
+     local_irq_disable();
+     /* Put value into CSSELR */
+     asm volatile("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr));

You need an isb() here, otherwise you might still address the wrong cache (the two accesses don't hazard in the CPU).

that was just totally obvious ;)

#56 lore raw

PATCH v4 12/14 KVM: ARM: VFP userspace interface

In reply to Christoffer Dall

From: Rusty Russell <[email protected]>

We use space #18 for floating point regs.

---
 Documentation/virtual/kvm/api.txt |    6 +
 arch/arm/include/uapi/asm/kvm.h   |   12 ++
 arch/arm/kvm/coproc.c             |  178 +++++++++++++++++++++++++++++++++++++
 3 files changed, 196 insertions(+)
Documentation/virtual/kvm/api.txt +6 -0
index bf8f99c..9ffd702 100644
@@ -1807,6 +1807,12 @@ ARM 64-bit CP15 registers have the following id bit patterns:
 ARM CCSIDR registers are demultiplexed by CSSELR value:
   0x4002 0000 0011 00 <csselr:8>
 
+ARM 32-bit VFP control registers have the following id bit patterns:
+  0x4002 0000 0012 1 <regno:12>
+
+ARM 64-bit FP registers have the following id bit patterns:
+  0x4002 0000 0012 0 <regno:12>
+
 4.69 KVM_GET_ONE_REG
 
 Capability: KVM_CAP_ONE_REG
arch/arm/include/uapi/asm/kvm.h +12 -0
index 78a629c..5142cab 100644
@@ -88,6 +88,18 @@ struct kvm_arch_memory_slot {
 #define KVM_REG_ARM_DEMUX_VAL_MASK	0x00000000000000FF
 #define KVM_REG_ARM_DEMUX_VAL_SHIFT	0
 
+/* VFP registers: we could overload CP10 like ARM does, but that's ugly. */
+#define KVM_REG_ARM_VFP			(0x0012 << KVM_REG_ARM_COPROC_SHIFT)
+#define KVM_REG_ARM_VFP_MASK		0x000000000000FFFF
+#define KVM_REG_ARM_VFP_BASE_REG	0x0
+#define KVM_REG_ARM_VFP_FPSID		0x1000
+#define KVM_REG_ARM_VFP_FPSCR		0x1001
+#define KVM_REG_ARM_VFP_MVFR1		0x1006
+#define KVM_REG_ARM_VFP_MVFR0		0x1007
+#define KVM_REG_ARM_VFP_FPEXC		0x1008
+#define KVM_REG_ARM_VFP_FPINST		0x1009
+#define KVM_REG_ARM_VFP_FPINST2		0x100A
+
 
 /* KVM_IRQ_LINE irq field index values */
 #define KVM_ARM_IRQ_TYPE_SHIFT		24
arch/arm/kvm/coproc.c +178 -0

214 lines not on this page · raw message

#57 lore raw

PATCH v4 13/14 KVM: ARM: Handle guest faults in KVM

In reply to Christoffer Dall

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

We invalidate the instruction cache by MVA whenever we map a page to the guest (no, we cannot only do it when we have an iabt because the guest may happily read/write a page before hitting the icache) if the hardware uses VIPT or PIPT. In the latter case, we can invalidate only that physical page. In the first case, all bets are off and we simply must invalidate the whole affair. Not that VIVT icaches are tagged with vmids, and we are out of the woods on that one. Alexander Graf was nice enough to remind us of this massive pain.

There is also a subtle bug hidden somewhere, which we currently hide by marking all pages dirty even when the pages are only mapped read-only. The current hypothesis is that marking pages dirty may exercise the IO system and data cache more and therefore we don't see stale data in the guest, but it's purely guesswork. The bug is manifested by seemingly random kernel crashes in guests when the host is under extreme memory pressure and swapping is enabled.

---
 arch/arm/include/asm/kvm_arm.h |    9 ++
 arch/arm/include/asm/kvm_asm.h |    2 +
 arch/arm/kvm/mmu.c             |  145 ++++++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/trace.h           |   26 +++++++
 4 files changed, 181 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_arm.h +9 -0
index e2cf681..368db1d 100644
@@ -156,11 +156,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
index 47a0e57..6fccdb3 100644
@@ -71,6 +71,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 +144 -1

162 lines not on this page · raw message

arch/arm/kvm/trace.h +26 -0
index c86a513..e2a9d2e 100644
@@ -39,6 +39,32 @@ TRACE_EVENT(kvm_exit,
 	TP_printk("PC: 0x%08lx", __entry->vcpu_pc)
 );
 
+TRACE_EVENT(kvm_guest_fault,
+	TP_PROTO(unsigned long vcpu_pc, unsigned long hsr,
+		 unsigned long hxfar,
+		 unsigned long ipa),
+	TP_ARGS(vcpu_pc, hsr, hxfar, ipa),
+
+	TP_STRUCT__entry(
+		__field(	unsigned long,	vcpu_pc		)
+		__field(	unsigned long,	hsr		)
+		__field(	unsigned long,	hxfar		)
+		__field(	unsigned long,	ipa		)
+	),
+
+	TP_fast_assign(
+		__entry->vcpu_pc		= vcpu_pc;
+		__entry->hsr			= hsr;
+		__entry->hxfar			= hxfar;
+		__entry->ipa			= ipa;
+	),
+
+	TP_printk("guest fault at PC %#08lx (hxfar %#08lx, "
+		  "ipa %#08lx, hsr %#08lx",
+		  __entry->vcpu_pc, __entry->hxfar,
+		  __entry->hsr, __entry->ipa)
+);
+
 TRACE_EVENT(kvm_irq_line,
 	TP_PROTO(unsigned int type, int vcpu_idx, int irq_num, int level),
 	TP_ARGS(type, vcpu_idx, irq_num, level),
#58 lore raw

Re: [PATCH v4 13/14] KVM: ARM: Handle guest faults in KVM

Quoted 22 lines · Christoffer Dall

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

We invalidate the instruction cache by MVA whenever we map a page to the guest (no, we cannot only do it when we have an iabt because the guest may happily read/write a page before hitting the icache) if the hardware uses VIPT or PIPT. In the latter case, we can invalidate only that physical page. In the first case, all bets are off and we simply must invalidate the whole affair. Not that VIVT icaches are tagged with vmids, and we are out of the woods on that one. Alexander Graf was nice enough to remind us of this massive pain.

There is also a subtle bug hidden somewhere, which we currently hide by marking all pages dirty even when the pages are only mapped read-only. The current hypothesis is that marking pages dirty may exercise the IO system and data cache more and therefore we don't see stale data in the guest, but it's purely guesswork. The bug is manifested by seemingly random kernel crashes in guests when the host is under extreme memory pressure and swapping is enabled.

[...]

Quoted 17 lines
arch/arm/kvm/mmu.c +2 -0
index f45be86..6c9ee3a 100644
@@ -21,9 +21,11 @@
 #include <linux/io.h>
 #include <asm/idmap.h>
 #include <asm/pgalloc.h>
+#include <asm/cacheflush.h>
 #include <asm/kvm_arm.h>
 #include <asm/kvm_mmu.h>
 #include <asm/kvm_asm.h>
+#include <asm/kvm_emulate.h>
 #include <asm/mach/map.h>
 #include <trace/events/kvm.h>
 

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

Quoted 8 lines
arch/arm/kvm/mmu.c +5 -0
@@ -503,9 +505,150 @@ out:
        return ret;
 }
 
+static void coherent_icache_guest_page(struct kvm *kvm, gfn_t gfn)
+{
+       /*
+        * If we are going to insert an instruction page and the icache is
+        * either VIPT or PIPT, there is a potential problem where the host

Why are PIPT caches affected by this? The virtual address is irrelevant.

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

Quoted 13 lines
arch/arm/kvm/mmu.c +13 -0
+        * (or another VM) may have used this page at the same virtual address
+        * as this guest, and we read incorrect data from the icache.  If
+        * we're using a PIPT cache, we can invalidate just that page, but if
+        * we are using a VIPT cache we need to invalidate the entire icache -
+        * damn shame - as written in the ARM ARM (DDI 0406C - Page B3-1384)
+        */
+       if (icache_is_pipt()) {
+               unsigned long hva = gfn_to_hva(kvm, gfn);
+               __cpuc_coherent_user_range(hva, hva + PAGE_SIZE);
+       } else if (!icache_is_vivt_asid_tagged()) {
+               /* any kind of VIPT cache */
+               __flush_icache_all();
+       }

so what if it *is* vivt_asid_tagged? Surely that necessitates nuking the thing, unless it's VMID tagged as well (does that even exist?).

Quoted 6 lines
arch/arm/kvm/mmu.c +6 -0
+}
+
+static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
+                         gfn_t gfn, struct kvm_memory_slot *memslot,
+                         bool is_iabt, unsigned long fault_status)
+{

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

Quoted 10 lines
arch/arm/kvm/mmu.c +10 -0
+       pte_t new_pte;
+       pfn_t pfn;
+       int ret;
+       bool write_fault, writable;
+       unsigned long mmu_seq;
+       struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache;
+
+       if (is_iabt)
+               write_fault = false;
+       else if ((vcpu->arch.hsr & HSR_ISV) && !(vcpu->arch.hsr & HSR_WNR))

Put this hsr parsing in a macro/function? Then you can just assign write_fault directly.

Quoted 6 lines
arch/arm/kvm/mmu.c +6 -0
+               write_fault = false;
+       else
+               write_fault = true;
+
+       if (fault_status == FSC_PERM && !write_fault) {
+               kvm_err("Unexpected L2 read permission error\n");

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

Quoted 10 lines
arch/arm/kvm/mmu.c +10 -0
+               return -EFAULT;
+       }
+
+       /* We need minimum second+third level pages */
+       ret = mmu_topup_memory_cache(memcache, 2, KVM_NR_MEM_OBJS);
+       if (ret)
+               return ret;
+
+       mmu_seq = vcpu->kvm->mmu_notifier_seq;
+       smp_rmb();

What's this barrier for and why isn't there a write barrier paired with it?

Quoted 16 lines
arch/arm/kvm/mmu.c +16 -0
+
+       pfn = gfn_to_pfn_prot(vcpu->kvm, gfn, write_fault, &writable);
+       if (is_error_pfn(pfn))
+               return -EFAULT;
+
+       new_pte = pfn_pte(pfn, PAGE_S2);
+       coherent_icache_guest_page(vcpu->kvm, gfn);
+
+       spin_lock(&vcpu->kvm->mmu_lock);
+       if (mmu_notifier_retry(vcpu->kvm, mmu_seq))
+               goto out_unlock;
+       if (writable) {
+               pte_val(new_pte) |= L_PTE_S2_RDWR;
+               kvm_set_pfn_dirty(pfn);
+       }
+       stage2_set_pte(vcpu->kvm, memcache, fault_ipa, &new_pte, false);

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

Quoted 10 lines
arch/arm/kvm/mmu.c +10 -0
+
+out_unlock:
+       spin_unlock(&vcpu->kvm->mmu_lock);
+       /*
+        * XXX TODO FIXME:
+-        * This is _really_ *weird* !!!
+-        * We should be calling the _clean version, because we set the pfn dirty
+        * if we map the page writable, but this causes memory failures in
+        * guests under heavy memory pressure on the host and heavy swapping.
+        */

We need to get to the bottom of this, or expand this comment and make it more widely known that there is something not understood in KVM VM code for ARM, otherwise we'll be shipping code that we know contains a serious flaw and I worry that, being the first release, it will end up getting deployed fairly widely (although the bug reports might be useful...).

Will

#59 lore raw

Re: [PATCH v4 13/14] KVM: ARM: Handle guest faults in KVM

Quoted 46 lines · Will Deacon
Quoted 22 lines · Christoffer Dall

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

We invalidate the instruction cache by MVA whenever we map a page to the guest (no, we cannot only do it when we have an iabt because the guest may happily read/write a page before hitting the icache) if the hardware uses VIPT or PIPT. In the latter case, we can invalidate only that physical page. In the first case, all bets are off and we simply must invalidate the whole affair. Not that VIVT icaches are tagged with vmids, and we are out of the woods on that one. Alexander Graf was nice enough to remind us of this massive pain.

There is also a subtle bug hidden somewhere, which we currently hide by marking all pages dirty even when the pages are only mapped read-only. The current hypothesis is that marking pages dirty may exercise the IO system and data cache more and therefore we don't see stale data in the guest, but it's purely guesswork. The bug is manifested by seemingly random kernel crashes in guests when the host is under extreme memory pressure and swapping is enabled.

[...]

Quoted 20 lines
arch/arm/kvm/mmu.c +2 -0
index f45be86..6c9ee3a 100644
@@ -21,9 +21,11 @@
 #include <linux/io.h>
 #include <asm/idmap.h>
 #include <asm/pgalloc.h>
+#include <asm/cacheflush.h>
 #include <asm/kvm_arm.h>
 #include <asm/kvm_mmu.h>
 #include <asm/kvm_asm.h>
+#include <asm/kvm_emulate.h>
 #include <asm/mach/map.h>
 #include <trace/events/kvm.h>
 
@@ -503,9 +505,150 @@ out:
        return ret;
 }
 
Quoted 8 lines

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

Quoted 5 lines
arch/arm/kvm/mmu.c +5 -0
+static void coherent_icache_guest_page(struct kvm *kvm, gfn_t gfn)
+{
+       /*
+        * If we are going to insert an instruction page and the icache is
+        * either VIPT or PIPT, there is a potential problem where the host

Why are PIPT caches affected by this? The virtual address is irrelevant.

The comment is slightly misleading, and I'll update it. Just so we're clear, this is the culprit:

1. guest uses page X, containing instruction A
2. page X gets swapped out
3. host uses page X, containing instruction B
4. instruction B enters i-cache at page X's cache line
5. page X gets swapped out
6. guest swaps page X back in
7. guest executes instruction B from cache, should execute instruction A

The point is that with PIPT we can flush only that page from the icache using the host virtual address, as the MMU will do the translation on the fly. In the VIPT we have to nuke the whole thing (unless we .

Quoted 14 lines

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

Quoted 13 lines
arch/arm/kvm/mmu.c +13 -0
+        * (or another VM) may have used this page at the same virtual address
+        * as this guest, and we read incorrect data from the icache.  If
+        * we're using a PIPT cache, we can invalidate just that page, but if
+        * we are using a VIPT cache we need to invalidate the entire icache -
+        * damn shame - as written in the ARM ARM (DDI 0406C - Page B3-1384)
+        */
+       if (icache_is_pipt()) {
+               unsigned long hva = gfn_to_hva(kvm, gfn);
+               __cpuc_coherent_user_range(hva, hva + PAGE_SIZE);
+       } else if (!icache_is_vivt_asid_tagged()) {
+               /* any kind of VIPT cache */
+               __flush_icache_all();
+       }
Quoted 3 lines

so what if it *is* vivt_asid_tagged? Surely that necessitates nuking the thing, unless it's VMID tagged as well (does that even exist?).

see page B3-1392 in the ARM ARM, if it's vivt_asid_tagged it is also vmid tagged.

Quoted 11 lines
Quoted 11 lines
arch/arm/kvm/mmu.c +11 -0
+}
+
+static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
+                         gfn_t gfn, struct kvm_memory_slot *memslot,
+                         bool is_iabt, unsigned long fault_status)
+{
+       pte_t new_pte;
+       pfn_t pfn;
+       int ret;
+       bool write_fault, writable;
+       unsigned long mmu_seq;
Quoted 9 lines

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

Quoted 5 lines
arch/arm/kvm/mmu.c +5 -0
+       struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache;
+
+       if (is_iabt)
+               write_fault = false;
+       else if ((vcpu->arch.hsr & HSR_ISV) && !(vcpu->arch.hsr & HSR_WNR))

Put this hsr parsing in a macro/function? Then you can just assign write_fault directly.

ok

Quoted 11 lines
Quoted 11 lines
arch/arm/kvm/mmu.c +11 -0
+               write_fault = false;
+       else
+               write_fault = true;
+
+       if (fault_status == FSC_PERM && !write_fault) {
+               kvm_err("Unexpected L2 read permission error\n");
+               return -EFAULT;
+       }
+
+       /* We need minimum second+third level pages */
+       ret = mmu_topup_memory_cache(memcache, 2, KVM_NR_MEM_OBJS);
Quoted 9 lines

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

Quoted 5 lines
arch/arm/kvm/mmu.c +5 -0
+       if (ret)
+               return ret;
+
+       mmu_seq = vcpu->kvm->mmu_notifier_seq;
+       smp_rmb();

What's this barrier for and why isn't there a write barrier paired with it?

The read barrier is to ensure that mmu_notifier_seq is read before we call gfn_to_pfn_prot (which is essentially get_user_pages), so that we don't get a page which is unmapped by an MMU notifier before we grab the spinlock that we would never see. I also added a comment explaining it in the patch below.

There is a write barrier paired with it, see virt/kvm/kvm_main.c, specifically kvm_mmu_notifier_invalidate_page (the spin_unlock), and kvm_mmu_notifier_invalidate_range_end.

See the following patch:

arch/arm/include/asm/kvm_mmu.h +12 -0
index 499e7b0..421a20b 100644
@@ -35,4 +35,16 @@ void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);
 phys_addr_t kvm_mmu_get_httbr(void);
 int kvm_mmu_init(void);
 void kvm_clear_hyp_idmap(void);
+
+static inline bool kvm_is_write_fault(unsigned long hsr)
+{
+	unsigned long hsr_ec = hsr >> HSR_EC_SHIFT;
+	if (hsr_ec == HSR_EC_IABT)
+		return false;
+	else if ((hsr & HSR_ISV) && !(hsr & HSR_WNR))
+		return false;
+	else
+		return true;
+}
+
 #endif /* __ARM_KVM_MMU_H__ */
arch/arm/kvm/mmu.c +0 -0
index 50deb74..503aa0f 100644
@@ -497,11 +497,14 @@ static void coherent_icache_guest_page(struct

kvm *kvm, gfn_t gfn)
/*
* If we are going to insert an instruction page and the icache is
* either VIPT or PIPT, there is a potential problem where the host
- * (or another VM) may have used this page at the same virtual address
- * as this guest, and we read incorrect data from the icache. If
- * we're using a PIPT cache, we can invalidate just that page, but if
- * we are using a VIPT cache we need to invalidate the entire icache -
- * damn shame - as written in the ARM ARM (DDI 0406C - Page B3-1384)
+ * (or another VM) may have used the same page as this guest, and we
+ * read incorrect data from the icache. If we're using a PIPT cache,
+ * we can invalidate just that page, but if we are using a VIPT cache
+ * we need to invalidate the entire icache - damn shame - as written
+ * in the ARM ARM (DDI 0406C.b - Page B3-1393).
+ *
+ * VIVT caches are tagged using both the ASID and the VMID and doesn't
+ * need any kind of flushing (DDI 0406C.b - Page B3-1392).
*/
if (icache_is_pipt()) {
unsigned long hva = gfn_to_hva(kvm, gfn);
@@ -514,7 +517,7 @@ static void coherent_icache_guest_page(struct kvm *kvm, gfn_t gfn)

static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
gfn_t gfn, struct kvm_memory_slot *memslot,
- bool is_iabt, unsigned long fault_status)
+ unsigned long fault_status)
{
pte_t new_pte;
pfn_t pfn;
@@ -523,13 +526,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
unsigned long mmu_seq;
struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache;

- if (is_iabt)
- write_fault = false;
- else if ((vcpu->arch.hsr & HSR_ISV) && !(vcpu->arch.hsr & HSR_WNR))
- write_fault = false;
- else
- write_fault = true;
-
+ write_fault = kvm_is_write_fault(vcpu->arch.hsr);
if (fault_status == FSC_PERM && !write_fault) {
kvm_err("Unexpected L2 read permission error\n"); return -EFAULT;
@@ -541,6 +538,15 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
return ret;

mmu_seq = vcpu->kvm->mmu_notifier_seq;
+ /*
+ * Ensure the read of mmu_notifier_seq happens before we call
+ * gfn_to_pfn_prot (which calls get_user_pages), so that we don't risk
+ * the page we just got a reference to gets unmapped before we have a
+ * chance to grab the mmu_lock, which ensure that if the page gets
+ * unmapped afterwards, the call to kvm_unmap_hva will take it away
+ * from us again properly. This smp_rmb() interacts with the smp_wmb()
+ * in kvm_mmu_notifier_invalidate_<page|range_end>.
+ */
smp_rmb();

pfn = gfn_to_pfn_prot(vcpu->kvm, gfn, write_fault, &writable);
@@ -627,8 +633,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
return -EINVAL;
}

- ret = user_mem_abort(vcpu, fault_ipa, gfn, memslot,
- is_iabt, fault_status);
+ ret = user_mem_abort(vcpu, fault_ipa, gfn, memslot, fault_status);
return ret ? ret : 1;
}

--

Thanks!
-Christoffer

#60 lore raw

Re: [PATCH v4 13/14] KVM: ARM: Handle guest faults in KVM

Quoted 8 lines · Christoffer Dall
Quoted 3 lines · Will Deacon

Why are PIPT caches affected by this? The virtual address is irrelevant.

The comment is slightly misleading, and I'll update it. Just so we're clear, this is the culprit:

Quoted 7 lines

1. guest uses page X, containing instruction A
2. page X gets swapped out
3. host uses page X, containing instruction B
4. instruction B enters i-cache at page X's cache line
5. page X gets swapped out
6. guest swaps page X back in
7. guest executes instruction B from cache, should execute instruction A

Ok, that's clearer. Thanks for the explanation.

Quoted 4 lines

The point is that with PIPT we can flush only that page from the icache using the host virtual address, as the MMU will do the translation on the fly. In the VIPT we have to nuke the whole thing (unless we .

Unless we what? Could we flush using the host VA + all virtual aliases instead?

Quoted 14 lines
Quoted 14 lines
Quoted 13 lines
(quoted diff) +13 -0
+        * (or another VM) may have used this page at the same virtual address
+        * as this guest, and we read incorrect data from the icache.  If
+        * we're using a PIPT cache, we can invalidate just that page, but if
+        * we are using a VIPT cache we need to invalidate the entire icache -
+        * damn shame - as written in the ARM ARM (DDI 0406C - Page B3-1384)
+        */
+       if (icache_is_pipt()) {
+               unsigned long hva = gfn_to_hva(kvm, gfn);
+               __cpuc_coherent_user_range(hva, hva + PAGE_SIZE);
+       } else if (!icache_is_vivt_asid_tagged()) {
+               /* any kind of VIPT cache */
+               __flush_icache_all();
+       }
Quoted 6 lines
Quoted 3 lines

so what if it *is* vivt_asid_tagged? Surely that necessitates nuking the thing, unless it's VMID tagged as well (does that even exist?).

see page B3-1392 in the ARM ARM, if it's vivt_asid_tagged it is also vmid tagged.

Great.

Quoted 27 lines
Quoted 11 lines
Quoted 11 lines
(quoted diff) +11 -0
+               write_fault = false;
+       else
+               write_fault = true;
+
+       if (fault_status == FSC_PERM && !write_fault) {
+               kvm_err("Unexpected L2 read permission error\n");
+               return -EFAULT;
+       }
+
+       /* We need minimum second+third level pages */
+       ret = mmu_topup_memory_cache(memcache, 2, KVM_NR_MEM_OBJS);
Quoted 9 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+       if (ret)
+               return ret;
+
+       mmu_seq = vcpu->kvm->mmu_notifier_seq;
+       smp_rmb();

What's this barrier for and why isn't there a write barrier paired with it?

The read barrier is to ensure that mmu_notifier_seq is read before we call gfn_to_pfn_prot (which is essentially get_user_pages), so that we don't get a page which is unmapped by an MMU notifier before we grab the spinlock that we would never see. I also added a comment explaining it in the patch below.

Quoted 3 lines

There is a write barrier paired with it, see virt/kvm/kvm_main.c, specifically kvm_mmu_notifier_invalidate_page (the spin_unlock), and kvm_mmu_notifier_invalidate_range_end.

Ok, so it sounds like a homebrew seqlock implementatiom. Any reason KVM isn't just using those directly?

Will

#61 lore raw

Re: [PATCH v4 13/14] KVM: ARM: Handle guest faults in KVM

Quoted 19 lines · Will Deacon
Quoted 8 lines · Christoffer Dall
Quoted 3 lines · Will Deacon

Why are PIPT caches affected by this? The virtual address is irrelevant.

The comment is slightly misleading, and I'll update it. Just so we're clear, this is the culprit:

Quoted 7 lines

1. guest uses page X, containing instruction A
2. page X gets swapped out
3. host uses page X, containing instruction B
4. instruction B enters i-cache at page X's cache line
5. page X gets swapped out
6. guest swaps page X back in
7. guest executes instruction B from cache, should execute instruction A

Ok, that's clearer. Thanks for the explanation.

Quoted 8 lines
Quoted 4 lines

The point is that with PIPT we can flush only that page from the icache using the host virtual address, as the MMU will do the translation on the fly. In the VIPT we have to nuke the whole thing (unless we .

Unless we what? Could we flush using the host VA + all virtual aliases instead?

you'd have to know all the virtual addresses of the guest(s) mapping that physical page and then flush all the aliases of those addresses, which we don't know at this time.

What we can do (down the road) is to mark the pages as XN, and catch the fault, get the virtual fault address, and then flush that single page.

The tradeoffs need to be measured before implementing this imho, and is an optimization we can add later.

-Christoffer

#62 lore raw

PATCH v4 14/14 KVM: ARM: Handle I/O aborts

In reply to Christoffer Dall

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

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

We only support instruction decoding for valid reasonable MMIO operations where trapping them do not provide sufficient information in the HSR (no 16-bit Thumb instructions provide register writeback that we care about).

The following instruction types are NOT supported for MMIO operations despite the HSR not containing decode info:
- any Load/Store multiple
- any load/store exclusive
- any load/store dual
- anything with the PC as the dest register

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.

Rusty Russell fixed a horrible race pointed out by Ben Herrenschmidt:
(1) Guest complicated mmio instruction traps.
(2) The hardware doesn't tell us enough, so we need to read the actual instruction which was being exectuted.
(3) KVM maps the instruction virtual address to a physical address.
(4) The guest (SMP) swaps out that page, and fills it with something else.
(5) We read the physical address, but now that's the wrong thing.

---
 arch/arm/include/asm/kvm_arm.h     |    3 
 arch/arm/include/asm/kvm_asm.h     |    2 
 arch/arm/include/asm/kvm_decode.h  |   47 ++++
 arch/arm/include/asm/kvm_emulate.h |    9 +
 arch/arm/include/asm/kvm_host.h    |    7 +
 arch/arm/include/asm/kvm_mmio.h    |   51 ++++
 arch/arm/kvm/Makefile              |    2 
 arch/arm/kvm/arm.c                 |   14 +
 arch/arm/kvm/decode.c              |  462 ++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/emulate.c             |  173 +++++++++++++
 arch/arm/kvm/interrupts.S          |   38 +++
 arch/arm/kvm/mmio.c                |  154 ++++++++++++
 arch/arm/kvm/mmu.c                 |    7 -
 arch/arm/kvm/trace.h               |   21 ++
 14 files changed, 985 insertions(+), 5 deletions(-)
 create mode 100644 arch/arm/include/asm/kvm_decode.h
 create mode 100644 arch/arm/include/asm/kvm_mmio.h
 create mode 100644 arch/arm/kvm/decode.c
 create mode 100644 arch/arm/kvm/mmio.c
arch/arm/include/asm/kvm_arm.h +3 -0
index 368db1d..246afd7 100644
@@ -156,8 +156,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_asm.h +2 -0
index 6fccdb3..99c0faf 100644
@@ -77,6 +77,8 @@ 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);
+
+extern u64 __kvm_va_to_pa(struct kvm_vcpu *vcpu, u32 va, bool priv);
 #endif
 
 #endif /* __ARM_KVM_ASM_H__ */
arch/arm/include/asm/kvm_decode.h new file +47 -0

47 lines not on this page · raw message

arch/arm/include/asm/kvm_emulate.h +8 -1
index 01a755b..83a6fe1 100644
@@ -21,16 +21,18 @@
 
 #include <linux/kvm_host.h>
 #include <asm/kvm_asm.h>
+#include <asm/kvm_mmio.h>
 
 u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num);
 u32 *vcpu_spsr(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,
+			struct kvm_exit_mmio *mmio);
 void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr);
 void kvm_inject_undefined(struct kvm_vcpu *vcpu);
 void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr);
 void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr);
-
 static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu)
 {
 	return (u32 *)&vcpu->arch.regs.usr_regs.ARM_pc;
@@ -53,4 +55,9 @@ static inline bool vcpu_mode_priv(struct kvm_vcpu *vcpu)
 	return cpsr_mode > USR_MODE;;
 }
 
+static inline bool kvm_vcpu_reg_is_pc(struct kvm_vcpu *vcpu, int reg)
+{
+	return reg == 15;
+}
+
 #endif /* __ARM_KVM_EMULATE_H__ */
arch/arm/include/asm/kvm_host.h +7 -0
index 1eab4c5..e7fc249 100644
@@ -22,6 +22,7 @@
 #include <asm/kvm.h>
 #include <asm/kvm_asm.h>
 #include <asm/fpstate.h>
+#include <asm/kvm_decode.h>
 
 #define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS
 #define KVM_MEMORY_SLOTS 32
@@ -99,6 +100,12 @@ struct kvm_vcpu_arch {
 	int last_pcpu;
 	cpumask_t require_dcache_flush;
 
+	/* Don't run the guest: see copy_current_insn() */
+	bool pause;
+
+	/* IO related fields */
+	struct kvm_decode mmio_decode;
+
 	/* Interrupt related fields */
 	u32 irq_lines;		/* IRQ and FIQ levels */
 
arch/arm/include/asm/kvm_mmio.h new file +51 -0

51 lines not on this page · raw message

arch/arm/kvm/Makefile +1 -1
index ea5b282..8a4f396 100644
@@ -19,4 +19,4 @@ obj-$(CONFIG_KVM_ARM_HOST) += init.o interrupts.o
 obj-$(CONFIG_KVM_ARM_HOST) += $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
 
 obj-$(CONFIG_KVM_ARM_HOST) += arm.o guest.o mmu.o emulate.o reset.o
-obj-$(CONFIG_KVM_ARM_HOST) += coproc.o coproc_a15.o
+obj-$(CONFIG_KVM_ARM_HOST) += coproc.o coproc_a15.o mmio.o decode.o
arch/arm/kvm/arm.c +13 -1
index 6a1d4c3..435c5cc 100644
@@ -581,6 +581,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 	if (unlikely(vcpu->arch.target < 0))
 		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);
 
@@ -616,7 +622,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		kvm_guest_enter();
 		vcpu->mode = IN_GUEST_MODE;
 
-		ret = kvm_call_hyp(__kvm_vcpu_run, vcpu);
+		smp_mb(); /* set mode before reading vcpu->arch.pause */
+		if (unlikely(vcpu->arch.pause)) {
+			/* This means ignore, try again. */
+			ret = ARM_EXCEPTION_IRQ;
+		} else {
+			ret = kvm_call_hyp(__kvm_vcpu_run, vcpu);
+		}
 
 		vcpu->mode = OUTSIDE_GUEST_MODE;
 		vcpu->arch.last_pcpu = smp_processor_id();
arch/arm/kvm/decode.c new file +462 -0

462 lines not on this page · raw message

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

185 lines not on this page · raw message

arch/arm/kvm/interrupts.S +38 -0
index d79f1d4..7c89708 100644
@@ -188,6 +188,44 @@ after_vfp_restore:
 	clrex				@ Clear exclusive monitor
 	bx	lr			@ return to IOCTL
 
+
+/********************************************************************
+ * Translate VA to PA
+ *
+ * u64 __kvm_va_to_pa(struct kvm_vcpu *vcpu, u32 va, bool priv)
+ *
+ * Arguments:
+ *  r0: pointer to vcpu struct
+ *  r1: virtual address to map (rounded to page)
+ *  r2: 1 = P1 (read) mapping, 0 = P0 (read) mapping.
+ * Returns 64 bit PAR value.
+ */
+ENTRY(__kvm_va_to_pa)
+	push	{r4-r12}
+
+	@ Fold flag into r1, easier than using stack.
+	cmp	r2, #0
+	movne	r2, #1
+	orr	r1, r1, r2
+
+	@ This swaps too many registers, but we're in the slow path anyway.
+	read_cp15_state
+	write_cp15_state 1, r0
+
+	ands	r2, r1, #1
+	bic	r1, r1, r2
+	mcrne	p15, 0, r1, c7, c8, 0	@ VA to PA, ATS1CPR
+	mcreq	p15, 0, r1, c7, c8, 2	@ VA to PA, ATS1CUR
+	isb
+
+	@ Restore host state.
+	read_cp15_state 1, r0
+	write_cp15_state
+
+	mrrc	p15, 0, r0, r1, c7	@ PAR
+	pop	{r4-r12}
+	bx	lr
+
 ENTRY(kvm_call_hyp)
 	hvc	#0
 	bx	lr
arch/arm/kvm/mmio.c new file +154 -0

154 lines not on this page · raw message

arch/arm/kvm/mmu.c +5 -2
index 6c9ee3a..7b0e6e5 100644
@@ -19,11 +19,13 @@
 #include <linux/mman.h>
 #include <linux/kvm_host.h>
 #include <linux/io.h>
+#include <trace/events/kvm.h>
 #include <asm/idmap.h>
 #include <asm/pgalloc.h>
 #include <asm/cacheflush.h>
 #include <asm/kvm_arm.h>
 #include <asm/kvm_mmu.h>
+#include <asm/kvm_mmio.h>
 #include <asm/kvm_asm.h>
 #include <asm/kvm_emulate.h>
 #include <asm/mach/map.h>
@@ -636,8 +638,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
 			return -EFAULT;
 		}
 
-		kvm_pr_unimpl("I/O address abort...");
-		return 0;
+		/* Adjust page offset */
+		fault_ipa |= vcpu->arch.hxfar & ~PAGE_MASK;
+		return io_mem_abort(vcpu, run, fault_ipa, memslot);
 	}
 
 	memslot = gfn_to_memslot(vcpu->kvm, gfn);
arch/arm/kvm/trace.h +21 -0
index e2a9d2e..c3d05f4 100644
@@ -90,6 +90,27 @@ TRACE_EVENT(kvm_irq_line,
 		  __entry->type, __entry->vcpu_idx, __entry->irq_num, __entry->level)
 );
 
+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,
#63 lore raw

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

Quoted 32 lines · Christoffer Dall

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

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

We only support instruction decoding for valid reasonable MMIO operations where trapping them do not provide sufficient information in the HSR (no 16-bit Thumb instructions provide register writeback that we care about).

The following instruction types are NOT supported for MMIO operations despite the HSR not containing decode info:
- any Load/Store multiple
- any load/store exclusive
- any load/store dual
- anything with the PC as the dest register

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.

Rusty Russell fixed a horrible race pointed out by Ben Herrenschmidt:
(1) Guest complicated mmio instruction traps.
(2) The hardware doesn't tell us enough, so we need to read the actual instruction which was being exectuted.
(3) KVM maps the instruction virtual address to a physical address.
(4) The guest (SMP) swaps out that page, and fills it with something else.
(5) We read the physical address, but now that's the wrong thing.

Quoted 4 lines

This is looking like the right sort of thing now, but I would like to see an Acked-by from Dave [CC'd] for this patch.

I'll try and hit the vGIC code this week...

Thanks,

Will

#64 lore raw

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

Quoted 33 lines · Will Deacon
Quoted 32 lines · Christoffer Dall

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

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

We only support instruction decoding for valid reasonable MMIO operations where trapping them do not provide sufficient information in the HSR (no 16-bit Thumb instructions provide register writeback that we care about).

The following instruction types are NOT supported for MMIO operations despite the HSR not containing decode info:
- any Load/Store multiple
- any load/store exclusive
- any load/store dual
- anything with the PC as the dest register

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.

Rusty Russell fixed a horrible race pointed out by Ben Herrenschmidt:
(1) Guest complicated mmio instruction traps.
(2) The hardware doesn't tell us enough, so we need to read the actual instruction which was being exectuted.
(3) KVM maps the instruction virtual address to a physical address.
(4) The guest (SMP) swaps out that page, and fills it with something else.
(5) We read the physical address, but now that's the wrong thing.

Quoted 7 lines
Quoted 4 lines

This is looking like the right sort of thing now, but I would like to see an Acked-by from Dave [CC'd] for this patch.

Apologies for not looking at this until now -- it looks like my mutt filter was buggy, causing me to miss a few mails.

I haven't reviewed the code in depth, but it looks like a reasonable and fairly clean subsystem-specific decoder implementation.

This still is not generic, and looks a bit too specialised to lead directly to a generic implementation which could be reused by other subsystems. But I think it is a step in the right direction in terms of the general way the code works. If a future generic decode implementation does not reuse this code, it should still be able to reuse ideas from it.

The amount of decode here is not that huge, so if KVM needs it, I'm not sure if it is worth blocking this patch just because it's not generic.

I still feel we are going to want to consolidate this decode stuff at some point, though.

Cheers
---Dave