Skip to contentSkip to conversation

KVM/ARM Implementation

PATCH In review Christoffer Dall 12 participants 82 messages Sep 15, 2012 – Oct 8, 2012

Subsystems (7)
lore
Share & tools
lore
#0 lore raw

cover KVM/ARM Implementation

The following series implements KVM support for ARM processors, specifically on the Cortex A-15 platform. We feel this is ready to be merged.

Work is done in collaboration between Columbia University, Virtual Open Systems and ARM/Linaro.

The patch series applies to Linux 3.6-rc5 with a number of merges:
1. git://git.kernel.org/pub/scm/linux/kernel/git/will/linux.git

        branch: io (fc8a08c3a3a)
 2. git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms.git
        branch: hyp-mode-boot-next (e5a04cb0b4a)
 3. git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms.git
        branch: timers-next (437814c44c)
 4. git://git.kernel.org/pub/scm/virt/kvm/kvm.git
        branch: next (7de5bdc96c3)

This is Version 11 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-v11
        branch: kvm-arm-v11-vgic
        branch: kvm-arm-v11-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

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 mem_type prot_pte accessor
2. ARM: Add page table defines for KVM
3. ARM: Section based HYP idmaps
4. ARM: Only initialize HYP idmap when HYP mode is available
5. ARM: Expose PMNC bitfields for KVM use

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:
6. Skeleton and reset hooks
7. Hypervisor initialization
8. Memory virtualization setup (hyp mode mappings and 2nd stage)
9. Inject IRQs and FIQs from userspace
10. World-switch implementation and Hyp exception vectors
11. Emulation framework and coproc emulation
12. Coproc user space API
13. Handle guest user memory aborts
14. Handle guest MMIO aborts
15. Support guest wait-for-interrupt instructions

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 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 (11):
ARM: Add page table and page defines needed by KVM
KVM: ARM: Initial skeleton to compile KVM support
KVM: ARM: Hypervisor inititalization
KVM: ARM: Memory virtualization setup
KVM: ARM: Inject IRQs and FIQs from userspace
KVM: ARM: World-switch implementation
KVM: ARM: Emulation framework and CP15 emulation
KVM: ARM: User space API for getting/setting co-proc registers
KVM: ARM: Handle guest faults in KVM
KVM: ARM: Handle I/O aborts
KVM: ARM: Guest wait-for-interrupts (WFI) support

Marc Zyngier (3):
ARM: add mem_type prot_pte accessor
ARM: Section based HYP idmap
ARM: idmap: only initialize HYP idmap when HYP mode is available

Rusty Russell (1):
ARM: Expose PMNC bitfields for KVM use

 Documentation/virtual/kvm/api.txt           |  125 +++
 arch/arm/Kconfig                            |    2 
 arch/arm/Makefile                           |    1 
 arch/arm/include/asm/idmap.h                |    7 
 arch/arm/include/asm/kvm.h                  |  109 +++
 arch/arm/include/asm/kvm_arm.h              |  197 +++++
 arch/arm/include/asm/kvm_asm.h              |   56 +
 arch/arm/include/asm/kvm_coproc.h           |   40 +
 arch/arm/include/asm/kvm_emulate.h          |  137 ++++
 arch/arm/include/asm/kvm_host.h             |  211 ++++++
 arch/arm/include/asm/kvm_mmu.h              |   46 +
 arch/arm/include/asm/mach/map.h             |    1 
 arch/arm/include/asm/perf_bits.h            |   56 +
 arch/arm/include/asm/pgtable-3level-hwdef.h |    5 
 arch/arm/include/asm/pgtable-3level.h       |   13 
 arch/arm/include/asm/pgtable.h              |    5 
 arch/arm/kernel/asm-offsets.c               |   44 +
 arch/arm/kernel/perf_event_v7.c             |   51 -
 arch/arm/kernel/vmlinux.lds.S               |    6 
 arch/arm/kvm/Kconfig                        |   45 +
 arch/arm/kvm/Makefile                       |   21 +
 arch/arm/kvm/arm.c                          | 1030 +++++++++++++++++++++++++++
 arch/arm/kvm/coproc.c                       |  946 +++++++++++++++++++++++++
 arch/arm/kvm/emulate.c                      |  793 +++++++++++++++++++++
 arch/arm/kvm/exports.c                      |   39 +
 arch/arm/kvm/guest.c                        |  212 ++++++
 arch/arm/kvm/init.S                         |  156 ++++
 arch/arm/kvm/interrupts.S                   |  823 ++++++++++++++++++++++
 arch/arm/kvm/mmu.c                          |  998 ++++++++++++++++++++++++++
 arch/arm/kvm/reset.c                        |   74 ++
 arch/arm/kvm/trace.h                        |  170 ++++
 arch/arm/mm/idmap.c                         |   92 ++
 arch/arm/mm/mmu.c                           |    9 
 include/linux/kvm.h                         |    3 
 mm/memory.c                                 |    2 
 35 files changed, 6454 insertions(+), 71 deletions(-)
 create mode 100644 arch/arm/include/asm/kvm.h
 create mode 100644 arch/arm/include/asm/kvm_arm.h
 create mode 100644 arch/arm/include/asm/kvm_asm.h
 create mode 100644 arch/arm/include/asm/kvm_coproc.h
 create mode 100644 arch/arm/include/asm/kvm_emulate.h
 create mode 100644 arch/arm/include/asm/kvm_host.h
 create mode 100644 arch/arm/include/asm/kvm_mmu.h
 create mode 100644 arch/arm/include/asm/perf_bits.h
 create mode 100644 arch/arm/kvm/Kconfig
 create mode 100644 arch/arm/kvm/Makefile
 create mode 100644 arch/arm/kvm/arm.c
 create mode 100644 arch/arm/kvm/coproc.c
 create mode 100644 arch/arm/kvm/emulate.c
 create mode 100644 arch/arm/kvm/exports.c
 create mode 100644 arch/arm/kvm/guest.c
 create mode 100644 arch/arm/kvm/init.S
 create mode 100644 arch/arm/kvm/interrupts.S
 create mode 100644 arch/arm/kvm/mmu.c
 create mode 100644 arch/arm/kvm/reset.c
 create mode 100644 arch/arm/kvm/trace.h
#1 lore raw

PATCH 1/15 ARM: add mem_type prot_pte accessor

From: Marc Zyngier <[email protected]>

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

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

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

Re: [PATCH 01/15] ARM: add mem_type prot_pte accessor

Quoted 33 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

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

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

---
 arch/arm/include/asm/mach/map.h |    1 +
 arch/arm/mm/mmu.c               |    6 ++++++
 2 files changed, 7 insertions(+)
arch/arm/include/asm/mach/map.h +1 -0
index a6efcdd..3787c9f 100644
@@ -37,6 +37,7 @@ extern void iotable_init(struct map_desc *, int);
 
 struct mem_type;
 extern const struct mem_type *get_mem_type(unsigned int type);
+extern pteval_t get_mem_type_prot_pte(unsigned int type);
 /*
  * external interface to remap single page with appropriate type
  */
arch/arm/mm/mmu.c +0 -0
index 4c2d045..76bf4f5 100644

arch/arm/mm/mmu.c const struct mem_type *get_mem_type(unsigned int type) · line 309

Quoted 9 lines
arch/arm/mm/mmu.c +6 -0
@@ -301,6 +301,12 @@ const struct mem_type *get_mem_type(unsigned int type)
 }
 EXPORT_SYMBOL(get_mem_type);
 
+pteval_t get_mem_type_prot_pte(unsigned int type)
+{
+	return get_mem_type(type)->prot_pte;
+}
+EXPORT_SYMBOL(get_mem_type_prot_pte);
+

get_mem_type can return NULL, so you should probably pass the error through rather than dereferencing it.

Will

#3 lore raw

Re: [PATCH 01/15] ARM: add mem_type prot_pte accessor

Quoted 37 lines · Will Deacon
Quoted 36 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

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

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

---
 arch/arm/include/asm/mach/map.h |    1 +
 arch/arm/mm/mmu.c               |    6 ++++++
 2 files changed, 7 insertions(+)
arch/arm/include/asm/mach/map.h +1 -0
index a6efcdd..3787c9f 100644
@@ -37,6 +37,7 @@ extern void iotable_init(struct map_desc *, int);
 
 struct mem_type;
 extern const struct mem_type *get_mem_type(unsigned int type);
+extern pteval_t get_mem_type_prot_pte(unsigned int type);
 /*
  * external interface to remap single page with appropriate type
  */
arch/arm/mm/mmu.c +0 -0
index 4c2d045..76bf4f5 100644
@@ -301,6 +301,12 @@ const struct mem_type *get_mem_type(unsigned int type)
 }
 EXPORT_SYMBOL(get_mem_type);
 
Quoted 10 lines

arch/arm/mm/mmu.c const struct mem_type *get_mem_type(unsigned int type) · line 309

Quoted 6 lines
arch/arm/mm/mmu.c +6 -0
+pteval_t get_mem_type_prot_pte(unsigned int type)
+{
+     return get_mem_type(type)->prot_pte;
+}
+EXPORT_SYMBOL(get_mem_type_prot_pte);
+

get_mem_type can return NULL, so you should probably pass the error through rather than dereferencing it.

right, I guess callers can check against 0, since L_PTE_PRESENT should always be there.

arch/arm/mm/mmu.c +3 -1
index a153fd4..f2b6287 100644
@@ -305,7 +305,9 @@ EXPORT_SYMBOL(get_mem_type);
 
 pteval_t get_mem_type_prot_pte(unsigned int type)
 {
-       return get_mem_type(type)->prot_pte;
+       if (get_mem_type(type))
+               return get_mem_type(type)->prot_pte;
+       return 0;
 }
 EXPORT_SYMBOL(get_mem_type_prot_pte);
#4 lore raw

Re: [PATCH 01/15] ARM: add mem_type prot_pte accessor

In reply to Christoffer Dall
Quoted 11 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

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

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

Is there a reason why we need this to be exposed, along with all the page table manipulation in patch 7?

Is there a reason why we can't have new MT_ types for PAGE_HYP and the HYP MT_DEVICE type (which is the same as MT_DEVICE but with PTE_USER set) and have the standard ARM/generic kernel code build those mappings?

That would (it seems) also avoid the need to export the pXd_clear_bad() acessors too...

#5 lore raw

Re: [PATCH 01/15] ARM: add mem_type prot_pte accessor

Quoted 13 lines · Russell King - ARM Linux
Quoted 11 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

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

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

Quoted 7 lines

Is there a reason why we need this to be exposed, along with all the page table manipulation in patch 7?

Is there a reason why we can't have new MT_ types for PAGE_HYP and the HYP MT_DEVICE type (which is the same as MT_DEVICE but with PTE_USER set) and have the standard ARM/generic kernel code build those mappings?

For hyp mode we can do this, but we cannot do this for the cpu interfaces that need to be mapped into each VM as they have each their own pgd. We can move the Hyp mode mappings, and I was playing with the though of having a PAGE_KVM_DEVICE set in pgtable.h and a pgprot_guest_device setup in build_mem_type_table.

What do you think?

Quoted 3 lines

That would (it seems) also avoid the need to export the pXd_clear_bad() acessors too...

for hyp mode mappings this would be true, then.

#6 lore raw

Re: [PATCH 01/15] ARM: add mem_type prot_pte accessor

Quoted 18 lines · Christoffer Dall
Quoted 16 lines · Russell King - ARM Linux
Quoted 11 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

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

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

Is there a reason why we need this to be exposed, along with all the page table manipulation in patch 7?

Quoted 8 lines
Quoted 4 lines

Is there a reason why we can't have new MT_ types for PAGE_HYP and the HYP MT_DEVICE type (which is the same as MT_DEVICE but with PTE_USER set) and have the standard ARM/generic kernel code build those mappings?

For hyp mode we can do this, but we cannot do this for the cpu interfaces that need to be mapped into each VM as they have each their own pgd.

Isn't that the same problem? The HYP mode has its own pgd too. I think this is the main issue with the generic code. If we can come up with an interface that allows the generic code to work on alternative pgds, we could pretty much do what Russell suggests here.

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

Re: [PATCH 01/15] ARM: add mem_type prot_pte accessor

Quoted 24 lines · Marc Zyngier
Quoted 23 lines · Christoffer Dall
Quoted 20 lines · Russell King - ARM Linux
Quoted 11 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

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

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

Is there a reason why we need this to be exposed, along with all the page table manipulation in patch 7?

Is there a reason why we can't have new MT_ types for PAGE_HYP and the HYP MT_DEVICE type (which is the same as MT_DEVICE but with PTE_USER set) and have the standard ARM/generic kernel code build those mappings?

Quoted 9 lines
Quoted 3 lines

For hyp mode we can do this, but we cannot do this for the cpu interfaces that need to be mapped into each VM as they have each their own pgd.

Isn't that the same problem? The HYP mode has its own pgd too. I think this is the main issue with the generic code. If we can come up with an interface that allows the generic code to work on alternative pgds, we could pretty much do what Russell suggests here.

Hyp mode has its own pgd, but there will only be one of them which can be allocated at boot and setup at boot in mmu.c

This will not be the case for guest pages. On the other hand, this locks us to only one user of such mappings and more users could potentially (I know it's a stretch) clutter mmu.c later on, which is why I suggest the PAGE_KVM_DEVICE approach for now, which should then be renamed, PAGE_S2_DEVICE probably.

#8 lore raw

PATCH 2/15 ARM: Add page table and page defines needed by KVM

In reply to Christoffer Dall

KVM uses the stage-2 page tables and the Hyp page table format, so let's define the fields we need to access in KVM.

We use pgprot_guest to indicate stage-2 entries.

Christoffer Dall <[email protected]>

---
 arch/arm/include/asm/pgtable-3level.h |   13 +++++++++++++
 arch/arm/include/asm/pgtable.h        |    5 +++++
 arch/arm/mm/mmu.c                     |    3 +++
 3 files changed, 21 insertions(+)
arch/arm/include/asm/pgtable-3level.h +13 -0
index b249035..7351eee 100644
@@ -102,11 +102,24 @@
  */
 #define L_PGD_SWAPPER		(_AT(pgdval_t, 1) << 55)	/* swapper_pg_dir entry */
 
+/*
+ * 2-nd stage PTE definitions for LPAE.
+ */
+#define L_PTE2_SHARED		L_PTE_SHARED
+#define L_PTE2_READ		(_AT(pteval_t, 1) << 6)	/* HAP[0] */
+#define L_PTE2_WRITE		(_AT(pteval_t, 1) << 7)	/* HAP[1] */
+#define L_PTE2_NORM_WB		(_AT(pteval_t, 3) << 4)	/* MemAttr[3:2] */
+#define L_PTE2_INNER_WB		(_AT(pteval_t, 3) << 2)	/* MemAttr[1:0] */
+
 #ifndef __ASSEMBLY__
 
 #define pud_none(pud)		(!pud_val(pud))
 #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 +5 -0
index 41dc31f..c422f62 100644
@@ -70,6 +70,7 @@ extern void __pgd_error(const char *file, int line, pgd_t);
 
 extern pgprot_t		pgprot_user;
 extern pgprot_t		pgprot_kernel;
+extern pgprot_t		pgprot_guest;
 
 #define _MOD_PROT(p, b)	__pgprot(pgprot_val(p) | (b))
 
@@ -82,6 +83,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_USER)
+#define PAGE_KVM_GUEST		_MOD_PROT(pgprot_guest, L_PTE2_READ | \
+					  L_PTE2_NORM_WB | L_PTE2_INNER_WB | \
+					  L_PTE2_SHARED)
 
 #define __PAGE_NONE		__pgprot(_L_PTE_DEFAULT | L_PTE_RDONLY | L_PTE_XN)
 #define __PAGE_SHARED		__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN)
arch/arm/mm/mmu.c +3 -0
index 76bf4f5..a153fd4 100644
@@ -56,9 +56,11 @@ static unsigned int cachepolicy __initdata = CPOLICY_WRITEBACK;
 static unsigned int ecc_mask __initdata = 0;
 pgprot_t pgprot_user;
 pgprot_t pgprot_kernel;
+pgprot_t pgprot_guest;
 
 EXPORT_SYMBOL(pgprot_user);
 EXPORT_SYMBOL(pgprot_kernel);
+EXPORT_SYMBOL(pgprot_guest);
 
 struct cachepolicy {
 	const char	policy[16];
@@ -514,6 +516,7 @@ static void __init build_mem_type_table(void)
 	pgprot_user   = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | user_pgprot);
 	pgprot_kernel = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG |
 				 L_PTE_DIRTY | kern_pgprot);
+	pgprot_guest  = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG);
 
 	mem_types[MT_LOW_VECTORS].prot_l1 |= ecc_mask;
 	mem_types[MT_HIGH_VECTORS].prot_l1 |= ecc_mask;
#9 lore raw

Re: [PATCH 02/15] ARM: Add page table and page defines needed by KVM

Quoted 17 lines · Christoffer Dall

KVM uses the stage-2 page tables and the Hyp page table format, so let's define the fields we need to access in KVM.

We use pgprot_guest to indicate stage-2 entries.

Christoffer Dall <[email protected]>

---
 arch/arm/include/asm/pgtable-3level.h |   13 +++++++++++++
 arch/arm/include/asm/pgtable.h        |    5 +++++
 arch/arm/mm/mmu.c                     |    3 +++
 3 files changed, 21 insertions(+)
arch/arm/include/asm/pgtable-3level.h +0 -0
index b249035..7351eee 100644

arch/arm/include/asm/pgtable-3level.h line 107

Quoted 6 lines
arch/arm/include/asm/pgtable-3level.h +3 -0
@@ -102,11 +102,24 @@
  */
 #define L_PGD_SWAPPER		(_AT(pgdval_t, 1) << 55)	/* swapper_pg_dir entry */
 
+/*
+ * 2-nd stage PTE definitions for LPAE.
+ */

Minor nit: 2nd

arch/arm/include/asm/pgtable-3level.h line 110

Quoted 3 lines
arch/arm/include/asm/pgtable-3level.h +3 -0
+#define L_PTE2_SHARED		L_PTE_SHARED
+#define L_PTE2_READ		(_AT(pteval_t, 1) << 6)	/* HAP[0] */
+#define L_PTE2_WRITE		(_AT(pteval_t, 1) << 7)	/* HAP[1] */

This is actually HAP[2:1], not HAP[1:0]. Also, can you follow what we do for stage 1 translation and name these RDONLY and WRONLY (do you even use that?).

arch/arm/include/asm/pgtable-3level.h line 112

Quoted 2 lines
arch/arm/include/asm/pgtable-3level.h +2 -0
+#define L_PTE2_NORM_WB		(_AT(pteval_t, 3) << 4)	/* MemAttr[3:2] */
+#define L_PTE2_INNER_WB		(_AT(pteval_t, 3) << 2)	/* MemAttr[1:0] */

Another minor nit: PTE2 looks awful. Maybe L_PTE_HYP_* instead?

Quoted 25 lines
arch/arm/include/asm/pgtable-3level.h +4 -0
 #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 +1 -0
index 41dc31f..c422f62 100644
@@ -70,6 +70,7 @@ extern void __pgd_error(const char *file, int line, pgd_t);
 
 extern pgprot_t		pgprot_user;
 extern pgprot_t		pgprot_kernel;
+extern pgprot_t		pgprot_guest;
 
 #define _MOD_PROT(p, b)	__pgprot(pgprot_val(p) | (b))
 

arch/arm/include/asm/pgtable.h extern pgprot_t pgprot_kernel; · line 86

Quoted 4 lines
arch/arm/include/asm/pgtable.h +1 -0
@@ -82,6 +83,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_USER)

Just define L_PTE_HYP to L_PTE_USER, otherwise that's confusing.

arch/arm/include/asm/pgtable.h extern pgprot_t pgprot_kernel; · line 89

Quoted 3 lines
arch/arm/include/asm/pgtable.h +3 -0
+#define PAGE_KVM_GUEST		_MOD_PROT(pgprot_guest, L_PTE2_READ | \
+					  L_PTE2_NORM_WB | L_PTE2_INNER_WB | \
+					  L_PTE2_SHARED)

It would be cleaner to separate the cacheability attributes out from here and into the cache_policies array. Then you just need L_PTE_HYP_RDONLY here.

Will

#10 lore raw

Re: [PATCH 02/15] ARM: Add page table and page defines needed by KVM

Quoted 8 lines · Will Deacon

arch/arm/include/asm/pgtable-3level.h line 110

Quoted 3 lines · Christoffer Dall
arch/arm/include/asm/pgtable-3level.h +3 -0
+#define L_PTE2_SHARED                L_PTE_SHARED
+#define L_PTE2_READ          (_AT(pteval_t, 1) << 6) /* HAP[0] */
+#define L_PTE2_WRITE         (_AT(pteval_t, 1) << 7) /* HAP[1] */

This is actually HAP[2:1], not HAP[1:0]. Also, can you follow what we do for stage 1 translation and name these RDONLY and WRONLY (do you even use that?).

We can't use RDONLY as this would have value 0 as the HAP attributes (stage 2 overriding stage 1 translation attributes). Unless you add 4 definitions like NOACCESS, RDONLY, WRONLY and RDWR to cover all the bit combinations.

Quoted 4 lines

arch/arm/include/asm/pgtable-3level.h line 112

Quoted 2 lines
arch/arm/include/asm/pgtable-3level.h +2 -0
+#define L_PTE2_NORM_WB               (_AT(pteval_t, 3) << 4) /* MemAttr[3:2] */
+#define L_PTE2_INNER_WB              (_AT(pteval_t, 3) << 2) /* MemAttr[1:0] */

Another minor nit: PTE2 looks awful. Maybe L_PTE_HYP_* instead?

L_PTE_HYP may be confused with the Stage 1 Hyp translation which is different from the guest Stage 2.

But I have another minor nit - just write them in the ascending bit order as other definitions in this file.

Signature · 1 line
Catalin
#11 lore raw

Re: [PATCH 02/15] ARM: Add page table and page defines needed by KVM

Quoted 15 lines · Catalin Marinas
Quoted 8 lines · Will Deacon

arch/arm/include/asm/pgtable-3level.h line 110

Quoted 3 lines · Christoffer Dall
arch/arm/include/asm/pgtable-3level.h +3 -0
+#define L_PTE2_SHARED                L_PTE_SHARED
+#define L_PTE2_READ          (_AT(pteval_t, 1) << 6) /* HAP[0] */
+#define L_PTE2_WRITE         (_AT(pteval_t, 1) << 7) /* HAP[1] */

This is actually HAP[2:1], not HAP[1:0]. Also, can you follow what we do for stage 1 translation and name these RDONLY and WRONLY (do you even use that?).

We can't use RDONLY as this would have value 0 as the HAP attributes (stage 2 overriding stage 1 translation attributes). Unless you add 4 definitions like NOACCESS, RDONLY, WRONLY and RDWR to cover all the bit combinations.

Quoted 7 lines
Quoted 4 lines

arch/arm/include/asm/pgtable-3level.h line 112

Quoted 2 lines
arch/arm/include/asm/pgtable-3level.h +2 -0
+#define L_PTE2_NORM_WB               (_AT(pteval_t, 3) << 4) /* MemAttr[3:2] */
+#define L_PTE2_INNER_WB              (_AT(pteval_t, 3) << 2) /* MemAttr[1:0] */

Another minor nit: PTE2 looks awful. Maybe L_PTE_HYP_* instead?

L_PTE_HYP may be confused with the Stage 1 Hyp translation which is different from the guest Stage 2.

exactly, it's misleading, how about L_PTE_STAGE2, a little verbose, but clear...?

Quoted 4 lines

But I have another minor nit - just write them in the ascending bit order as other definitions in this file.

ok, will fix.

Thanks,
-Christoffer

#12 lore raw

Re: [PATCH 02/15] ARM: Add page table and page defines needed by KVM

Quoted 22 lines · Christoffer Dall
Quoted 20 lines · Catalin Marinas
Quoted 8 lines · Will Deacon

arch/arm/include/asm/pgtable-3level.h line 110

Quoted 3 lines · Christoffer Dall
arch/arm/include/asm/pgtable-3level.h +3 -0
+#define L_PTE2_SHARED                L_PTE_SHARED
+#define L_PTE2_READ          (_AT(pteval_t, 1) << 6) /* HAP[0] */
+#define L_PTE2_WRITE         (_AT(pteval_t, 1) << 7) /* HAP[1] */

This is actually HAP[2:1], not HAP[1:0]. Also, can you follow what we do for stage 1 translation and name these RDONLY and WRONLY (do you even use that?).

We can't use RDONLY as this would have value 0 as the HAP attributes (stage 2 overriding stage 1 translation attributes). Unless you add 4 definitions like NOACCESS, RDONLY, WRONLY and RDWR to cover all the bit combinations.

Quoted 4 lines

arch/arm/include/asm/pgtable-3level.h line 112

Quoted 2 lines
arch/arm/include/asm/pgtable-3level.h +2 -0
+#define L_PTE2_NORM_WB               (_AT(pteval_t, 3) << 4) /* MemAttr[3:2] */
+#define L_PTE2_INNER_WB              (_AT(pteval_t, 3) << 2) /* MemAttr[1:0] */

Another minor nit: PTE2 looks awful. Maybe L_PTE_HYP_* instead?

Quoted 5 lines
Quoted 2 lines

L_PTE_HYP may be confused with the Stage 1 Hyp translation which is different from the guest Stage 2.

exactly, it's misleading, how about L_PTE_STAGE2, a little verbose, but clear...?

I don't mind any (apart from L_PTE_HYP_ would be confusing) for stage 2. You could just use S2 to make it shorter.

Signature · 1 line
Catalin
#13 lore raw

Re: [PATCH 02/15] ARM: Add page table and page defines needed by KVM

Quoted 23 lines · Catalin Marinas
Quoted 22 lines · Christoffer Dall
Quoted 20 lines · Catalin Marinas
Quoted 8 lines · Will Deacon

arch/arm/include/asm/pgtable-3level.h line 110

Quoted 3 lines · Christoffer Dall
arch/arm/include/asm/pgtable-3level.h +3 -0
+#define L_PTE2_SHARED                L_PTE_SHARED
+#define L_PTE2_READ          (_AT(pteval_t, 1) << 6) /* HAP[0] */
+#define L_PTE2_WRITE         (_AT(pteval_t, 1) << 7) /* HAP[1] */

This is actually HAP[2:1], not HAP[1:0]. Also, can you follow what we do for stage 1 translation and name these RDONLY and WRONLY (do you even use that?).

We can't use RDONLY as this would have value 0 as the HAP attributes (stage 2 overriding stage 1 translation attributes). Unless you add 4 definitions like NOACCESS, RDONLY, WRONLY and RDWR to cover all the bit combinations.

Quoted 4 lines

arch/arm/include/asm/pgtable-3level.h line 112

Quoted 2 lines
arch/arm/include/asm/pgtable-3level.h +2 -0
+#define L_PTE2_NORM_WB               (_AT(pteval_t, 3) << 4) /* MemAttr[3:2] */
+#define L_PTE2_INNER_WB              (_AT(pteval_t, 3) << 2) /* MemAttr[1:0] */

Another minor nit: PTE2 looks awful. Maybe L_PTE_HYP_* instead?

Quoted 9 lines
Quoted 5 lines
Quoted 2 lines

L_PTE_HYP may be confused with the Stage 1 Hyp translation which is different from the guest Stage 2.

exactly, it's misleading, how about L_PTE_STAGE2, a little verbose, but clear...?

I don't mind any (apart from L_PTE_HYP_ would be confusing) for stage 2. You could just use S2 to make it shorter.

I'm good with that, done.

-Christoffer

#14 lore raw

Re: [PATCH 02/15] ARM: Add page table and page defines needed by KVM

In reply to Will Deacon
Quoted 27 lines · Will Deacon
Quoted 17 lines · Christoffer Dall

KVM uses the stage-2 page tables and the Hyp page table format, so let's define the fields we need to access in KVM.

We use pgprot_guest to indicate stage-2 entries.

Christoffer Dall <[email protected]>

---
 arch/arm/include/asm/pgtable-3level.h |   13 +++++++++++++
 arch/arm/include/asm/pgtable.h        |    5 +++++
 arch/arm/mm/mmu.c                     |    3 +++
 3 files changed, 21 insertions(+)
arch/arm/include/asm/pgtable-3level.h +0 -0
index b249035..7351eee 100644

arch/arm/include/asm/pgtable-3level.h line 107

Quoted 6 lines
arch/arm/include/asm/pgtable-3level.h +3 -0
@@ -102,11 +102,24 @@
  */
 #define L_PGD_SWAPPER                (_AT(pgdval_t, 1) << 55)        /* swapper_pg_dir entry */
 
+/*
+ * 2-nd stage PTE definitions for LPAE.
+ */

Minor nit: 2nd

Quoted 8 lines

arch/arm/include/asm/pgtable-3level.h line 110

Quoted 3 lines
arch/arm/include/asm/pgtable-3level.h +3 -0
+#define L_PTE2_SHARED                L_PTE_SHARED
+#define L_PTE2_READ          (_AT(pteval_t, 1) << 6) /* HAP[0] */
+#define L_PTE2_WRITE         (_AT(pteval_t, 1) << 7) /* HAP[1] */

This is actually HAP[2:1], not HAP[1:0]. Also, can you follow what we do for stage 1 translation and name these RDONLY and WRONLY (do you even use that?).

The ARM arm is actually ambiguous, B3-1335 defines it as HAP[2:1], but B3-1355 defines it as HAP[1:0] and I chose the latter as it is more clear to most people not knowing that for historical reasons {H}AP[0] is not defined. If there's a consensus for the other choice here, then I'm good with that.

Also, these bits have a different meaning for stage-2, HAP[2] (ok, in this case it's less misleading with bit index 2), HAP[2] actually means you can write this, two clear bits means access denied, not read/write, so it made more sense to me to do:

prot = READ | WRITE;

than

prt = RDONLY | WRONLY; // (not mutually exclusive). See my point?

Quoted 37 lines

arch/arm/include/asm/pgtable-3level.h line 112

Quoted 2 lines
arch/arm/include/asm/pgtable-3level.h +2 -0
+#define L_PTE2_NORM_WB               (_AT(pteval_t, 3) << 4) /* MemAttr[3:2] */
+#define L_PTE2_INNER_WB              (_AT(pteval_t, 3) << 2) /* MemAttr[1:0] */

Another minor nit: PTE2 looks awful. Maybe L_PTE_HYP_* instead?

Quoted 25 lines
arch/arm/include/asm/pgtable-3level.h +4 -0
 #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 +1 -0
index 41dc31f..c422f62 100644
@@ -70,6 +70,7 @@ extern void __pgd_error(const char *file, int line, pgd_t);
 
 extern pgprot_t              pgprot_user;
 extern pgprot_t              pgprot_kernel;
+extern pgprot_t              pgprot_guest;
 
 #define _MOD_PROT(p, b)      __pgprot(pgprot_val(p) | (b))
 

arch/arm/include/asm/pgtable.h extern pgprot_t pgprot_kernel; · line 86

Quoted 4 lines
arch/arm/include/asm/pgtable.h +1 -0
@@ -82,6 +83,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_USER)

Just define L_PTE_HYP to L_PTE_USER, otherwise that's confusing.

Quoted 7 lines

arch/arm/include/asm/pgtable.h extern pgprot_t pgprot_kernel; · line 89

Quoted 3 lines
arch/arm/include/asm/pgtable.h +3 -0
+#define PAGE_KVM_GUEST               _MOD_PROT(pgprot_guest, L_PTE2_READ | \
+                                       L_PTE2_NORM_WB | L_PTE2_INNER_WB | \
+                                       L_PTE2_SHARED)

It would be cleaner to separate the cacheability attributes out from here and into the cache_policies array. Then you just need L_PTE_HYP_RDONLY here.

ok, below is an attempt to rework all this, comments please:

(quoted diff) +0 -0

b/arch/arm/include/asm/pgtable-3level.h
index 7351eee..6df235c 100644

arch/arm/include/asm/pgtable-3level.h +12 -6
@@ -103,13 +103,19 @@
 #define L_PGD_SWAPPER		(_AT(pgdval_t, 1) << 55)	/* swapper_pg_dir entry */
 
 /*
- * 2-nd stage PTE definitions for LPAE.
+ * 2nd stage PTE definitions for LPAE.
  */
-#define L_PTE2_SHARED		L_PTE_SHARED
-#define L_PTE2_READ		(_AT(pteval_t, 1) << 6)	/* HAP[0] */
-#define L_PTE2_WRITE		(_AT(pteval_t, 1) << 7)	/* HAP[1] */
-#define L_PTE2_NORM_WB		(_AT(pteval_t, 3) << 4)	/* MemAttr[3:2] */
-#define L_PTE2_INNER_WB		(_AT(pteval_t, 3) << 2)	/* MemAttr[1:0] */
+#define L_PTE_S2_SHARED		L_PTE_SHARED
+#define L_PTE_S2_READ		(_AT(pteval_t, 1) << 6)	  /* HAP[1] */
+#define L_PTE_S2_WRITE		(_AT(pteval_t, 1) << 7)	  /* HAP[2] */
+#define L_PTE_S2_MT_UNCACHED	(_AT(pteval_t, 0x5) << 2) /* MemAttr[3:0] */
+#define L_PTE_S2_MT_WRTHROUGH	(_AT(pteval_t, 0xa) << 2) /* MemAttr[3:0] */
+#define L_PTE_S2_MT_WRBACK	(_AT(pteval_t, 0xf) << 2) /* MemAttr[3:0] */
+
+/*
+ * Hyp-mode PL2 PTE definitions for LPAE.
+ */
+#define L_PTE_HYP		L_PTE_USER
 
 #ifndef __ASSEMBLY__
 
arch/arm/include/asm/pgtable.h +2 -4
index c422f62..6ab276b 100644
@@ -83,10 +83,8 @@ extern pgprot_t		pgprot_guest;
 #define PAGE_READONLY_EXEC	_MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_RDONLY)
 #define PAGE_KERNEL		_MOD_PROT(pgprot_kernel, L_PTE_XN)
 #define PAGE_KERNEL_EXEC	pgprot_kernel
-#define PAGE_HYP		_MOD_PROT(pgprot_kernel, L_PTE_USER)
-#define PAGE_KVM_GUEST		_MOD_PROT(pgprot_guest, L_PTE2_READ | \
-					  L_PTE2_NORM_WB | L_PTE2_INNER_WB | \
-					  L_PTE2_SHARED)
+#define PAGE_HYP		_MOD_PROT(pgprot_kernel, L_PTE_HYP)
+#define PAGE_KVM_GUEST		_MOD_PROT(pgprot_guest, L_PTE_S2_READ)
 
 #define __PAGE_NONE		__pgprot(_L_PTE_DEFAULT | L_PTE_RDONLY | L_PTE_XN)
 #define __PAGE_SHARED		__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN)
arch/arm/kvm/mmu.c +0 -0
index 82d0edf..3ff427b 100644
@@ -476,7 +476,7 @@ int kvm_phys_addr_ioremap(struct kvm *kvm,

phys_addr_t guest_ipa,

end = (guest_ipa + size + PAGE_SIZE - 1) & PAGE_MASK; prot = __pgprot(get_mem_type_prot_pte(MT_DEVICE) | L_PTE_USER |
- L_PTE2_READ | L_PTE2_WRITE);
+ L_PTE_S2_READ | L_PTE_S2_WRITE);
pfn = __phys_to_pfn(pa);

for (addr = guest_ipa; addr < end; addr += PAGE_SIZE) {
@@ -567,7 +567,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
goto out;
new_pte = pfn_pte(pfn, PAGE_KVM_GUEST);
if (writable)
- pte_val(new_pte) |= L_PTE2_WRITE;
+ pte_val(new_pte) |= L_PTE_S2_WRITE;
coherent_icache_guest_page(vcpu->kvm, gfn);

spin_lock(&vcpu->kvm->arch.pgd_lock);

arch/arm/mm/mmu.c +10 -2
index f2b6287..a06f3496 100644
@@ -67,6 +67,7 @@ struct cachepolicy {
 	unsigned int	cr_mask;
 	pmdval_t	pmd;
 	pteval_t	pte;
+	pteval_t	pte_s2;
 };
 
 static struct cachepolicy cache_policies[] __initdata = {
@@ -75,26 +76,31 @@ static struct cachepolicy cache_policies[] __initdata = {
 		.cr_mask	= CR_W|CR_C,
 		.pmd		= PMD_SECT_UNCACHED,
 		.pte		= L_PTE_MT_UNCACHED,
+		.pte_s2		= L_PTE_S2_MT_UNCACHED,
 	}, {
 		.policy		= "buffered",
 		.cr_mask	= CR_C,
 		.pmd		= PMD_SECT_BUFFERED,
 		.pte		= L_PTE_MT_BUFFERABLE,
+		.pte_s2		= L_PTE_S2_MT_UNCACHED,
 	}, {
 		.policy		= "writethrough",
 		.cr_mask	= 0,
 		.pmd		= PMD_SECT_WT,
 		.pte		= L_PTE_MT_WRITETHROUGH,
+		.pte_s2		= L_PTE_S2_MT_WRTHROUGH,
 	}, {
 		.policy		= "writeback",
 		.cr_mask	= 0,
 		.pmd		= PMD_SECT_WB,
 		.pte		= L_PTE_MT_WRITEBACK,
+		.pte_s2		= L_PTE_S2_MT_WRBACK,
 	}, {
 		.policy		= "writealloc",
 		.cr_mask	= 0,
 		.pmd		= PMD_SECT_WBWA,
 		.pte		= L_PTE_MT_WRITEALLOC,
+		.pte_s2		= L_PTE_S2_MT_WRBACK,
 	}
 };
 
@@ -318,7 +324,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 user_pgprot, kern_pgprot, vecs_pgprot, guest_pgprot;
 	int cpu_arch = cpu_architecture();
 	int i;
 
@@ -430,6 +436,7 @@ static void __init build_mem_type_table(void)
 	 */
 	cp = &cache_policies[cachepolicy];
 	vecs_pgprot = kern_pgprot = user_pgprot = cp->pte;
+	guest_pgprot = cp->pte_s2;
 
 	/*
 	 * Enable CPU-specific coherency if supported.
@@ -464,6 +471,7 @@ static void __init build_mem_type_table(void)
 			user_pgprot |= L_PTE_SHARED;
 			kern_pgprot |= L_PTE_SHARED;
 			vecs_pgprot |= L_PTE_SHARED;
+			guest_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;
@@ -518,7 +526,7 @@ static void __init build_mem_type_table(void)
 	pgprot_user   = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | user_pgprot);
 	pgprot_kernel = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG |
 				 L_PTE_DIRTY | kern_pgprot);
-	pgprot_guest  = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG);
+	pgprot_guest  = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | guest_pgprot);
 
 	mem_types[MT_LOW_VECTORS].prot_l1 |= ecc_mask;
 	mem_types[MT_HIGH_VECTORS].prot_l1 |= ecc_mask;
#15 lore raw

Re: [PATCH 02/15] ARM: Add page table and page defines needed by KVM

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

Review

Quoted 3 lines · Christoffer Dall
(quoted diff) +3 -0
+#define L_PTE2_SHARED                L_PTE_SHARED
+#define L_PTE2_READ          (_AT(pteval_t, 1) << 6) /* HAP[0] */
+#define L_PTE2_WRITE         (_AT(pteval_t, 1) << 7) /* HAP[1] */

This is actually HAP[2:1], not HAP[1:0]. Also, can you follow what we do for stage 1 translation and name these RDONLY and WRONLY (do you even use that?).

Quoted 5 lines

The ARM arm is actually ambiguous, B3-1335 defines it as HAP[2:1], but B3-1355 defines it as HAP[1:0] and I chose the latter as it is more clear to most people not knowing that for historical reasons {H}AP[0] is not defined. If there's a consensus for the other choice here, then I'm good with that.

Just checked the latest ARM ARM (rev C) and "HAP[1:0]" doesn't appear anywhere in the document, so it looks like it's been fixed.

Quoted 10 lines

Also, these bits have a different meaning for stage-2, HAP[2] (ok, in this case it's less misleading with bit index 2), HAP[2] actually means you can write this, two clear bits means access denied, not read/write, so it made more sense to me to do:

prot = READ | WRITE;

than

prt = RDONLY | WRONLY; // (not mutually exclusive). See my point?

If you define the bits like:

L_PTE_S2_RDONLY (_AT(pteval_t, 1) << 6)
L_PTE_S2_WRONLY (_AT(pteval_t, 2) << 6)

then I think it's fairly clear and it also matches the ARM ARM descriptions for the HAP permissions. You could also add L_PTE_S2_RDWR if you don't like orring the things elsewhere.

Quoted 21 lines
Quoted 4 lines

It would be cleaner to separate the cacheability attributes out from here and into the cache_policies array. Then you just need L_PTE_HYP_RDONLY here.

ok, below is an attempt to rework all this, comments please:

arch/arm/include/asm/pgtable-3level.h +1 -2
index 7351eee..6df235c 100644
@@ -103,13 +103,19 @@
 #define L_PGD_SWAPPER		(_AT(pgdval_t, 1) << 55)	/* swapper_pg_dir entry */
 
 /*
- * 2-nd stage PTE definitions for LPAE.
+ * 2nd stage PTE definitions for LPAE.
  */
-#define L_PTE2_SHARED		L_PTE_SHARED

arch/arm/include/asm/pgtable-3level.h line 113

Quoted 10 lines
arch/arm/include/asm/pgtable-3level.h +6 -4
-#define L_PTE2_READ		(_AT(pteval_t, 1) << 6)	/* HAP[0] */
-#define L_PTE2_WRITE		(_AT(pteval_t, 1) << 7)	/* HAP[1] */
-#define L_PTE2_NORM_WB		(_AT(pteval_t, 3) << 4)	/* MemAttr[3:2] */
-#define L_PTE2_INNER_WB		(_AT(pteval_t, 3) << 2)	/* MemAttr[1:0] */
+#define L_PTE_S2_SHARED		L_PTE_SHARED
+#define L_PTE_S2_READ		(_AT(pteval_t, 1) << 6)	  /* HAP[1] */
+#define L_PTE_S2_WRITE		(_AT(pteval_t, 1) << 7)	  /* HAP[2] */
+#define L_PTE_S2_MT_UNCACHED	(_AT(pteval_t, 0x5) << 2) /* MemAttr[3:0] */
+#define L_PTE_S2_MT_WRTHROUGH	(_AT(pteval_t, 0xa) << 2) /* MemAttr[3:0] */
+#define L_PTE_S2_MT_WRBACK	(_AT(pteval_t, 0xf) << 2) /* MemAttr[3:0] */

Again, just use the same names as we do for stage 1. It really makes the code easier to read (L_PTE_S2_MT_WRITETHROUGH etc.).

Quoted 81 lines
arch/arm/include/asm/pgtable-3level.h +4 -0
+/*
+ * Hyp-mode PL2 PTE definitions for LPAE.
+ */
+#define L_PTE_HYP		L_PTE_USER
 
 #ifndef __ASSEMBLY__
 
arch/arm/include/asm/pgtable.h +2 -4
index c422f62..6ab276b 100644
@@ -83,10 +83,8 @@ extern pgprot_t		pgprot_guest;
 #define PAGE_READONLY_EXEC	_MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_RDONLY)
 #define PAGE_KERNEL		_MOD_PROT(pgprot_kernel, L_PTE_XN)
 #define PAGE_KERNEL_EXEC	pgprot_kernel
-#define PAGE_HYP		_MOD_PROT(pgprot_kernel, L_PTE_USER)
-#define PAGE_KVM_GUEST		_MOD_PROT(pgprot_guest, L_PTE2_READ | \
-					  L_PTE2_NORM_WB | L_PTE2_INNER_WB | \
-					  L_PTE2_SHARED)
+#define PAGE_HYP		_MOD_PROT(pgprot_kernel, L_PTE_HYP)
+#define PAGE_KVM_GUEST		_MOD_PROT(pgprot_guest, L_PTE_S2_READ)
 
 #define __PAGE_NONE		__pgprot(_L_PTE_DEFAULT | L_PTE_RDONLY | L_PTE_XN)
 #define __PAGE_SHARED		__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN)
arch/arm/kvm/mmu.c +1 -1
index 82d0edf..3ff427b 100644
@@ -476,7 +476,7 @@ int kvm_phys_addr_ioremap(struct kvm *kvm,
 phys_addr_t guest_ipa,
 
 	end = (guest_ipa + size + PAGE_SIZE - 1) & PAGE_MASK;
 	prot = __pgprot(get_mem_type_prot_pte(MT_DEVICE) | L_PTE_USER |
-			L_PTE2_READ | L_PTE2_WRITE);
+			L_PTE_S2_READ | L_PTE_S2_WRITE);
 	pfn = __phys_to_pfn(pa);
 
arch/arm/kvm/mmu.c +1 -1
 	for (addr = guest_ipa; addr < end; addr += PAGE_SIZE) {
@@ -567,7 +567,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu,
 phys_addr_t fault_ipa,
 		goto out;
 	new_pte = pfn_pte(pfn, PAGE_KVM_GUEST);
 	if (writable)
-		pte_val(new_pte) |= L_PTE2_WRITE;
+		pte_val(new_pte) |= L_PTE_S2_WRITE;
 	coherent_icache_guest_page(vcpu->kvm, gfn);
 
arch/arm/kvm/mmu.c +0 -0
 	spin_lock(&vcpu->kvm->arch.pgd_lock);
arch/arm/mm/mmu.c +4 -0
index f2b6287..a06f3496 100644
@@ -67,6 +67,7 @@ struct cachepolicy {
 	unsigned int	cr_mask;
 	pmdval_t	pmd;
 	pteval_t	pte;
+	pteval_t	pte_s2;
 };
 
 static struct cachepolicy cache_policies[] __initdata = {
@@ -75,26 +76,31 @@ static struct cachepolicy cache_policies[] __initdata = {
 		.cr_mask	= CR_W|CR_C,
 		.pmd		= PMD_SECT_UNCACHED,
 		.pte		= L_PTE_MT_UNCACHED,
+		.pte_s2		= L_PTE_S2_MT_UNCACHED,
 	}, {
 		.policy		= "buffered",
 		.cr_mask	= CR_C,
 		.pmd		= PMD_SECT_BUFFERED,
 		.pte		= L_PTE_MT_BUFFERABLE,
+		.pte_s2		= L_PTE_S2_MT_UNCACHED,
 	}, {
 		.policy		= "writethrough",
 		.cr_mask	= 0,
 		.pmd		= PMD_SECT_WT,
 		.pte		= L_PTE_MT_WRITETHROUGH,
+		.pte_s2		= L_PTE_S2_MT_WRTHROUGH,
 	}, {
 		.policy		= "writeback",
 		.cr_mask	= 0,
 		.pmd		= PMD_SECT_WB,

arch/arm/mm/mmu.c static struct cachepolicy cache_policies[] __initdata = { · line 105

Quoted 10 lines
arch/arm/mm/mmu.c +2 -0
 		.pte		= L_PTE_MT_WRITEBACK,
+		.pte_s2		= L_PTE_S2_MT_WRBACK,
 	}, {
 		.policy		= "writealloc",
 		.cr_mask	= 0,
 		.pmd		= PMD_SECT_WBWA,
 		.pte		= L_PTE_MT_WRITEALLOC,
+		.pte_s2		= L_PTE_S2_MT_WRBACK,
 	}
 };

Does this still compile for classic MMU? It might be nicer to use arrays for the pte types instead of the extra field too -- I assume you'll want something similar for the pmd if/when you map stage2 translations using sections?

Quoted 18 lines
arch/arm/mm/mmu.c +2 -1
@@ -318,7 +324,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 user_pgprot, kern_pgprot, vecs_pgprot, guest_pgprot;
 	int cpu_arch = cpu_architecture();
 	int i;
 
@@ -430,6 +436,7 @@ static void __init build_mem_type_table(void)
 	 */
 	cp = &cache_policies[cachepolicy];
 	vecs_pgprot = kern_pgprot = user_pgprot = cp->pte;
+	guest_pgprot = cp->pte_s2;
 
 	/*
 	 * Enable CPU-specific coherency if supported.

arch/arm/mm/mmu.c static void __init build_mem_type_table(void) · line 474

Quoted 4 lines
arch/arm/mm/mmu.c +1 -0
@@ -464,6 +471,7 @@ static void __init build_mem_type_table(void)
 			user_pgprot |= L_PTE_SHARED;
 			kern_pgprot |= L_PTE_SHARED;
 			vecs_pgprot |= L_PTE_SHARED;
+			guest_pgprot |= L_PTE_SHARED;

If we're using L_PTE_SHARED directly, do we even need L_PTE_S2_SHARED to be defined?

arch/arm/mm/mmu.c static void __init build_mem_type_table(void) · line 529

Quoted 9 lines
arch/arm/mm/mmu.c +1 -1
 			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;
@@ -518,7 +526,7 @@ static void __init build_mem_type_table(void)
 	pgprot_user   = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | user_pgprot);
 	pgprot_kernel = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG |
 				 L_PTE_DIRTY | kern_pgprot);
-	pgprot_guest  = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG);
+	pgprot_guest  = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | guest_pgprot);

We don't have L_PTE_S2_PRESENT, for example.

I'll try and get on to the meat of the code at some point, but there's an awful lot of it and it's hard to see how it fits together.

Cheers,

Will

#16 lore raw

Re: [PATCH 02/15] ARM: Add page table and page defines needed by KVM

Quoted 32 lines · Will Deacon
Quoted 11 lines · Christoffer Dall
Quoted 9 lines · Will Deacon

Review

Quoted 3 lines · Christoffer Dall
(quoted diff) +3 -0
+#define L_PTE2_SHARED                L_PTE_SHARED
+#define L_PTE2_READ          (_AT(pteval_t, 1) << 6) /* HAP[0] */
+#define L_PTE2_WRITE         (_AT(pteval_t, 1) << 7) /* HAP[1] */

This is actually HAP[2:1], not HAP[1:0]. Also, can you follow what we do for stage 1 translation and name these RDONLY and WRONLY (do you even use that?).

Quoted 5 lines

The ARM arm is actually ambiguous, B3-1335 defines it as HAP[2:1], but B3-1355 defines it as HAP[1:0] and I chose the latter as it is more clear to most people not knowing that for historical reasons {H}AP[0] is not defined. If there's a consensus for the other choice here, then I'm good with that.

Just checked the latest ARM ARM (rev C) and "HAP[1:0]" doesn't appear anywhere in the document, so it looks like it's been fixed.

Quoted 10 lines

Also, these bits have a different meaning for stage-2, HAP[2] (ok, in this case it's less misleading with bit index 2), HAP[2] actually means you can write this, two clear bits means access denied, not read/write, so it made more sense to me to do:

prot = READ | WRITE;

than

prt = RDONLY | WRONLY; // (not mutually exclusive). See my point?

Quoted 9 lines

If you define the bits like:

L_PTE_S2_RDONLY (_AT(pteval_t, 1) << 6)
L_PTE_S2_WRONLY (_AT(pteval_t, 2) << 6)

then I think it's fairly clear and it also matches the ARM ARM descriptions for the HAP permissions. You could also add L_PTE_S2_RDWR if you don't like orring the things elsewhere.

ok

Quoted 26 lines
Quoted 26 lines
Quoted 4 lines

It would be cleaner to separate the cacheability attributes out from here and into the cache_policies array. Then you just need L_PTE_HYP_RDONLY here.

ok, below is an attempt to rework all this, comments please:

arch/arm/include/asm/pgtable-3level.h +2 -6
index 7351eee..6df235c 100644
@@ -103,13 +103,19 @@
 #define L_PGD_SWAPPER                (_AT(pgdval_t, 1) << 55)        /* swapper_pg_dir entry */
 
 /*
- * 2-nd stage PTE definitions for LPAE.
+ * 2nd stage PTE definitions for LPAE.
  */
-#define L_PTE2_SHARED                L_PTE_SHARED
-#define L_PTE2_READ          (_AT(pteval_t, 1) << 6) /* HAP[0] */
-#define L_PTE2_WRITE         (_AT(pteval_t, 1) << 7) /* HAP[1] */
-#define L_PTE2_NORM_WB               (_AT(pteval_t, 3) << 4) /* MemAttr[3:2] */
-#define L_PTE2_INNER_WB              (_AT(pteval_t, 3) << 2) /* MemAttr[1:0] */
+#define L_PTE_S2_SHARED              L_PTE_SHARED
Quoted 9 lines

arch/arm/include/asm/pgtable-3level.h line 113

Quoted 5 lines
arch/arm/include/asm/pgtable-3level.h +5 -0
+#define L_PTE_S2_READ                (_AT(pteval_t, 1) << 6)   /* HAP[1] */
+#define L_PTE_S2_WRITE               (_AT(pteval_t, 1) << 7)   /* HAP[2] */
+#define L_PTE_S2_MT_UNCACHED (_AT(pteval_t, 0x5) << 2) /* MemAttr[3:0] */
+#define L_PTE_S2_MT_WRTHROUGH        (_AT(pteval_t, 0xa) << 2) /* MemAttr[3:0] */
+#define L_PTE_S2_MT_WRBACK   (_AT(pteval_t, 0xf) << 2) /* MemAttr[3:0] */

Again, just use the same names as we do for stage 1. It really makes the code easier to read (L_PTE_S2_MT_WRITETHROUGH etc.).

ok, I've changed it, they're just really long

Quoted 88 lines
Quoted 88 lines
arch/arm/include/asm/pgtable-3level.h +4 -0
+/*
+ * Hyp-mode PL2 PTE definitions for LPAE.
+ */
+#define L_PTE_HYP            L_PTE_USER
 
 #ifndef __ASSEMBLY__
 
arch/arm/include/asm/pgtable.h +2 -4
index c422f62..6ab276b 100644
@@ -83,10 +83,8 @@ extern pgprot_t            pgprot_guest;
 #define PAGE_READONLY_EXEC   _MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_RDONLY)
 #define PAGE_KERNEL          _MOD_PROT(pgprot_kernel, L_PTE_XN)
 #define PAGE_KERNEL_EXEC     pgprot_kernel
-#define PAGE_HYP             _MOD_PROT(pgprot_kernel, L_PTE_USER)
-#define PAGE_KVM_GUEST               _MOD_PROT(pgprot_guest, L_PTE2_READ | \
-                                       L_PTE2_NORM_WB | L_PTE2_INNER_WB | \
-                                       L_PTE2_SHARED)
+#define PAGE_HYP             _MOD_PROT(pgprot_kernel, L_PTE_HYP)
+#define PAGE_KVM_GUEST               _MOD_PROT(pgprot_guest, L_PTE_S2_READ)
 
 #define __PAGE_NONE          __pgprot(_L_PTE_DEFAULT | L_PTE_RDONLY | L_PTE_XN)
 #define __PAGE_SHARED                __pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN)
arch/arm/kvm/mmu.c +1 -1
index 82d0edf..3ff427b 100644
@@ -476,7 +476,7 @@ int kvm_phys_addr_ioremap(struct kvm *kvm,
 phys_addr_t guest_ipa,
 
      end = (guest_ipa + size + PAGE_SIZE - 1) & PAGE_MASK;
      prot = __pgprot(get_mem_type_prot_pte(MT_DEVICE) | L_PTE_USER |
-                     L_PTE2_READ | L_PTE2_WRITE);
+                     L_PTE_S2_READ | L_PTE_S2_WRITE);
      pfn = __phys_to_pfn(pa);
 
      for (addr = guest_ipa; addr < end; addr += PAGE_SIZE) {
arch/arm/kvm/mmu.c +1 -1
@@ -567,7 +567,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu,
 phys_addr_t fault_ipa,
              goto out;
      new_pte = pfn_pte(pfn, PAGE_KVM_GUEST);
      if (writable)
-             pte_val(new_pte) |= L_PTE2_WRITE;
+             pte_val(new_pte) |= L_PTE_S2_WRITE;
      coherent_icache_guest_page(vcpu->kvm, gfn);
 
      spin_lock(&vcpu->kvm->arch.pgd_lock);
arch/arm/mm/mmu.c +5 -0
index f2b6287..a06f3496 100644
@@ -67,6 +67,7 @@ struct cachepolicy {
      unsigned int    cr_mask;
      pmdval_t        pmd;
      pteval_t        pte;
+     pteval_t        pte_s2;
 };
 
 static struct cachepolicy cache_policies[] __initdata = {
@@ -75,26 +76,31 @@ static struct cachepolicy cache_policies[] __initdata = {
              .cr_mask        = CR_W|CR_C,
              .pmd            = PMD_SECT_UNCACHED,
              .pte            = L_PTE_MT_UNCACHED,
+             .pte_s2         = L_PTE_S2_MT_UNCACHED,
      }, {
              .policy         = "buffered",
              .cr_mask        = CR_C,
              .pmd            = PMD_SECT_BUFFERED,
              .pte            = L_PTE_MT_BUFFERABLE,
+             .pte_s2         = L_PTE_S2_MT_UNCACHED,
      }, {
              .policy         = "writethrough",
              .cr_mask        = 0,
              .pmd            = PMD_SECT_WT,
              .pte            = L_PTE_MT_WRITETHROUGH,
+             .pte_s2         = L_PTE_S2_MT_WRTHROUGH,
      }, {
              .policy         = "writeback",
              .cr_mask        = 0,
              .pmd            = PMD_SECT_WB,
              .pte            = L_PTE_MT_WRITEBACK,
+             .pte_s2         = L_PTE_S2_MT_WRBACK,
      }, {
              .policy         = "writealloc",
              .cr_mask        = 0,
              .pmd            = PMD_SECT_WBWA,
              .pte            = L_PTE_MT_WRITEALLOC,
Quoted 9 lines

arch/arm/mm/mmu.c static struct cachepolicy cache_policies[] __initdata = { · line 105

Quoted 3 lines
arch/arm/mm/mmu.c +1 -0
+             .pte_s2         = L_PTE_S2_MT_WRBACK,
      }
 };

Does this still compile for classic MMU? It might be nicer to use arrays for the pte types instead of the extra field too -- I assume you'll want something similar for the pmd if/when you map stage2 translations using sections?

definitely did not compile for MMU, and was just an RFC. I don't think it's nicer to have an opaque array than an explicit field as it is clearer and requires less code changes, and I think we will add the pmd_s2 field when we need it, so we don't merge untested code. Perhaps I am missing some good reason to use an array.

Quoted 15 lines
Quoted 15 lines
arch/arm/mm/mmu.c +2 -1
@@ -318,7 +324,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 user_pgprot, kern_pgprot, vecs_pgprot, guest_pgprot;
      int cpu_arch = cpu_architecture();
      int i;
 
@@ -430,6 +436,7 @@ static void __init build_mem_type_table(void)
       */
      cp = &cache_policies[cachepolicy];
      vecs_pgprot = kern_pgprot = user_pgprot = cp->pte;
+     guest_pgprot = cp->pte_s2;
 
Quoted 11 lines

arch/arm/mm/mmu.c static void __init build_mem_type_table(void) · line 474

Quoted 7 lines
arch/arm/mm/mmu.c +1 -0
      /*
       * Enable CPU-specific coherency if supported.
@@ -464,6 +471,7 @@ static void __init build_mem_type_table(void)
                      user_pgprot |= L_PTE_SHARED;
                      kern_pgprot |= L_PTE_SHARED;
                      vecs_pgprot |= L_PTE_SHARED;
+                     guest_pgprot |= L_PTE_SHARED;

If we're using L_PTE_SHARED directly, do we even need L_PTE_S2_SHARED to be defined?

nope, removed it.

Quoted 12 lines

arch/arm/mm/mmu.c static void __init build_mem_type_table(void) · line 529

Quoted 9 lines
arch/arm/mm/mmu.c +1 -1
                      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;
@@ -518,7 +526,7 @@ static void __init build_mem_type_table(void)
      pgprot_user   = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | user_pgprot);
      pgprot_kernel = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG |
                               L_PTE_DIRTY | kern_pgprot);
-     pgprot_guest  = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG);
+     pgprot_guest  = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | guest_pgprot);

We don't have L_PTE_S2_PRESENT, for example.

Quoted 3 lines

I'll try and get on to the meat of the code at some point, but there's an awful lot of it and it's hard to see how it fits together.

I don't think it's that bad once you understand the KVM structure (which you will need to anyway to review the code) and it's very much self-contained. In any case, I'll be available for any questions to clarify things if needed. I appreciate the review very much!

Here's an updated version of the entire patch that compile both with/without LPAE:

Author: Christoffer Dall <[email protected]>
Date:   Wed Sep 19 17:50:07 2012 -0400

ARM: Add page table and page defines needed by KVM

KVM uses the stage-2 page tables and the Hyp page table format, so let's define the fields we need to access in KVM.

We use pgprot_guest to indicate stage-2 entries.

Christoffer Dall <[email protected]>

(quoted diff) +0 -0

b/arch/arm/include/asm/pgtable-3level.h
index b249035..eaba5a4 100644

arch/arm/include/asm/pgtable-3level.h +18 -0
@@ -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 +3 -0
index 41dc31f..f9a124a 100644
@@ -70,6 +70,7 @@ extern void __pgd_error(const char *file, int line, pgd_t);
 
 extern pgprot_t		pgprot_user;
 extern pgprot_t		pgprot_kernel;
+extern pgprot_t		pgprot_guest;
 
 #define _MOD_PROT(p, b)	__pgprot(pgprot_val(p) | (b))
 
@@ -82,6 +83,8 @@ 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_KVM_GUEST		_MOD_PROT(pgprot_guest, 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 +0 -0
index 57f21de..6267f38 100644
@@ -56,43 +56,57 @@ static unsigned int cachepolicy __initdata =

CPOLICY_WRITEBACK;
static unsigned int ecc_mask __initdata = 0;
pgprot_t pgprot_user;
pgprot_t pgprot_kernel;
+pgprot_t pgprot_guest;

EXPORT_SYMBOL(pgprot_user);
EXPORT_SYMBOL(pgprot_kernel);
+EXPORT_SYMBOL(pgprot_guest);

struct cachepolicy {
const char policy[16];
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),
}
};

@@ -314,7 +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 user_pgprot, kern_pgprot, vecs_pgprot, guest_pgprot;
int cpu_arch = cpu_architecture();
int i;

@@ -426,6 +440,7 @@ static void __init build_mem_type_table(void)
*/
cp = &cache_policies[cachepolicy];
vecs_pgprot = kern_pgprot = user_pgprot = cp->pte;
+ guest_pgprot = cp->pte_s2;

/*
* Enable CPU-specific coherency if supported.
@@ -460,6 +475,7 @@ static void __init build_mem_type_table(void)

 			user_pgprot |= L_PTE_SHARED;
 			kern_pgprot |= L_PTE_SHARED;
 			vecs_pgprot |= L_PTE_SHARED;

+ guest_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;
@@ -514,6 +530,7 @@ static void __init build_mem_type_table(void)

 	pgprot_user   = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | user_pgprot);
 	pgprot_kernel = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG |

L_PTE_DIRTY | kern_pgprot);
+ pgprot_guest = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | guest_pgprot);

mem_types[MT_LOW_VECTORS].prot_l1 |= ecc_mask; mem_types[MT_HIGH_VECTORS].prot_l1 |= ecc_mask;

#17 lore raw

PATCH 3/15 ARM: Section based HYP idmap

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

Add a HYP pgd to the core code (so it can benefit all Linux hypervisors).

Populate this pgd with an identity mapping of the code contained in the .hyp.idmap.text section

Offer a method to drop the this identity mapping through hyp_idmap_teardown and re-create it through hyp_idmap_setup.

Make all the above depend on CONFIG_ARM_VIRT_EXT

---
 arch/arm/include/asm/idmap.h                |    7 ++
 arch/arm/include/asm/pgtable-3level-hwdef.h |    1 
 arch/arm/kernel/vmlinux.lds.S               |    6 ++
 arch/arm/mm/idmap.c                         |   88 +++++++++++++++++++++++----
 4 files changed, 89 insertions(+), 13 deletions(-)
arch/arm/include/asm/idmap.h +7 -0
index bf863ed..a1ab8d6 100644
@@ -11,4 +11,11 @@ extern pgd_t *idmap_pgd;
 
 void setup_mm_for_reboot(void);
 
+#ifdef CONFIG_ARM_VIRT_EXT
+extern pgd_t *hyp_pgd;
+
+void hyp_idmap_teardown(void);
+void hyp_idmap_setup(void);
+#endif
+
 #endif	/* __ASM_IDMAP_H */
arch/arm/include/asm/pgtable-3level-hwdef.h +1 -0
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 +76 -12

115 lines not on this page · raw message

#18 lore raw

Re: [PATCH 03/15] ARM: Section based HYP idmap

Quoted 8 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

Add a HYP pgd to the core code (so it can benefit all Linux hypervisors).

Populate this pgd with an identity mapping of the code contained in the .hyp.idmap.text section

Quoted 8 lines

Offer a method to drop the this identity mapping through hyp_idmap_teardown and re-create it through hyp_idmap_setup.

Make all the above depend on CONFIG_ARM_VIRT_EXT

I didn't get CC'd on this -- please check your git send-email configuration.

Quoted 28 lines
---
 arch/arm/include/asm/idmap.h                |    7 ++
 arch/arm/include/asm/pgtable-3level-hwdef.h |    1 
 arch/arm/kernel/vmlinux.lds.S               |    6 ++
 arch/arm/mm/idmap.c                         |   88 +++++++++++++++++++++++----
 4 files changed, 89 insertions(+), 13 deletions(-)
arch/arm/include/asm/idmap.h +7 -0
index bf863ed..a1ab8d6 100644
@@ -11,4 +11,11 @@ extern pgd_t *idmap_pgd;
 
 void setup_mm_for_reboot(void);
 
+#ifdef CONFIG_ARM_VIRT_EXT
+extern pgd_t *hyp_pgd;
+
+void hyp_idmap_teardown(void);
+void hyp_idmap_setup(void);
+#endif
+
 #endif	/* __ASM_IDMAP_H */
arch/arm/include/asm/pgtable-3level-hwdef.h +0 -0
index d795282..a2d404e 100644

arch/arm/include/asm/pgtable-3level-hwdef.h line 48

Quoted 5 lines
arch/arm/include/asm/pgtable-3level-hwdef.h +1 -0
@@ -44,6 +44,7 @@
 #define PMD_SECT_XN		(_AT(pmdval_t, 1) << 54)
 #define PMD_SECT_AP_WRITE	(_AT(pmdval_t, 0))
 #define PMD_SECT_AP_READ	(_AT(pmdval_t, 0))
+#define PMD_SECT_AP1		(_AT(pmdval_t, 1) << 6)
 #define PMD_SECT_TEX(x)		(_AT(pmdval_t, 0))

If we're making an idmap for the hyp, shouldn't this just follow the L_PTE_HYP_RDONLY bit (L_PTE2_READ?) from the previous patch except with PMD in the name (the bit is identical)?

Quoted 48 lines
arch/arm/mm/idmap.c +14 -8
index ab88ed4..7a944af 100644
@@ -1,4 +1,6 @@
+#include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/slab.h>
 
 #include <asm/cputype.h>
 #include <asm/idmap.h>
@@ -59,11 +61,20 @@ static void idmap_add_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
 	} while (pud++, addr = next, addr != end);
 }
 
-static void identity_mapping_add(pgd_t *pgd, unsigned long addr, unsigned long end)
+static void identity_mapping_add(pgd_t *pgd, const char *text_start,
+				 const char *text_end, unsigned long prot)
 {
-	unsigned long prot, next;
+	unsigned long addr, end;
+	unsigned long next;
+
+	addr = virt_to_phys(text_start);
+	end = virt_to_phys(text_end);
+
+	pr_info("Setting up static %sidentity map for 0x%llx - 0x%llx\n",
+		prot ? "HYP " : "",
+		(long long)addr, (long long)end);
+	prot |= PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AF;
 
-	prot = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AF;
 	if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale())
 		prot |= PMD_BIT4;
 
@@ -78,24 +89,77 @@ extern char  __idmap_text_start[], __idmap_text_end[];
 
 static int __init init_static_idmap(void)
 {
-	phys_addr_t idmap_start, idmap_end;
-
 	idmap_pgd = pgd_alloc(&init_mm);
 	if (!idmap_pgd)
 		return -ENOMEM;
 
-	/* Add an identity mapping for the physical address of the section. */
-	idmap_start = virt_to_phys((void *)__idmap_text_start);
-	idmap_end = virt_to_phys((void *)__idmap_text_end);

arch/arm/mm/idmap.c extern char __idmap_text_start[], __idmap_text_end[]; · line 101

Quoted 10 lines
arch/arm/mm/idmap.c +2 -4
-
-	pr_info("Setting up static identity map for 0x%llx - 0x%llx\n",
-		(long long)idmap_start, (long long)idmap_end);
-	identity_mapping_add(idmap_pgd, idmap_start, idmap_end);
+	identity_mapping_add(idmap_pgd, __idmap_text_start,
+			     __idmap_text_end, 0);
 
 	return 0;
 }
 early_initcall(init_static_idmap);

I think it would be cleaner just to have a separate initcall for the hyp pgd initialisation, leaving the print like it is. That way we also don't have to infer the exception level from the prot, which is pretty fragile.

Quoted 49 lines
arch/arm/mm/idmap.c +49 -0
+#ifdef CONFIG_ARM_VIRT_EXT
+pgd_t *hyp_pgd;
+EXPORT_SYMBOL_GPL(hyp_pgd);
+
+static void hyp_idmap_del_pmd(pgd_t *pgd, unsigned long addr)
+{
+	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));
+}
+
+extern char  __hyp_idmap_text_start[], __hyp_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(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);
+
+	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(void)
+{
+	identity_mapping_add(hyp_pgd, __hyp_idmap_text_start,
+			     __hyp_idmap_text_end, PMD_SECT_AP1);
+}
+EXPORT_SYMBOL_GPL(hyp_idmap_setup);
+
+static int __init hyp_init_static_idmap(void)
+{

arch/arm/mm/idmap.c extern char __idmap_text_start[], __idmap_text_end[]; · line 161

Quoted 10 lines
arch/arm/mm/idmap.c +10 -0
+	hyp_pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL);
+	if (!hyp_pgd)
+		return -ENOMEM;
+
+	hyp_idmap_setup();
+
+	return 0;
+}
+early_initcall(hyp_init_static_idmap);
+#endif

I'd rather the alloc/free functions for the hyp pgd were somewhere else, like they are for standard pgds. Then we can just call them here without having to encode knowledge of PGD size etc in the mapping code.

Will

#19 lore raw

Re: [PATCH 03/15] ARM: Section based HYP idmap

[snip]

Quoted 9 lines
Quoted 9 lines
(quoted diff) +9 -0
+
+static int __init hyp_init_static_idmap(void)
+{
+     hyp_pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL);
+     if (!hyp_pgd)
+             return -ENOMEM;
+
+     hyp_idmap_setup();
+
Quoted 9 lines

Review

Quoted 4 lines
(quoted diff) +4 -0
+     return 0;
+}
+early_initcall(hyp_init_static_idmap);
+#endif

I'd rather the alloc/free functions for the hyp pgd were somewhere else, like they are for standard pgds. Then we can just call them here without having to encode knowledge of PGD size etc in the mapping code.

this used to be the case, but Marc changed it iirc., so just cc'ing him. The following is an attempt at what you're looking for if I understand you correctly:

arch/arm/include/asm/idmap.h +2 -4
index a1ab8d6..36708ba 100644
@@ -12,10 +12,8 @@ extern pgd_t *idmap_pgd;
 void setup_mm_for_reboot(void);
 
 #ifdef CONFIG_ARM_VIRT_EXT
-extern pgd_t *hyp_pgd;
-
-void hyp_idmap_teardown(void);
-void hyp_idmap_setup(void);
+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/kvm_mmu.h +0 -0
index c3f90b0..ecfaaf0 100644
@@ -43,4 +43,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);
+int kvm_mmu_init(void);
+void kvm_mmu_exit(void);
#endif /* __ARM_KVM_MMU_H__ */

arch/arm/kvm/arm.c +9 -3
index 7e11280..d64ce2a 100644
@@ -35,7 +35,6 @@
 #include <asm/ptrace.h>
 #include <asm/mman.h>
 #include <asm/cputype.h>
-#include <asm/idmap.h>
 #include <asm/tlbflush.h>
 #include <asm/cacheflush.h>
 #include <asm/virt.h>
@@ -887,7 +886,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 = virt_to_phys(hyp_pgd);
+	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;
@@ -918,6 +917,13 @@ static int init_hyp_mode(void)
 	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.
 	 */
@@ -954,7 +960,7 @@ static int init_hyp_mode(void)
 	/*
 	 * Unmap the identity mapping
 	 */
-	hyp_idmap_teardown();
+	kvm_mmu_exit();
 
 	/*
 	 * Map the Hyp-code called directly from the host
arch/arm/kvm/mmu.c +21 -0
index 9499d4d..a35a8a9 100644
@@ -34,6 +34,7 @@
 #include "trace.h"
 
 static DEFINE_MUTEX(kvm_hyp_pgd_mutex);
+static pgd_t *hyp_pgd;
 
 static int mmu_topup_memory_cache(struct kvm_mmu_memory_cache *cache,
 				  int min, int max)
@@ -994,3 +995,23 @@ 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)
+{
+	return virt_to_phys(hyp_pgd);
+}
+
+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;
+}
+
+void kvm_mmu_exit(void)
+{
+	hyp_idmap_teardown(hyp_pgd);
+}
arch/arm/mm/idmap.c +0 -3
index f0ab339..ea7430e 100644
@@ -102,9 +102,6 @@ static int __init init_static_idmap(void)
 early_initcall(init_static_idmap);
 
 #if defined(CONFIG_ARM_VIRT_EXT) && defined(CONFIG_ARM_LPAE)
-pgd_t *hyp_pgd;
-EXPORT_SYMBOL_GPL(hyp_pgd);
-
 static void hyp_idmap_del_pmd(pgd_t *pgd, unsigned long addr)
 {
 	pud_t *pud;
@@ -123,7 +120,7 @@ extern char  __hyp_idmap_text_start[],

__hyp_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(void)
+void hyp_idmap_teardown(pgd_t *hyp_pgd)
{
unsigned long addr, end;
unsigned long next;
@@ -141,27 +138,12 @@ void hyp_idmap_teardown(void)
}
EXPORT_SYMBOL_GPL(hyp_idmap_teardown);

-void hyp_idmap_setup(void)
+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);
-
-static int __init hyp_init_static_idmap(void)
-{
- if (!is_hyp_mode_available())
- return 0;
-
- hyp_pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL);
- if (!hyp_pgd)
- return -ENOMEM;
-
- hyp_idmap_setup();
-
- return 0;
-}
-early_initcall(hyp_init_static_idmap);
#endif

/*

#20 lore raw

PATCH 4/15 ARM: idmap: only initialize HYP idmap when HYP mode is available

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

---
 arch/arm/mm/idmap.c |    4 ++++
 1 file changed, 4 insertions(+)
arch/arm/mm/idmap.c +4 -0
index 7a944af..95e8d67 100644
@@ -8,6 +8,7 @@
 #include <asm/pgtable.h>
 #include <asm/sections.h>
 #include <asm/system_info.h>
+#include <asm/virt.h>
 
 pgd_t *idmap_pgd;
 
@@ -149,6 +150,9 @@ EXPORT_SYMBOL_GPL(hyp_idmap_setup);
 
 static int __init hyp_init_static_idmap(void)
 {
+	if (!is_hyp_mode_available())
+		return 0;
+
 	hyp_pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL);
 	if (!hyp_pgd)
 		return -ENOMEM;
#22 lore raw

Re: [PATCH 04/15] ARM: idmap: only initialize HYP idmap when HYP mode is available

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

From: Marc Zyngier <[email protected]>

---
 arch/arm/mm/idmap.c |    4 ++++
 1 file changed, 4 insertions(+)

Just fold this into the previous patch.

yep, that was silly.

Thanks,
-Christoffer

#23 lore raw

PATCH 5/15 ARM: Expose PMNC bitfields for KVM use

In reply to Christoffer Dall

From: Rusty Russell <[email protected]>

We want some of these for use in KVM, so pull them out of arch/arm/kernel/perf_event_v7.c into their own asm/perf_bits.h.

---
 arch/arm/include/asm/perf_bits.h |   56 ++++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/perf_event_v7.c  |   51 +----------------------------------
 2 files changed, 57 insertions(+), 50 deletions(-)
 create mode 100644 arch/arm/include/asm/perf_bits.h
arch/arm/include/asm/perf_bits.h new file +56 -0
new file mode 100644
index 0000000..eeb266a
@@ -0,0 +1,56 @@
+#ifndef __ARM_PERF_BITS_H__
+#define __ARM_PERF_BITS_H__
+
+/*
+ * ARMv7 low level PMNC access
+ */
+
+/*
+ * Per-CPU PMNC: config reg
+ */
+#define ARMV7_PMNC_E		(1 << 0) /* Enable all counters */
+#define ARMV7_PMNC_P		(1 << 1) /* Reset all counters */
+#define ARMV7_PMNC_C		(1 << 2) /* Cycle counter reset */
+#define ARMV7_PMNC_D		(1 << 3) /* CCNT counts every 64th cpu cycle */
+#define ARMV7_PMNC_X		(1 << 4) /* Export to ETM */
+#define ARMV7_PMNC_DP		(1 << 5) /* Disable CCNT if non-invasive debug*/
+#define	ARMV7_PMNC_N_SHIFT	11	 /* Number of counters supported */
+#define	ARMV7_PMNC_N_MASK	0x1f
+#define	ARMV7_PMNC_MASK		0x3f	 /* Mask for writable bits */
+
+/*
+ * FLAG: counters overflow flag status reg
+ */
+#define	ARMV7_FLAG_MASK		0xffffffff	/* Mask for writable bits */
+#define	ARMV7_OVERFLOWED_MASK	ARMV7_FLAG_MASK
+
+/*
+ * PMXEVTYPER: Event selection reg
+ */
+#define	ARMV7_EVTYPE_MASK	0xc00000ff	/* Mask for writable bits */
+#define	ARMV7_EVTYPE_EVENT	0xff		/* Mask for EVENT bits */
+
+/*
+ * Event filters for PMUv2
+ */
+#define	ARMV7_EXCLUDE_PL1	(1 << 31)
+#define	ARMV7_EXCLUDE_USER	(1 << 30)
+#define	ARMV7_INCLUDE_HYP	(1 << 27)
+
+#ifndef __ASSEMBLY__
+static inline u32 armv7_pmnc_read(void)
+{
+	u32 val;
+	asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r"(val));
+	return val;
+}
+
+static inline void armv7_pmnc_write(u32 val)
+{
+	val &= ARMV7_PMNC_MASK;
+	isb();
+	asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r"(val));
+}
+#endif
+
+#endif /* __ARM_PERF_BITS_H__ */
arch/arm/kernel/perf_event_v7.c +1 -50

68 lines not on this page · raw message

#24 lore raw

Re: [PATCH 05/15] ARM: Expose PMNC bitfields for KVM use

Quoted 5 lines · Christoffer Dall

From: Rusty Russell <[email protected]>

We want some of these for use in KVM, so pull them out of arch/arm/kernel/perf_event_v7.c into their own asm/perf_bits.h.

Quoted 7 lines
---
 arch/arm/include/asm/perf_bits.h |   56 ++++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/perf_event_v7.c  |   51 +----------------------------------
 2 files changed, 57 insertions(+), 50 deletions(-)
 create mode 100644 arch/arm/include/asm/perf_bits.h

I don't like this I'm afraid. These bit definitions, although useful for kvm, are only applicable to ARMv7 PMUs. Perf does a reasonable job of separating the low-level CPU-specific code and adding the v7 definitions into their own global header feels like a step backwards. I also want to move a load of this into drivers/ at some point and this won't help with that effort.

Is KVM just using this for world switch? If so, why does it care about the bit definitions (and what do you do for things like debug regs)? Is there anything I could add to perf that you could call instead?

Will

#25 lore raw

Re: [PATCH 05/15] ARM: Expose PMNC bitfields for KVM use

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

From: Rusty Russell <[email protected]>

We want some of these for use in KVM, so pull them out of arch/arm/kernel/perf_event_v7.c into their own asm/perf_bits.h.

Quoted 7 lines
---
 arch/arm/include/asm/perf_bits.h |   56 ++++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/perf_event_v7.c  |   51 +----------------------------------
 2 files changed, 57 insertions(+), 50 deletions(-)
 create mode 100644 arch/arm/include/asm/perf_bits.h

I don't like this I'm afraid. These bit definitions, although useful for kvm, are only applicable to ARMv7 PMUs. Perf does a reasonable job of separating the low-level CPU-specific code and adding the v7 definitions into their own global header feels like a step backwards. I also want to move a load of this into drivers/ at some point and this won't help with that effort.

Quoted 4 lines

Is KVM just using this for world switch? If so, why does it care about the bit definitions (and what do you do for things like debug regs)? Is there anything I could add to perf that you could call instead?

I'm going to let Rusty reply to this one...

-Christoffer

#26 lore raw

Re: [kvmarm] [PATCH 05/15] ARM: Expose PMNC bitfields for KVM use

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

From: Rusty Russell <[email protected]>

We want some of these for use in KVM, so pull them out of arch/arm/kernel/perf_event_v7.c into their own asm/perf_bits.h.

Quoted 7 lines
---
 arch/arm/include/asm/perf_bits.h |   56 ++++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/perf_event_v7.c  |   51 +----------------------------------
 2 files changed, 57 insertions(+), 50 deletions(-)
 create mode 100644 arch/arm/include/asm/perf_bits.h

I don't like this I'm afraid. These bit definitions, although useful for kvm, are only applicable to ARMv7 PMUs. Perf does a reasonable job of separating the low-level CPU-specific code and adding the v7 definitions into their own global header feels like a step backwards. I also want to move a load of this into drivers/ at some point and this won't help with that effort.

Quoted 3 lines

Is KVM just using this for world switch? If so, why does it care about the bit definitions (and what do you do for things like debug regs)? Is there anything I could add to perf that you could call instead?

No, we need these definitions if we ever want to actually implement PMU for the guest.[1]

But we don't do this yet, so you can defer this patch until then if you want.

Cheers,
Rusty.
[1] Which we should do, since you NAKed the patch which would allow the guest to detect that we don't have a PMU, insisting that "all A15s have a PMU", despite the fact that we don't. I assume this means you're busy implementing it right now :)

#27 lore raw

Re: [kvmarm] [PATCH 05/15] ARM: Expose PMNC bitfields for KVM use

Quoted 26 lines · Rusty Russell
Quoted 21 lines · Will Deacon
Quoted 5 lines · Christoffer Dall

From: Rusty Russell <[email protected]>

We want some of these for use in KVM, so pull them out of arch/arm/kernel/perf_event_v7.c into their own asm/perf_bits.h.

Quoted 7 lines
---
 arch/arm/include/asm/perf_bits.h |   56 ++++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/perf_event_v7.c  |   51 +----------------------------------
 2 files changed, 57 insertions(+), 50 deletions(-)
 create mode 100644 arch/arm/include/asm/perf_bits.h

I don't like this I'm afraid. These bit definitions, although useful for kvm, are only applicable to ARMv7 PMUs. Perf does a reasonable job of separating the low-level CPU-specific code and adding the v7 definitions into their own global header feels like a step backwards. I also want to move a load of this into drivers/ at some point and this won't help with that effort.

Quoted 3 lines

Is KVM just using this for world switch? If so, why does it care about the bit definitions (and what do you do for things like debug regs)? Is there anything I could add to perf that you could call instead?

Quoted 5 lines

No, we need these definitions if we ever want to actually implement PMU for the guest.[1]

But we don't do this yet, so you can defer this patch until then if you want.

Ok, let's postpone the pain for the moment.

Quoted 4 lines

[1] Which we should do, since you NAKed the patch which would allow the guest to detect that we don't have a PMU, insisting that "all A15s have a PMU", despite the fact that we don't. I assume this means you're busy implementing it right now :)

Yeah, right! Happy to add hooks to perf if you need them though, rather than expose the raw bit definitions.

Will

#28 lore raw

PATCH 6/15 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.

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

---
 Documentation/virtual/kvm/api.txt  |   54 +++++-
 arch/arm/Kconfig                   |    2 
 arch/arm/Makefile                  |    1 
 arch/arm/include/asm/kvm.h         |   88 +++++++++
 arch/arm/include/asm/kvm_arm.h     |   28 +++
 arch/arm/include/asm/kvm_asm.h     |   30 +++
 arch/arm/include/asm/kvm_coproc.h  |   24 +++
 arch/arm/include/asm/kvm_emulate.h |  108 +++++++++++
 arch/arm/include/asm/kvm_host.h    |  172 ++++++++++++++++++
 arch/arm/kvm/Kconfig               |   44 +++++
 arch/arm/kvm/Makefile              |   21 ++
 arch/arm/kvm/arm.c                 |  345 ++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/coproc.c              |   22 ++
 arch/arm/kvm/emulate.c             |  127 +++++++++++++
 arch/arm/kvm/exports.c             |   21 ++
 arch/arm/kvm/guest.c               |  211 ++++++++++++++++++++++
 arch/arm/kvm/init.S                |   19 ++
 arch/arm/kvm/interrupts.S          |   19 ++
 arch/arm/kvm/mmu.c                 |   17 ++
 arch/arm/kvm/reset.c               |   74 ++++++++
 arch/arm/kvm/trace.h               |   52 +++++
 include/linux/kvm.h                |    2 
 22 files changed, 1477 insertions(+), 4 deletions(-)
 create mode 100644 arch/arm/include/asm/kvm.h
 create mode 100644 arch/arm/include/asm/kvm_arm.h
 create mode 100644 arch/arm/include/asm/kvm_asm.h
 create mode 100644 arch/arm/include/asm/kvm_coproc.h
 create mode 100644 arch/arm/include/asm/kvm_emulate.h
 create mode 100644 arch/arm/include/asm/kvm_host.h
 create mode 100644 arch/arm/kvm/Kconfig
 create mode 100644 arch/arm/kvm/Makefile
 create mode 100644 arch/arm/kvm/arm.c
 create mode 100644 arch/arm/kvm/coproc.c
 create mode 100644 arch/arm/kvm/emulate.c
 create mode 100644 arch/arm/kvm/exports.c
 create mode 100644 arch/arm/kvm/guest.c
 create mode 100644 arch/arm/kvm/init.S
 create mode 100644 arch/arm/kvm/interrupts.S
 create mode 100644 arch/arm/kvm/mmu.c
 create mode 100644 arch/arm/kvm/reset.c
 create mode 100644 arch/arm/kvm/trace.h
Documentation/virtual/kvm/api.txt +50 -4
index 36befa7..67640c6 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
@@ -1732,6 +1733,11 @@ registers, find a list below:
         |                       |
   PPC   | KVM_REG_PPC_HIOR      | 64
 
+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:32>
 
 4.69 KVM_GET_ONE_REG
 
@@ -1985,6 +1991,46 @@ the virtualized real-mode area (VRMA) facility, the kernel will
 re-create the VMRA HPTEs on the next KVM_RUN of any vcpu.)
 
 
+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 2f88d8d..c8fea8f 100644
@@ -2336,3 +2336,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "arch/arm/kvm/Kconfig"
arch/arm/Makefile +1 -0
index 30eae87..3bcc414 100644
@@ -255,6 +255,7 @@ core-$(CONFIG_VFP)		+= arch/arm/vfp/
 # If we have a machine-specific directory, then include it in the build.
 core-y				+= arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
 core-y				+= arch/arm/net/
+core-y 				+= arch/arm/kvm/
 core-y				+= $(machdirs) $(platdirs)
 
 drivers-$(CONFIG_OPROFILE)      += arch/arm/oprofile/
arch/arm/include/asm/kvm.h new file +88 -0

88 lines not on this page · raw message

arch/arm/include/asm/kvm_arm.h new file +28 -0
new file mode 100644
index 0000000..2f9d28e
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __ARM_KVM_ARM_H__
+#define __ARM_KVM_ARM_H__
+
+/* Supported Processor Types */
+#define CORTEX_A15	(0xC0F)
+
+/* Multiprocessor Affinity Register */
+#define MPIDR_CPUID	(0x3 << 0)
+
+#endif /* __ARM_KVM_ARM_H__ */
arch/arm/include/asm/kvm_asm.h new file +30 -0
new file mode 100644
index 0000000..44591f9
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __ARM_KVM_ASM_H__
+#define __ARM_KVM_ASM_H__
+
+#define ARM_EXCEPTION_RESET	  0
+#define ARM_EXCEPTION_UNDEFINED   1
+#define ARM_EXCEPTION_SOFTWARE    2
+#define ARM_EXCEPTION_PREF_ABORT  3
+#define ARM_EXCEPTION_DATA_ABORT  4
+#define ARM_EXCEPTION_IRQ	  5
+#define ARM_EXCEPTION_FIQ	  6
+
+#endif /* __ARM_KVM_ASM_H__ */
arch/arm/include/asm/kvm_coproc.h new file +24 -0
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 +108 -0

108 lines not on this page · raw message

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

172 lines not on this page · raw message

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

44 lines not on this page · raw message

arch/arm/kvm/Makefile new file +21 -0
new file mode 100644
index 0000000..db8c8f4
@@ -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 exports.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 +345 -0

345 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 +127 -0

127 lines not on this page · raw message

arch/arm/kvm/exports.c new file +21 -0
new file mode 100644
index 0000000..3e38c95
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/module.h>
+
+EXPORT_SYMBOL_GPL(smp_send_reschedule);
arch/arm/kvm/guest.c new file +211 -0

211 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/linux/kvm.h +2 -0
index d808694..a960f66 100644
@@ -911,6 +911,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)
#29 lore raw

Re: [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

Quoted 10 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.

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

[...]

Quoted 37 lines
arch/arm/include/asm/kvm.h new file +31 -0
new file mode 100644
index 0000000..a13b582
@@ -0,0 +1,88 @@
+/*
+ * 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>
+
+#define __KVM_HAVE_GUEST_DEBUG
+
+#define KVM_REG_SIZE(id)                                               \
+       (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT))
+
+struct kvm_regs {
+       __u32 usr_regs[15];     /* R0_usr - R14_usr */
+       __u32 svc_regs[3];      /* SP_svc, LR_svc, SPSR_svc */

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

Quoted 10 lines
arch/arm/include/asm/kvm.h new file +10 -0
+       __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 */
+       __u32 pc;               /* The program counter (r15) */
+       __u32 cpsr;             /* The guest CPSR */
+};
+
+/* Supported Processor Types */
+#define KVM_ARM_TARGET_CORTEX_A15      (0xC0F)

So there's this define...

Quoted 19 lines
arch/arm/include/asm/kvm_arm.h new file +13 -0
new file mode 100644
index 0000000..2f9d28e
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *

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

Quoted 10 lines
arch/arm/include/asm/kvm_arm.h new file +10 -0
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __ARM_KVM_ARM_H__
+#define __ARM_KVM_ARM_H__
+
+/* Supported Processor Types */
+#define CORTEX_A15     (0xC0F)

... and then this one. Do we really need both?

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

Quoted 2 lines
arch/arm/include/asm/kvm_arm.h new file +2 -0
+/* Multiprocessor Affinity Register */
+#define MPIDR_CPUID    (0x3 << 0)

I'm fairly sure we already have code under arch/arm/ for dealing with the mpidr. Let's re-use that rather than reinventing it here.

Quoted 24 lines
arch/arm/include/asm/kvm_asm.h new file +18 -0
new file mode 100644
index 0000000..44591f9
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+

arch/arm/include/asm/kvm_asm.h line 28

Quoted 10 lines
arch/arm/include/asm/kvm_asm.h new file +10 -0
+#ifndef __ARM_KVM_ASM_H__
+#define __ARM_KVM_ASM_H__
+
+#define ARM_EXCEPTION_RESET      0
+#define ARM_EXCEPTION_UNDEFINED   1
+#define ARM_EXCEPTION_SOFTWARE    2
+#define ARM_EXCEPTION_PREF_ABORT  3
+#define ARM_EXCEPTION_DATA_ABORT  4
+#define ARM_EXCEPTION_IRQ        5
+#define ARM_EXCEPTION_FIQ        6

Again, you have these defines (which look more suited to an enum type), but later (in kvm_host.h) you have:

arch/arm/include/asm/kvm_asm.h

Quoted 9 lines
arch/arm/include/asm/kvm_asm.h new file +9 -0
+#define EXCEPTION_NONE      0
+#define EXCEPTION_RESET     0x80
+#define EXCEPTION_UNDEFINED 0x40
+#define EXCEPTION_SOFTWARE  0x20
+#define EXCEPTION_PREFETCH  0x10
+#define EXCEPTION_DATA      0x08
+#define EXCEPTION_IMPRECISE 0x04
+#define EXCEPTION_IRQ       0x02
+#define EXCEPTION_FIQ       0x01

Why the noise?

Quoted 38 lines
arch/arm/include/asm/kvm_emulate.h new file +32 -0
new file mode 100644
index 0000000..9e29335
@@ -0,0 +1,108 @@
+/*
+ * 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_EMULATE_H__
+#define __ARM_KVM_EMULATE_H__
+
+#include <linux/kvm_host.h>
+#include <asm/kvm_asm.h>
+
+u32 *vcpu_reg_mode(struct kvm_vcpu *vcpu, u8 reg_num, enum vcpu_mode mode);
+
+static inline u8 __vcpu_mode(u32 cpsr)
+{
+       u8 modes_table[32] = {
+               0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
+               0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
+               MODE_USR,       /* 0x0 */

arch/arm/include/asm/kvm_emulate.h line 42

Quoted 10 lines
arch/arm/include/asm/kvm_emulate.h new file +10 -0
+               MODE_FIQ,       /* 0x1 */
+               MODE_IRQ,       /* 0x2 */
+               MODE_SVC,       /* 0x3 */
+               0xf, 0xf, 0xf,
+               MODE_ABT,       /* 0x7 */
+               0xf, 0xf, 0xf,
+               MODE_UND,       /* 0xb */
+               0xf, 0xf, 0xf,
+               MODE_SYS        /* 0xf */
+       };

These MODE_* definitions sound like our *_MODE definitions... except they're not. It would probably be far more readable as a switch, but at least change the name of those things!

arch/arm/include/asm/kvm_emulate.h line 52

Quoted 6 lines
arch/arm/include/asm/kvm_emulate.h new file +6 -0
+static inline enum vcpu_mode vcpu_mode(struct kvm_vcpu *vcpu)
+{
+       u8 mode = __vcpu_mode(vcpu->arch.regs.cpsr);
+       BUG_ON(mode == 0xf);
+       return mode;
+}

I noticed that you have a fair few BUG_ONs throughout the series. Fair enough, but for hyp code is that really the right thing to do? Killing the guest could make more sense, perhaps?

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

Quoted 4 lines
arch/arm/include/asm/kvm_emulate.h new file +4 -0
+static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu)
+{
+       return vcpu_reg(vcpu, 15);
+}

If you stick a struct pt_regs into struct kvm_regs, you could reuse ARM_pc here etc.

Quoted 20 lines
arch/arm/include/asm/kvm_host.h new file +14 -0
new file mode 100644
index 0000000..24959f4
@@ -0,0 +1,172 @@
+/*
+ * 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

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

Quoted 10 lines
arch/arm/include/asm/kvm_host.h new file +10 -0
+ * 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>
+
+#define KVM_MAX_VCPUS 4

NR_CPUS?

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

Quoted 5 lines
arch/arm/include/asm/kvm_host.h new file +5 -0
+#define KVM_MEMORY_SLOTS 32
+#define KVM_PRIVATE_MEM_SLOTS 4
+#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
+
+#define NUM_FEATURES 0

Ha! No idea what that means, but hopefully there's less code to review because of it :)

Quoted 51 lines
arch/arm/include/asm/kvm_host.h new file +51 -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 {
+       /* The VMID generation used for the virt. memory system */
+       u64    vmid_gen;
+       u32    vmid;
+
+       /* 1-level 2nd stage table and lock */
+       spinlock_t pgd_lock;
+       pgd_t *pgd;
+
+       /* VTTBR value associated with above pgd and vmid */
+       u64    vttbr;
+};
+
+#define EXCEPTION_NONE      0
+#define EXCEPTION_RESET     0x80
+#define EXCEPTION_UNDEFINED 0x40
+#define EXCEPTION_SOFTWARE  0x20
+#define EXCEPTION_PREFETCH  0x10
+#define EXCEPTION_DATA      0x08
+#define EXCEPTION_IMPRECISE 0x04
+#define EXCEPTION_IRQ       0x02
+#define EXCEPTION_FIQ       0x01
+
+#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];
+};
+
+/*
+ * Modes used for short-hand mode determinition in the world-switch code and
+ * in emulation code.
+ *
+ * Note: These indices do NOT correspond to the value of the CPSR mode bits!

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

Quoted 10 lines
arch/arm/include/asm/kvm_host.h new file +10 -0
+ */
+enum vcpu_mode {
+       MODE_FIQ = 0,
+       MODE_IRQ,
+       MODE_SVC,
+       MODE_ABT,
+       MODE_UND,
+       MODE_USR,
+       MODE_SYS
+};

So the need for this enum is for indexing the array of modes, right? But accesses to that array are already hidden behind an accessor function from what I can tell, so I'd rather the arithmetic from cpsr -> index was restricted to that function and the rest of the code just passed either the raw mode or the full cpsr around.

Quoted 206 lines
arch/arm/kvm/arm.c new file +200 -0
new file mode 100644
index 0000000..fd6fa9b
@@ -0,0 +1,345 @@
+/*
+ * 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/errno.h>
+#include <linux/err.h>
+#include <linux/kvm_host.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/mman.h>
+#include <linux/sched.h>
+#include <trace/events/kvm.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+#include <asm/unified.h>
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/mman.h>
+#include <asm/cputype.h>
+
+#ifdef REQUIRES_VIRT
+__asm__(".arch_extension       virt");
+#endif
+
+int kvm_arch_hardware_enable(void *garbage)
+{
+       return 0;
+}
+
+int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
+}
+
+void kvm_arch_hardware_disable(void *garbage)
+{
+}
+
+int kvm_arch_hardware_setup(void)
+{
+       return 0;
+}
+
+void kvm_arch_hardware_unsetup(void)
+{
+}
+
+void kvm_arch_check_processor_compat(void *rtn)
+{
+       *(int *)rtn = 0;
+}
+
+void kvm_arch_sync_events(struct kvm *kvm)
+{
+}
+
+int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
+{
+       if (type)
+               return -EINVAL;
+
+       return 0;
+}
+
+int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
+{
+       return VM_FAULT_SIGBUS;
+}
+
+void kvm_arch_free_memslot(struct kvm_memory_slot *free,
+                          struct kvm_memory_slot *dont)
+{
+}
+
+int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)
+{
+       return 0;
+}
+
+void kvm_arch_destroy_vm(struct kvm *kvm)
+{
+       int i;
+
+       for (i = 0; i < KVM_MAX_VCPUS; ++i) {
+               if (kvm->vcpus[i]) {
+                       kvm_arch_vcpu_free(kvm->vcpus[i]);
+                       kvm->vcpus[i] = NULL;
+               }
+       }
+}
+
+int kvm_dev_ioctl_check_extension(long ext)
+{
+       int r;
+       switch (ext) {
+       case KVM_CAP_USER_MEMORY:
+       case KVM_CAP_DESTROY_MEMORY_REGION_WORKS:
+       case KVM_CAP_ONE_REG:
+               r = 1;
+               break;
+       case KVM_CAP_COALESCED_MMIO:
+               r = KVM_COALESCED_MMIO_PAGE_OFFSET;
+               break;
+       default:
+               r = 0;
+               break;
+       }
+       return r;
+}
+
+long kvm_arch_dev_ioctl(struct file *filp,
+                       unsigned int ioctl, unsigned long arg)
+{
+       return -EINVAL;
+}
+
+int kvm_arch_set_memory_region(struct kvm *kvm,
+                              struct kvm_userspace_memory_region *mem,
+                              struct kvm_memory_slot old,
+                              int user_alloc)
+{
+       return 0;
+}
+
+int kvm_arch_prepare_memory_region(struct kvm *kvm,
+                                  struct kvm_memory_slot *memslot,
+                                  struct kvm_memory_slot old,
+                                  struct kvm_userspace_memory_region *mem,
+                                  int user_alloc)
+{
+       return 0;
+}
+
+void kvm_arch_commit_memory_region(struct kvm *kvm,
+                                  struct kvm_userspace_memory_region *mem,
+                                  struct kvm_memory_slot old,
+                                  int user_alloc)
+{
+}
+
+void kvm_arch_flush_shadow_all(struct kvm *kvm)
+{
+}
+
+void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
+                                  struct kvm_memory_slot *slot)
+{
+}
+
+struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
+{
+       int err;
+       struct kvm_vcpu *vcpu;
+
+       vcpu = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL);
+       if (!vcpu) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       err = kvm_vcpu_init(vcpu, kvm, id);
+       if (err)
+               goto free_vcpu;
+
+       return vcpu;
+free_vcpu:
+       kmem_cache_free(kvm_vcpu_cache, vcpu);
+out:
+       return ERR_PTR(err);
+}
+
+void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
+{
+}
+
+void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
+{
+       kvm_arch_vcpu_free(vcpu);
+}
+
+int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
+{
+       return 0;

arch/arm/kvm/arm.c line 210

Quoted 10 lines
arch/arm/kvm/arm.c new file +10 -0
+}
+
+int __attribute_const__ kvm_target_cpu(void)
+{
+       unsigned int midr;
+
+       midr = read_cpuid_id();
+       switch ((midr >> 4) & 0xfff) {
+       case KVM_ARM_TARGET_CORTEX_A15:
+               return KVM_ARM_TARGET_CORTEX_A15;

I have this code already in perf_event.c. Can we move it somewhere common and share it? You should also check that the implementor field is 0x41.

Quoted 86 lines
arch/arm/kvm/arm.c new file +86 -0
+       default:
+               return -EINVAL;
+       }
+}
+
+int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
+{
+       return 0;
+}
+
+void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)
+{
+}
+
+void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
+}
+
+void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
+{
+}
+
+int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
+                                       struct kvm_guest_debug *dbg)
+{
+       return -EINVAL;
+}
+
+
+int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
+                                   struct kvm_mp_state *mp_state)
+{
+       return -EINVAL;
+}
+
+int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
+                                   struct kvm_mp_state *mp_state)
+{
+       return -EINVAL;
+}
+
+int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
+{
+       return 0;
+}
+
+int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v)
+{
+       return 0;
+}
+
+int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+       return -EINVAL;
+}
+
+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;

arch/arm/kvm/arm.c line 306

Quoted 10 lines
arch/arm/kvm/arm.c new file +10 -0
+
+               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))
+                       return -EFAULT;
+               if (n < reg_list.n)
+                       return -E2BIG;
+               return kvm_arm_copy_reg_indices(vcpu, user_list->reg);

kvm_reg_list sounds like it could be done using a regset instead.

Quoted 120 lines
arch/arm/kvm/emulate.c new file +114 -0
new file mode 100644
index 0000000..690bbb3
@@ -0,0 +1,127 @@
+/*
+ * 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 REG_OFFSET(_reg) \
+       (offsetof(struct kvm_regs, _reg) / sizeof(u32))
+
+#define USR_REG_OFFSET(_num) REG_OFFSET(usr_regs[_num])
+
+static const unsigned long vcpu_reg_offsets[MODE_SYS + 1][16] = {
+       /* 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[1]), /* 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 */
+               REG_OFFSET(pc)           /* r15 */
+       },
+
+       /* 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 */
+               REG_OFFSET(pc)           /* r15 */
+       },
+
+       /* 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 */
+               REG_OFFSET(pc)           /* r15 */
+       },
+
+       /* 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 */
+               REG_OFFSET(pc)           /* r15 */
+       },
+
+       /* 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 */
+               REG_OFFSET(pc)           /* r15 */
+       },
+
+       /* USR 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(usr_regs[13]), /* r13 */
+               REG_OFFSET(usr_regs[14]), /* r14 */
+               REG_OFFSET(pc)            /* r15 */
+       },
+
+       /* 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),
+               REG_OFFSET(usr_regs[13]), /* r13 */
+               REG_OFFSET(usr_regs[14]), /* r14 */
+               REG_OFFSET(pc)            /* r15 */
+       },
+};
+

arch/arm/kvm/emulate.c line 124

Quoted 10 lines
arch/arm/kvm/emulate.c new file +10 -0
+/*
+ * Return a pointer to the register number valid in the specified mode of
+ * the virtual CPU.
+ */
+u32 *vcpu_reg_mode(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode)
+{
+       u32 *reg_array = (u32 *)&vcpu->arch.regs;
+
+       BUG_ON(reg_num > 15);
+       BUG_ON(mode > MODE_SYS);

Again, BUG_ON seems a bit OTT here. Also, this is where the mode => idx calculation should happen.

Quoted 19 lines
arch/arm/kvm/emulate.c new file +2 -0
+       return reg_array + vcpu_reg_offsets[mode][reg_num];
+}
arch/arm/kvm/exports.c new file +11 -0
new file mode 100644
index 0000000..3e38c95
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

arch/arm/kvm/exports.c line 21

Quoted 10 lines
arch/arm/kvm/exports.c new file +10 -0
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/module.h>
+
+EXPORT_SYMBOL_GPL(smp_send_reschedule);

Erm...

We already have arch/arm/kernel/armksyms.c for exports -- please use that. However, exporting such low-level operations sounds like a bad idea. How realistic is kvm-as-a-module on ARM anyway?

Quoted 94 lines
arch/arm/kvm/guest.c new file +88 -0
new file mode 100644
index 0000000..19a5389
@@ -0,0 +1,211 @@
+/*
+ * 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/errno.h>
+#include <linux/err.h>
+#include <linux/kvm_host.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <asm/kvm.h>
+#include <asm/kvm_asm.h>
+#include <asm/kvm_emulate.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 }
+
+struct kvm_stats_debugfs_item debugfs_entries[] = {
+       { NULL }
+};
+
+int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
+{
+       return 0;
+}
+
+static u64 core_reg_offset_from_id(u64 id)
+{
+       return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);
+}
+
+static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+       u32 __user *uaddr = (u32 __user *)(long)reg->addr;
+       struct kvm_regs *regs = &vcpu->arch.regs;
+       u64 off;
+
+       if (KVM_REG_SIZE(reg->id) != 4)
+               return -ENOENT;
+
+       /* Our ID is an index into the kvm_regs struct. */
+       off = core_reg_offset_from_id(reg->id);
+       if (off >= sizeof(*regs) / KVM_REG_SIZE(reg->id))
+               return -ENOENT;
+
+       return put_user(((u32 *)regs)[off], uaddr);
+}
+
+static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+       u32 __user *uaddr = (u32 __user *)(long)reg->addr;
+       struct kvm_regs *regs = &vcpu->arch.regs;
+       u64 off, val;
+
+       if (KVM_REG_SIZE(reg->id) != 4)
+               return -ENOENT;
+
+       /* Our ID is an index into the kvm_regs struct. */
+       off = core_reg_offset_from_id(reg->id);
+       if (off >= sizeof(*regs) / KVM_REG_SIZE(reg->id))
+               return -ENOENT;
+
+       if (get_user(val, uaddr) != 0)
+               return -EFAULT;
+
+       if (off == KVM_REG_ARM_CORE_REG(cpsr)) {
+               if (__vcpu_mode(val) == 0xf)
+                       return -EINVAL;
+       }
+
+       ((u32 *)regs)[off] = val;
+       return 0;
+}

arch/arm/kvm/guest.c line 98

Quoted 10 lines
arch/arm/kvm/guest.c new file +10 -0
+
+int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+{
+       return -EINVAL;
+}
+
+int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+{
+       return -EINVAL;
+}

Again, all looks like this should be implemented using regsets from what I can tell.

Quoted 70 lines
arch/arm/kvm/reset.c new file +64 -0
new file mode 100644
index 0000000..290a13d
@@ -0,0 +1,74 @@
+/*
+ * 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/compiler.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kvm_host.h>
+#include <linux/kvm.h>
+
+#include <asm/unified.h>
+#include <asm/ptrace.h>
+#include <asm/cputype.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_coproc.h>
+
+/******************************************************************************
+ * Cortex-A15 Reset Values
+ */
+
+static const int a15_max_cpu_idx = 3;
+
+static struct kvm_regs a15_regs_reset = {
+       .cpsr = SVC_MODE | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT,
+};
+
+
+/*******************************************************************************
+ * Exported reset function
+ */
+
+/**
+ * kvm_reset_vcpu - sets core registers and cp15 registers to reset value
+ * @vcpu: The VCPU pointer
+ *
+ * This function finds the right table above and sets the registers on the
+ * virtual CPU struct to their architectually defined reset values.
+ */
+int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
+{
+       struct kvm_regs *cpu_reset;
+
+       switch (vcpu->arch.target) {
+       case KVM_ARM_TARGET_CORTEX_A15:
+               if (vcpu->vcpu_id > a15_max_cpu_idx)
+                       return -EINVAL;
+               cpu_reset = &a15_regs_reset;
+               vcpu->arch.midr = read_cpuid_id();
+               break;
+       default:
+               return -ENODEV;

arch/arm/kvm/reset.c line 74

Quoted 10 lines
arch/arm/kvm/reset.c new file +10 -0
+       }
+
+       /* Reset core registers */
+       memcpy(&vcpu->arch.regs, cpu_reset, sizeof(vcpu->arch.regs));
+
+       /* Reset CP15 registers */
+       kvm_reset_coprocs(vcpu);
+
+       return 0;
+}

This is a nice way to plug in new CPUs but the way the rest of the code is currently written, all the ARMv7 and Cortex-A15 code is merged together. I *strongly* suggest you isolate this from the start, as it will help you see what is architected and what is implementation-specific.

Will

#30 lore raw

Re: [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

Quoted 87 lines · Will Deacon
Quoted 10 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.

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

[...]

Quoted 37 lines
arch/arm/include/asm/kvm.h new file +31 -0
new file mode 100644
index 0000000..a13b582
@@ -0,0 +1,88 @@
+/*
+ * 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>
+
+#define __KVM_HAVE_GUEST_DEBUG
+
+#define KVM_REG_SIZE(id)                                               \
+       (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT))
+
+struct kvm_regs {
+       __u32 usr_regs[15];     /* R0_usr - R14_usr */
+       __u32 svc_regs[3];      /* SP_svc, LR_svc, SPSR_svc */

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

Quoted 10 lines
arch/arm/include/asm/kvm.h new file +10 -0
+       __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 */
+       __u32 pc;               /* The program counter (r15) */
+       __u32 cpsr;             /* The guest CPSR */
+};
+
+/* Supported Processor Types */
+#define KVM_ARM_TARGET_CORTEX_A15      (0xC0F)

So there's this define...

Quoted 23 lines
arch/arm/include/asm/kvm_arm.h new file +17 -0
new file mode 100644
index 0000000..2f9d28e
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
Quoted 9 lines

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

Quoted 6 lines
arch/arm/include/asm/kvm_arm.h new file +6 -0
+
+#ifndef __ARM_KVM_ARM_H__
+#define __ARM_KVM_ARM_H__
+
+/* Supported Processor Types */
+#define CORTEX_A15     (0xC0F)

... and then this one. Do we really need both?

no, we don't

Quoted 6 lines

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

Quoted 2 lines
arch/arm/include/asm/kvm_arm.h new file +2 -0
+/* Multiprocessor Affinity Register */
+#define MPIDR_CPUID    (0x3 << 0)

I'm fairly sure we already have code under arch/arm/ for dealing with the mpidr. Let's re-use that rather than reinventing it here.

I see some defines in topology.c - do you want some of these factored out into a header file that we can then also use from kvm? If so, where?

Quoted 29 lines
Quoted 29 lines
arch/arm/include/asm/kvm_asm.h new file +23 -0
new file mode 100644
index 0000000..44591f9
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __ARM_KVM_ASM_H__
+#define __ARM_KVM_ASM_H__
+
+#define ARM_EXCEPTION_RESET      0
+#define ARM_EXCEPTION_UNDEFINED   1
Quoted 8 lines

arch/arm/include/asm/kvm_asm.h line 28

Quoted 5 lines
arch/arm/include/asm/kvm_asm.h new file +5 -0
+#define ARM_EXCEPTION_SOFTWARE    2
+#define ARM_EXCEPTION_PREF_ABORT  3
+#define ARM_EXCEPTION_DATA_ABORT  4
+#define ARM_EXCEPTION_IRQ        5
+#define ARM_EXCEPTION_FIQ        6

Again, you have these defines (which look more suited to an enum type), but later (in kvm_host.h) you have:

well, unless I miss some known trick, assembly code doesn't like enums?

Quoted 13 lines

arch/arm/include/asm/kvm_asm.h

Quoted 10 lines
arch/arm/include/asm/kvm_asm.h new file +9 -0
+#define EXCEPTION_NONE      0
+#define EXCEPTION_RESET     0x80
+#define EXCEPTION_UNDEFINED 0x40
+#define EXCEPTION_SOFTWARE  0x20
+#define EXCEPTION_PREFETCH  0x10
+#define EXCEPTION_DATA      0x08
+#define EXCEPTION_IMPRECISE 0x04
+#define EXCEPTION_IRQ       0x02
+#define EXCEPTION_FIQ       0x01

Why the noise?

these are simply cruft from a previous life of KVM/ARM.

Quoted 44 lines
Quoted 44 lines
arch/arm/include/asm/kvm_emulate.h new file +38 -0
new file mode 100644
index 0000000..9e29335
@@ -0,0 +1,108 @@
+/*
+ * 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_EMULATE_H__
+#define __ARM_KVM_EMULATE_H__
+
+#include <linux/kvm_host.h>
+#include <asm/kvm_asm.h>
+
+u32 *vcpu_reg_mode(struct kvm_vcpu *vcpu, u8 reg_num, enum vcpu_mode mode);
+
+static inline u8 __vcpu_mode(u32 cpsr)
+{
+       u8 modes_table[32] = {
+               0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
+               0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
+               MODE_USR,       /* 0x0 */
+               MODE_FIQ,       /* 0x1 */
+               MODE_IRQ,       /* 0x2 */
+               MODE_SVC,       /* 0x3 */
+               0xf, 0xf, 0xf,
+               MODE_ABT,       /* 0x7 */
+               0xf, 0xf, 0xf,
Quoted 8 lines

arch/arm/include/asm/kvm_emulate.h line 42

Quoted 4 lines
arch/arm/include/asm/kvm_emulate.h new file +4 -0
+               MODE_UND,       /* 0xb */
+               0xf, 0xf, 0xf,
+               MODE_SYS        /* 0xf */
+       };

These MODE_* definitions sound like our *_MODE definitions... except they're not. It would probably be far more readable as a switch, but at least change the name of those things!

fair enough, they're renamed to VCPU_XXX_MODE

Quoted 10 lines

arch/arm/include/asm/kvm_emulate.h line 52

Quoted 6 lines
arch/arm/include/asm/kvm_emulate.h new file +6 -0
+static inline enum vcpu_mode vcpu_mode(struct kvm_vcpu *vcpu)
+{
+       u8 mode = __vcpu_mode(vcpu->arch.regs.cpsr);
+       BUG_ON(mode == 0xf);
+       return mode;
+}

I noticed that you have a fair few BUG_ONs throughout the series. Fair enough, but for hyp code is that really the right thing to do? Killing the guest could make more sense, perhaps?

the idea is to have BUG_ONs that are indeed BUG_ONs that we want to catch explicitly on the host. We have had a pass over the code to change all the BUG_ONs that can be provoked by the guest and inject the proper exceptions into the guest in this case. If you find places where this is not the case, it should be changed, and do let me know.

Quoted 9 lines

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

Quoted 5 lines
arch/arm/include/asm/kvm_emulate.h new file +4 -0
+static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu)
+{
+       return vcpu_reg(vcpu, 15);
+}

If you stick a struct pt_regs into struct kvm_regs, you could reuse ARM_pc here etc.

I prefer not to, because we'd have those registers presumably for usr mode and then we only define the others explicit. I think it's much clearer to look at kvm_regs today.

Quoted 24 lines
Quoted 24 lines
arch/arm/include/asm/kvm_host.h new file +18 -0
new file mode 100644
index 0000000..24959f4
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
Quoted 9 lines

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

Quoted 6 lines
arch/arm/include/asm/kvm_host.h new file +6 -0
+#ifndef __ARM_KVM_HOST_H__
+#define __ARM_KVM_HOST_H__
+
+#include <asm/kvm.h>
+
+#define KVM_MAX_VCPUS 4

NR_CPUS?

well this is defined by KVM generic code, and is common for other architecture.

Quoted 9 lines

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

Quoted 5 lines
arch/arm/include/asm/kvm_host.h new file +5 -0
+#define KVM_MEMORY_SLOTS 32
+#define KVM_PRIVATE_MEM_SLOTS 4
+#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
+
+#define NUM_FEATURES 0

Ha! No idea what that means, but hopefully there's less code to review because of it :)

that's actually true.

will rename to KVM_VCPU_MAX_FEATURES
(or do you want NR in this case? :-\)

Quoted 59 lines
Quoted 59 lines
arch/arm/include/asm/kvm_host.h new file +59 -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 {
+       /* The VMID generation used for the virt. memory system */
+       u64    vmid_gen;
+       u32    vmid;
+
+       /* 1-level 2nd stage table and lock */
+       spinlock_t pgd_lock;
+       pgd_t *pgd;
+
+       /* VTTBR value associated with above pgd and vmid */
+       u64    vttbr;
+};
+
+#define EXCEPTION_NONE      0
+#define EXCEPTION_RESET     0x80
+#define EXCEPTION_UNDEFINED 0x40
+#define EXCEPTION_SOFTWARE  0x20
+#define EXCEPTION_PREFETCH  0x10
+#define EXCEPTION_DATA      0x08
+#define EXCEPTION_IMPRECISE 0x04
+#define EXCEPTION_IRQ       0x02
+#define EXCEPTION_FIQ       0x01
+
+#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];
+};
+
+/*
+ * Modes used for short-hand mode determinition in the world-switch code and
+ * in emulation code.
+ *
+ * Note: These indices do NOT correspond to the value of the CPSR mode bits!
+ */
+enum vcpu_mode {
+       MODE_FIQ = 0,
+       MODE_IRQ,
+       MODE_SVC,
+       MODE_ABT,
+       MODE_UND,
+       MODE_USR,
Quoted 9 lines

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

Quoted 2 lines
arch/arm/include/asm/kvm_host.h new file +2 -0
+       MODE_SYS
+};

So the need for this enum is for indexing the array of modes, right? But accesses to that array are already hidden behind an accessor function from what I can tell, so I'd rather the arithmetic from cpsr -> index was restricted to that function and the rest of the code just passed either the raw mode or the full cpsr around.

good point, this was really useful in that prior life of kvm/arm where we did a bunch of emulation and decoding all over the place. I'll send out a v2 with this reworked.

Quoted 211 lines
Quoted 211 lines
arch/arm/kvm/arm.c new file +205 -0
new file mode 100644
index 0000000..fd6fa9b
@@ -0,0 +1,345 @@
+/*
+ * 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/errno.h>
+#include <linux/err.h>
+#include <linux/kvm_host.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/mman.h>
+#include <linux/sched.h>
+#include <trace/events/kvm.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+#include <asm/unified.h>
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/mman.h>
+#include <asm/cputype.h>
+
+#ifdef REQUIRES_VIRT
+__asm__(".arch_extension       virt");
+#endif
+
+int kvm_arch_hardware_enable(void *garbage)
+{
+       return 0;
+}
+
+int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
+}
+
+void kvm_arch_hardware_disable(void *garbage)
+{
+}
+
+int kvm_arch_hardware_setup(void)
+{
+       return 0;
+}
+
+void kvm_arch_hardware_unsetup(void)
+{
+}
+
+void kvm_arch_check_processor_compat(void *rtn)
+{
+       *(int *)rtn = 0;
+}
+
+void kvm_arch_sync_events(struct kvm *kvm)
+{
+}
+
+int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
+{
+       if (type)
+               return -EINVAL;
+
+       return 0;
+}
+
+int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
+{
+       return VM_FAULT_SIGBUS;
+}
+
+void kvm_arch_free_memslot(struct kvm_memory_slot *free,
+                          struct kvm_memory_slot *dont)
+{
+}
+
+int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)
+{
+       return 0;
+}
+
+void kvm_arch_destroy_vm(struct kvm *kvm)
+{
+       int i;
+
+       for (i = 0; i < KVM_MAX_VCPUS; ++i) {
+               if (kvm->vcpus[i]) {
+                       kvm_arch_vcpu_free(kvm->vcpus[i]);
+                       kvm->vcpus[i] = NULL;
+               }
+       }
+}
+
+int kvm_dev_ioctl_check_extension(long ext)
+{
+       int r;
+       switch (ext) {
+       case KVM_CAP_USER_MEMORY:
+       case KVM_CAP_DESTROY_MEMORY_REGION_WORKS:
+       case KVM_CAP_ONE_REG:
+               r = 1;
+               break;
+       case KVM_CAP_COALESCED_MMIO:
+               r = KVM_COALESCED_MMIO_PAGE_OFFSET;
+               break;
+       default:
+               r = 0;
+               break;
+       }
+       return r;
+}
+
+long kvm_arch_dev_ioctl(struct file *filp,
+                       unsigned int ioctl, unsigned long arg)
+{
+       return -EINVAL;
+}
+
+int kvm_arch_set_memory_region(struct kvm *kvm,
+                              struct kvm_userspace_memory_region *mem,
+                              struct kvm_memory_slot old,
+                              int user_alloc)
+{
+       return 0;
+}
+
+int kvm_arch_prepare_memory_region(struct kvm *kvm,
+                                  struct kvm_memory_slot *memslot,
+                                  struct kvm_memory_slot old,
+                                  struct kvm_userspace_memory_region *mem,
+                                  int user_alloc)
+{
+       return 0;
+}
+
+void kvm_arch_commit_memory_region(struct kvm *kvm,
+                                  struct kvm_userspace_memory_region *mem,
+                                  struct kvm_memory_slot old,
+                                  int user_alloc)
+{
+}
+
+void kvm_arch_flush_shadow_all(struct kvm *kvm)
+{
+}
+
+void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
+                                  struct kvm_memory_slot *slot)
+{
+}
+
+struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
+{
+       int err;
+       struct kvm_vcpu *vcpu;
+
+       vcpu = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL);
+       if (!vcpu) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       err = kvm_vcpu_init(vcpu, kvm, id);
+       if (err)
+               goto free_vcpu;
+
+       return vcpu;
+free_vcpu:
+       kmem_cache_free(kvm_vcpu_cache, vcpu);
+out:
+       return ERR_PTR(err);
+}
+
+void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
+{
+}
+
+void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
+{
+       kvm_arch_vcpu_free(vcpu);
+}
+
+int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
+{
+       return 0;
+}
+
+int __attribute_const__ kvm_target_cpu(void)
+{
+       unsigned int midr;
Quoted 9 lines

arch/arm/kvm/arm.c line 210

Quoted 5 lines
arch/arm/kvm/arm.c new file +5 -0
+
+       midr = read_cpuid_id();
+       switch ((midr >> 4) & 0xfff) {
+       case KVM_ARM_TARGET_CORTEX_A15:
+               return KVM_ARM_TARGET_CORTEX_A15;

I have this code already in perf_event.c. Can we move it somewhere common and share it? You should also check that the implementor field is 0x41.

by all means, you can probably suggest a good place better than I can...

Quoted 265 lines
Quoted 86 lines
arch/arm/kvm/arm.c new file +86 -0
+       default:
+               return -EINVAL;
+       }
+}
+
+int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
+{
+       return 0;
+}
+
+void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)
+{
+}
+
+void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
+}
+
+void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
+{
+}
+
+int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
+                                       struct kvm_guest_debug *dbg)
+{
+       return -EINVAL;
+}
+
+
+int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
+                                   struct kvm_mp_state *mp_state)
+{
+       return -EINVAL;
+}
+
+int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
+                                   struct kvm_mp_state *mp_state)
+{
+       return -EINVAL;
+}
+
+int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
+{
+       return 0;
+}
+
+int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v)
+{
+       return 0;
+}
+
+int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+       return -EINVAL;
+}
+
+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;

arch/arm/kvm/arm.c line 306

Quoted 10 lines
arch/arm/kvm/arm.c new file +10 -0
+
+               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))
+                       return -EFAULT;
+               if (n < reg_list.n)
+                       return -E2BIG;
+               return kvm_arm_copy_reg_indices(vcpu, user_list->reg);

kvm_reg_list sounds like it could be done using a regset instead.

Quoted 120 lines
arch/arm/kvm/emulate.c new file +114 -0
new file mode 100644
index 0000000..690bbb3
@@ -0,0 +1,127 @@
+/*
+ * 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 REG_OFFSET(_reg) \
+       (offsetof(struct kvm_regs, _reg) / sizeof(u32))
+
+#define USR_REG_OFFSET(_num) REG_OFFSET(usr_regs[_num])
+
+static const unsigned long vcpu_reg_offsets[MODE_SYS + 1][16] = {
+       /* 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[1]), /* 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 */
+               REG_OFFSET(pc)           /* r15 */
+       },
+
+       /* 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 */
+               REG_OFFSET(pc)           /* r15 */
+       },
+
+       /* 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 */
+               REG_OFFSET(pc)           /* r15 */
+       },
+
+       /* 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 */
+               REG_OFFSET(pc)           /* r15 */
+       },
+
+       /* 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 */
+               REG_OFFSET(pc)           /* r15 */
+       },
+
+       /* USR 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(usr_regs[13]), /* r13 */
+               REG_OFFSET(usr_regs[14]), /* r14 */
+               REG_OFFSET(pc)            /* r15 */
+       },
+
+       /* 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),
+               REG_OFFSET(usr_regs[13]), /* r13 */
+               REG_OFFSET(usr_regs[14]), /* r14 */
+               REG_OFFSET(pc)            /* r15 */
+       },
+};
+

arch/arm/kvm/emulate.c line 124

Quoted 10 lines
arch/arm/kvm/emulate.c new file +10 -0
+/*
+ * Return a pointer to the register number valid in the specified mode of
+ * the virtual CPU.
+ */
+u32 *vcpu_reg_mode(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode)
+{
+       u32 *reg_array = (u32 *)&vcpu->arch.regs;
+
+       BUG_ON(reg_num > 15);
+       BUG_ON(mode > MODE_SYS);

Again, BUG_ON seems a bit OTT here. Also, this is where the mode => idx calculation should happen.

Quoted 19 lines
arch/arm/kvm/emulate.c new file +2 -0
+       return reg_array + vcpu_reg_offsets[mode][reg_num];
+}
arch/arm/kvm/exports.c new file +11 -0
new file mode 100644
index 0000000..3e38c95
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

arch/arm/kvm/exports.c line 21

Quoted 10 lines
arch/arm/kvm/exports.c new file +10 -0
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/module.h>
+
+EXPORT_SYMBOL_GPL(smp_send_reschedule);

Erm...

Quoted 4 lines

We already have arch/arm/kernel/armksyms.c for exports -- please use that. However, exporting such low-level operations sounds like a bad idea. How realistic is kvm-as-a-module on ARM anyway?

at this point it's broken, so I'll just remove this and leave this for a fun project for some poor soul at some point if anyone ever needs half the code outside the kernel as a module (the other half needs to be compiled in anyway)

Quoted 99 lines
Quoted 99 lines
arch/arm/kvm/guest.c new file +93 -0
new file mode 100644
index 0000000..19a5389
@@ -0,0 +1,211 @@
+/*
+ * 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/errno.h>
+#include <linux/err.h>
+#include <linux/kvm_host.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <asm/kvm.h>
+#include <asm/kvm_asm.h>
+#include <asm/kvm_emulate.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 }
+
+struct kvm_stats_debugfs_item debugfs_entries[] = {
+       { NULL }
+};
+
+int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
+{
+       return 0;
+}
+
+static u64 core_reg_offset_from_id(u64 id)
+{
+       return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);
+}
+
+static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+       u32 __user *uaddr = (u32 __user *)(long)reg->addr;
+       struct kvm_regs *regs = &vcpu->arch.regs;
+       u64 off;
+
+       if (KVM_REG_SIZE(reg->id) != 4)
+               return -ENOENT;
+
+       /* Our ID is an index into the kvm_regs struct. */
+       off = core_reg_offset_from_id(reg->id);
+       if (off >= sizeof(*regs) / KVM_REG_SIZE(reg->id))
+               return -ENOENT;
+
+       return put_user(((u32 *)regs)[off], uaddr);
+}
+
+static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+       u32 __user *uaddr = (u32 __user *)(long)reg->addr;
+       struct kvm_regs *regs = &vcpu->arch.regs;
+       u64 off, val;
+
+       if (KVM_REG_SIZE(reg->id) != 4)
+               return -ENOENT;
+
+       /* Our ID is an index into the kvm_regs struct. */
+       off = core_reg_offset_from_id(reg->id);
+       if (off >= sizeof(*regs) / KVM_REG_SIZE(reg->id))
+               return -ENOENT;
+
+       if (get_user(val, uaddr) != 0)
+               return -EFAULT;
+
+       if (off == KVM_REG_ARM_CORE_REG(cpsr)) {
+               if (__vcpu_mode(val) == 0xf)
+                       return -EINVAL;
+       }
+
+       ((u32 *)regs)[off] = val;
+       return 0;
+}
+
+int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+{
+       return -EINVAL;
+}
Quoted 9 lines

arch/arm/kvm/guest.c line 98

Quoted 5 lines
arch/arm/kvm/guest.c new file +5 -0
+
+int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+{
+       return -EINVAL;
+}

Again, all looks like this should be implemented using regsets from what I can tell.

this API has been discussed to death on the KVM lists, and we can of course revive that if the regset makes it nicer - I'd prefer getting this upstream the way that it is now though, where GET_REG / SET_REG seems to be the way forward from a KVM perspective.

Quoted 77 lines
Quoted 77 lines
arch/arm/kvm/reset.c new file +71 -0
new file mode 100644
index 0000000..290a13d
@@ -0,0 +1,74 @@
+/*
+ * 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/compiler.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kvm_host.h>
+#include <linux/kvm.h>
+
+#include <asm/unified.h>
+#include <asm/ptrace.h>
+#include <asm/cputype.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_coproc.h>
+
+/******************************************************************************
+ * Cortex-A15 Reset Values
+ */
+
+static const int a15_max_cpu_idx = 3;
+
+static struct kvm_regs a15_regs_reset = {
+       .cpsr = SVC_MODE | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT,
+};
+
+
+/*******************************************************************************
+ * Exported reset function
+ */
+
+/**
+ * kvm_reset_vcpu - sets core registers and cp15 registers to reset value
+ * @vcpu: The VCPU pointer
+ *
+ * This function finds the right table above and sets the registers on the
+ * virtual CPU struct to their architectually defined reset values.
+ */
+int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
+{
+       struct kvm_regs *cpu_reset;
+
+       switch (vcpu->arch.target) {
+       case KVM_ARM_TARGET_CORTEX_A15:
+               if (vcpu->vcpu_id > a15_max_cpu_idx)
+                       return -EINVAL;
+               cpu_reset = &a15_regs_reset;
+               vcpu->arch.midr = read_cpuid_id();
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       /* Reset core registers */
+       memcpy(&vcpu->arch.regs, cpu_reset, sizeof(vcpu->arch.regs));
+
+       /* Reset CP15 registers */
+       kvm_reset_coprocs(vcpu);
Quoted 9 lines

arch/arm/kvm/reset.c line 74

Quoted 3 lines
arch/arm/kvm/reset.c new file +3 -0
+
+       return 0;
+}

This is a nice way to plug in new CPUs but the way the rest of the code is currently written, all the ARMv7 and Cortex-A15 code is merged together. I *strongly* suggest you isolate this from the start, as it will help you see what is architected and what is implementation-specific.

not entirely sure what you mean. You want a separate coproc.c file for Cortex-A15 specific stuff like coproc_a15.c?

Thanks a bunch for the review!
-Christoffer

#31 lore raw

Re: [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

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

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

Quoted 2 lines
arch/arm/include/asm/kvm_arm.h new file +2 -0
+/* Multiprocessor Affinity Register */
+#define MPIDR_CPUID    (0x3 << 0)

I'm fairly sure we already have code under arch/arm/ for dealing with the mpidr. Let's re-use that rather than reinventing it here.

I see some defines in topology.c - do you want some of these factored out into a header file that we can then also use from kvm? If so, where?

I guess either in topology.h or a new header (topology-bits.h).

Quoted 13 lines
Quoted 12 lines

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

Quoted 9 lines
arch/arm/include/asm/kvm_host.h new file +9 -0
+#define EXCEPTION_NONE      0
+#define EXCEPTION_RESET     0x80
+#define EXCEPTION_UNDEFINED 0x40
+#define EXCEPTION_SOFTWARE  0x20
+#define EXCEPTION_PREFETCH  0x10
+#define EXCEPTION_DATA      0x08
+#define EXCEPTION_IMPRECISE 0x04
+#define EXCEPTION_IRQ       0x02
+#define EXCEPTION_FIQ       0x01

Why the noise?

Quoted 1 line

these are simply cruft from a previous life of KVM/ARM.

Ok, then please get rid of them.

Quoted 11 lines
Quoted 10 lines

arch/arm/include/asm/kvm_emulate.h line 52

Quoted 6 lines
arch/arm/include/asm/kvm_emulate.h new file +6 -0
+static inline enum vcpu_mode vcpu_mode(struct kvm_vcpu *vcpu)
+{
+       u8 mode = __vcpu_mode(vcpu->arch.regs.cpsr);
+       BUG_ON(mode == 0xf);
+       return mode;
+}

I noticed that you have a fair few BUG_ONs throughout the series. Fair enough, but for hyp code is that really the right thing to do? Killing the guest could make more sense, perhaps?

Quoted 5 lines

the idea is to have BUG_ONs that are indeed BUG_ONs that we want to catch explicitly on the host. We have had a pass over the code to change all the BUG_ONs that can be provoked by the guest and inject the proper exceptions into the guest in this case. If you find places where this is not the case, it should be changed, and do let me know.

Ok, so are you saying that a BUG_ON due to some detected inconsistency with one guest may not necessarily terminate the other guests? BUG_ONs in the host seem like a bad idea if the host is able to continue with a subset of guests.

Quoted 10 lines
Quoted 9 lines

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

Quoted 5 lines
arch/arm/include/asm/kvm_emulate.h new file +4 -0
+static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu)
+{
+       return vcpu_reg(vcpu, 15);
+}

If you stick a struct pt_regs into struct kvm_regs, you could reuse ARM_pc here etc.

Quoted 3 lines

I prefer not to, because we'd have those registers presumably for usr mode and then we only define the others explicit. I think it's much clearer to look at kvm_regs today.

I disagree and think that you should reuse as much of the arch/arm/ code as possible. Not only does it make it easier to read by people who are familiar with that code (and in turn get you more reviewers) but it also means that we limit the amount of duplication that we have.

I think Marc (CC'd) had a go at this with some success.

Quoted 10 lines
Quoted 9 lines

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

Quoted 6 lines
arch/arm/include/asm/kvm_host.h new file +6 -0
+#ifndef __ARM_KVM_HOST_H__
+#define __ARM_KVM_HOST_H__
+
+#include <asm/kvm.h>
+
+#define KVM_MAX_VCPUS 4

NR_CPUS?

Quoted 2 lines

well this is defined by KVM generic code, and is common for other architecture.

I mean #define KVM_MAX_CPUS NR_CPUS. The 4 seems arbitrary.

Quoted 13 lines
Quoted 12 lines

arch/arm/kvm/arm.c line 210

Quoted 8 lines
arch/arm/kvm/arm.c new file +8 -0
+int __attribute_const__ kvm_target_cpu(void)
+{
+       unsigned int midr;
+
+       midr = read_cpuid_id();
+       switch ((midr >> 4) & 0xfff) {
+       case KVM_ARM_TARGET_CORTEX_A15:
+               return KVM_ARM_TARGET_CORTEX_A15;

I have this code already in perf_event.c. Can we move it somewhere common and share it? You should also check that the implementor field is 0x41.

Quoted 1 line

by all means, you can probably suggest a good place better than I can...

cputype.h?

Quoted 11 lines
Quoted 10 lines

arch/arm/kvm/exports.c line 21

Quoted 3 lines
arch/arm/kvm/exports.c new file +3 -0
+#include <linux/module.h>
+
+EXPORT_SYMBOL_GPL(smp_send_reschedule);

Erm...

We already have arch/arm/kernel/armksyms.c for exports -- please use that. However, exporting such low-level operations sounds like a bad idea. How realistic is kvm-as-a-module on ARM anyway?

Quoted 4 lines

at this point it's broken, so I'll just remove this and leave this for a fun project for some poor soul at some point if anyone ever needs half the code outside the kernel as a module (the other half needs to be compiled in anyway)

Ok, that suits me. If it's broken, let's not include it in the initial submission.

Quoted 14 lines
Quoted 4 lines
Quoted 4 lines
arch/arm/kvm/guest.c new file +4 -0
+int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+{
+       return -EINVAL;
+}
Quoted 9 lines

arch/arm/kvm/guest.c line 98

Quoted 5 lines
arch/arm/kvm/guest.c new file +5 -0
+
+int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+{
+       return -EINVAL;
+}

Again, all looks like this should be implemented using regsets from what I can tell.

Quoted 4 lines

this API has been discussed to death on the KVM lists, and we can of course revive that if the regset makes it nicer - I'd prefer getting this upstream the way that it is now though, where GET_REG / SET_REG seems to be the way forward from a KVM perspective.

I'm sure the API has been discussed, but I've not seen that conversation and I'm also not aware about whether or not regsets were considered as a possibility for this stuff. The advantages of using them are:

1. It's less code for the arch to implement (and most of what you need, you already have).

2. You can move the actual copying code into core KVM, like we have for ptrace.

3. New KVM ports (e.g. arm64) can reuse the core copying code easily.

Furthermore, some registers (typically) floating point and GPRs will already have regsets for the ptrace code, so that can be reused if you share the datatypes.

The big problem with getting things upstream and then changing it later is that you will break the ABI. I highly doubt that's feasible, so can we not just use regsets from the start for ARM?

Quoted 30 lines
Quoted 20 lines
Quoted 20 lines
arch/arm/kvm/reset.c new file +20 -0
+int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
+{
+       struct kvm_regs *cpu_reset;
+
+       switch (vcpu->arch.target) {
+       case KVM_ARM_TARGET_CORTEX_A15:
+               if (vcpu->vcpu_id > a15_max_cpu_idx)
+                       return -EINVAL;
+               cpu_reset = &a15_regs_reset;
+               vcpu->arch.midr = read_cpuid_id();
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       /* Reset core registers */
+       memcpy(&vcpu->arch.regs, cpu_reset, sizeof(vcpu->arch.regs));
+
+       /* Reset CP15 registers */
+       kvm_reset_coprocs(vcpu);
Quoted 9 lines

arch/arm/kvm/arm.c line 45

Quoted 3 lines
arch/arm/kvm/arm.c new file +3 -0
+
+       return 0;
+}

This is a nice way to plug in new CPUs but the way the rest of the code is currently written, all the ARMv7 and Cortex-A15 code is merged together. I *strongly* suggest you isolate this from the start, as it will help you see what is architected and what is implementation-specific.

Quoted 2 lines

not entirely sure what you mean. You want a separate coproc.c file for Cortex-A15 specific stuff like coproc_a15.c?

Indeed. I think it will make adding new CPUs a lot clearer and separate the architecture from the implementation.

Cheers,

Will

#32 lore raw

Re: [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

Quoted 16 lines · Will Deacon
Quoted 11 lines · Christoffer Dall
Quoted 9 lines · Will Deacon

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

Quoted 5 lines
arch/arm/include/asm/kvm_emulate.h new file +4 -0
+static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu)
+{
+       return vcpu_reg(vcpu, 15);
+}

If you stick a struct pt_regs into struct kvm_regs, you could reuse ARM_pc here etc.

Quoted 3 lines

I prefer not to, because we'd have those registers presumably for usr mode and then we only define the others explicit. I think it's much clearer to look at kvm_regs today.

Quoted 6 lines

I disagree and think that you should reuse as much of the arch/arm/ code as possible. Not only does it make it easier to read by people who are familiar with that code (and in turn get you more reviewers) but it also means that we limit the amount of duplication that we have.

I think Marc (CC'd) had a go at this with some success.

Yup, I have it converted already. It requires a number of changes, but I took this opportunity to do some other cleanup (world switch save/restore code, mostly).

Patches are at the top of mu kvm-cleanup branch.

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

Re: [kvmarm] [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

In reply to Will Deacon
Quoted 9 lines · Will Deacon
Quoted 4 lines · Christoffer Dall

this API has been discussed to death on the KVM lists, and we can of course revive that if the regset makes it nicer - I'd prefer getting this upstream the way that it is now though, where GET_REG / SET_REG seems to be the way forward from a KVM perspective.

I'm sure the API has been discussed, but I've not seen that conversation and I'm also not aware about whether or not regsets were considered as a possibility for this stuff.

Can you point me at some documentation for regsets? It's a bit difficult to have a sensible conversation about their suitability otherwise...

(The potentially tricky area to handle is the cp15 registers, where it's quite likely that new registers might be added later and so userspace has to be able to query the kernel for what is supported and possibly deal with the kernel reporting attempts to set read only bits within registers, etc. Using the same ABI for simpler cases like the GPRs and FP registers is then just a matter of being consistent in the interface we expose to userspace.)

-- PMM

#34 lore raw

Re: [kvmarm] [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

Quoted 7 lines · Peter Maydell
Quoted 6 lines · Will Deacon
Quoted 4 lines · Christoffer Dall

this API has been discussed to death on the KVM lists, and we can of course revive that if the regset makes it nicer - I'd prefer getting this upstream the way that it is now though, where GET_REG / SET_REG seems to be the way forward from a KVM perspective.

Quoted 7 lines
Quoted 3 lines

I'm sure the API has been discussed, but I've not seen that conversation and I'm also not aware about whether or not regsets were considered as a possibility for this stuff.

Can you point me at some documentation for regsets? It's a bit difficult to have a sensible conversation about their suitability otherwise...

The actual regset structure (struct user_regset) is internal to the kernel, so it's not very well documented. As far as userspace interaction goes, the usual method is via an iovec (see readv/writev) which is well documented, but of course the kvm ioctl would still need documenting. For ptrace, that's just a small paragraph in a user header:

/*
* Generic ptrace interface that exports the architecture specific regsets
* using the corresponding NT_* types (which are also used in the core dump).
* Please note that the NT_PRSTATUS note type in a core dump contains a full
* 'struct elf_prstatus'. But the user_regset for NT_PRSTATUS contains just the
* elf_gregset_t that is the pr_reg field of 'struct elf_prstatus'. For all the
* other user_regset flavors, the user_regset layout and the ELF core dump note
* payload are exactly the same layout.
*
* This interface usage is as follows:
* struct iovec iov = { buf, len};
*
* ret = ptrace(PTRACE_GETREGSET/PTRACE_SETREGSET, pid, NT_XXX_TYPE, &iov);
*
* On the successful completion, iov.len will be updated by the kernel,
* specifying how much the kernel has written/read to/from the user's iov.buf. */

but obviously you'd probably have something under Documentation/.

Quoted 7 lines

(The potentially tricky area to handle is the cp15 registers, where it's quite likely that new registers might be added later and so userspace has to be able to query the kernel for what is supported and possibly deal with the kernel reporting attempts to set read only bits within registers, etc. Using the same ABI for simpler cases like the GPRs and FP registers is then just a matter of being consistent in the interface we expose to userspace.)

You *could* split up cp15 into lots of regsets but realistically that's going to hurt down the line. GPR and FP would be good though.

Will

#35 lore raw

Re: [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

In reply to Will Deacon
Quoted 48 lines · Will Deacon
Quoted 10 lines · Christoffer Dall
Quoted 6 lines · Will Deacon

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

Quoted 2 lines
arch/arm/include/asm/kvm_arm.h new file +2 -0
+/* Multiprocessor Affinity Register */
+#define MPIDR_CPUID    (0x3 << 0)

I'm fairly sure we already have code under arch/arm/ for dealing with the mpidr. Let's re-use that rather than reinventing it here.

I see some defines in topology.c - do you want some of these factored out into a header file that we can then also use from kvm? If so, where?

I guess either in topology.h or a new header (topology-bits.h).

Quoted 13 lines
Quoted 12 lines

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

Quoted 9 lines
arch/arm/include/asm/kvm_host.h new file +9 -0
+#define EXCEPTION_NONE      0
+#define EXCEPTION_RESET     0x80
+#define EXCEPTION_UNDEFINED 0x40
+#define EXCEPTION_SOFTWARE  0x20
+#define EXCEPTION_PREFETCH  0x10
+#define EXCEPTION_DATA      0x08
+#define EXCEPTION_IMPRECISE 0x04
+#define EXCEPTION_IRQ       0x02
+#define EXCEPTION_FIQ       0x01

Why the noise?

Quoted 1 line

these are simply cruft from a previous life of KVM/ARM.

Ok, then please get rid of them.

Quoted 11 lines
Quoted 10 lines

arch/arm/include/asm/kvm_emulate.h line 52

Quoted 6 lines
arch/arm/include/asm/kvm_emulate.h new file +6 -0
+static inline enum vcpu_mode vcpu_mode(struct kvm_vcpu *vcpu)
+{
+       u8 mode = __vcpu_mode(vcpu->arch.regs.cpsr);
+       BUG_ON(mode == 0xf);
+       return mode;
+}

I noticed that you have a fair few BUG_ONs throughout the series. Fair enough, but for hyp code is that really the right thing to do? Killing the guest could make more sense, perhaps?

Quoted 5 lines

the idea is to have BUG_ONs that are indeed BUG_ONs that we want to catch explicitly on the host. We have had a pass over the code to change all the BUG_ONs that can be provoked by the guest and inject the proper exceptions into the guest in this case. If you find places where this is not the case, it should be changed, and do let me know.

Quoted 5 lines

Ok, so are you saying that a BUG_ON due to some detected inconsistency with one guest may not necessarily terminate the other guests? BUG_ONs in the host seem like a bad idea if the host is able to continue with a subset of guests.

No, I'm saying a BUG_ON is an actual BUG, it should not happen and there should be nowhere where a guest can cause a BUG_ON to occur in the host, because that would be a bug.

We basically never kill a guest unless really extreme things happen (like we cannot allocate a pte, in which case we return -ENOMEM). If a guest does something really weird, that guest will receive the appropriate exception (undefined, prefetch abort, ...)

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines

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

Quoted 5 lines
arch/arm/include/asm/kvm_emulate.h new file +4 -0
+static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu)
+{
+       return vcpu_reg(vcpu, 15);
+}

If you stick a struct pt_regs into struct kvm_regs, you could reuse ARM_pc here etc.

Quoted 8 lines
Quoted 3 lines

I prefer not to, because we'd have those registers presumably for usr mode and then we only define the others explicit. I think it's much clearer to look at kvm_regs today.

I disagree and think that you should reuse as much of the arch/arm/ code as possible. Not only does it make it easier to read by people who are familiar with that code (and in turn get you more reviewers) but it also means that we limit the amount of duplication that we have.

Reusing a struct just for the sake of reusing is not necessarily an improvement. Some times it complicates things, and some times it's misleading. To me, pt_regs carry the meaning that these are the registers for a user space process that traps into the kernel - in KVM we emulate a virtual CPU and that current definition is quite clear.

The argument that more people will review the code if the struct contains a pt_regs field rather than a usr_regs field is completely invalid, because I'm sure everyone that reviews virtualization code will know that user mode is a mode on the cpu and it has some registers and this is the state we store when we context switch a VM - pt_regs could be read as the regs that we stored in the mode that the VM happened to be in when we took an exception, which I would think is crazy, and probably not what you suggest.

Writing the literal 15 for the PC register is not really a problem in terms of duplication - it's nothing that requires separate maintenance.

At this point the priority should really be correctness, readability, and performance, imho.

Quoted 3 lines

I think Marc (CC'd) had a go at this with some success.

great, if this improves the code, then I suggest someone rebases an appropriate patch and sends it to the kvmarm mailing list so we can have a look at it, but there are users out there looking to try kvm/arm and we should try to give it to them.

Quoted 130 lines
Quoted 10 lines
Quoted 9 lines

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

Quoted 6 lines
arch/arm/include/asm/kvm_host.h new file +6 -0
+#ifndef __ARM_KVM_HOST_H__
+#define __ARM_KVM_HOST_H__
+
+#include <asm/kvm.h>
+
+#define KVM_MAX_VCPUS 4

NR_CPUS?

Quoted 2 lines

well this is defined by KVM generic code, and is common for other architecture.

I mean #define KVM_MAX_CPUS NR_CPUS. The 4 seems arbitrary.

Quoted 13 lines
Quoted 12 lines

arch/arm/kvm/arm.c line 210

Quoted 8 lines
arch/arm/kvm/arm.c new file +8 -0
+int __attribute_const__ kvm_target_cpu(void)
+{
+       unsigned int midr;
+
+       midr = read_cpuid_id();
+       switch ((midr >> 4) & 0xfff) {
+       case KVM_ARM_TARGET_CORTEX_A15:
+               return KVM_ARM_TARGET_CORTEX_A15;

I have this code already in perf_event.c. Can we move it somewhere common and share it? You should also check that the implementor field is 0x41.

Quoted 1 line

by all means, you can probably suggest a good place better than I can...

cputype.h?

Quoted 11 lines
Quoted 10 lines

arch/arm/kvm/exports.c line 21

Quoted 3 lines
arch/arm/kvm/exports.c new file +3 -0
+#include <linux/module.h>
+
+EXPORT_SYMBOL_GPL(smp_send_reschedule);

Erm...

We already have arch/arm/kernel/armksyms.c for exports -- please use that. However, exporting such low-level operations sounds like a bad idea. How realistic is kvm-as-a-module on ARM anyway?

Quoted 4 lines

at this point it's broken, so I'll just remove this and leave this for a fun project for some poor soul at some point if anyone ever needs half the code outside the kernel as a module (the other half needs to be compiled in anyway)

Ok, that suits me. If it's broken, let's not include it in the initial submission.

Quoted 14 lines
Quoted 4 lines
Quoted 4 lines
arch/arm/kvm/guest.c new file +4 -0
+int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+{
+       return -EINVAL;
+}
Quoted 9 lines

arch/arm/kvm/guest.c line 98

Quoted 5 lines
arch/arm/kvm/guest.c new file +5 -0
+
+int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+{
+       return -EINVAL;
+}

Again, all looks like this should be implemented using regsets from what I can tell.

Quoted 4 lines

this API has been discussed to death on the KVM lists, and we can of course revive that if the regset makes it nicer - I'd prefer getting this upstream the way that it is now though, where GET_REG / SET_REG seems to be the way forward from a KVM perspective.

I'm sure the API has been discussed, but I've not seen that conversation and I'm also not aware about whether or not regsets were considered as a possibility for this stuff. The advantages of using them are:

1. It's less code for the arch to implement (and most of what you need, you already have).

2. You can move the actual copying code into core KVM, like we have for ptrace.

3. New KVM ports (e.g. arm64) can reuse the core copying code easily.

Furthermore, some registers (typically) floating point and GPRs will already have regsets for the ptrace code, so that can be reused if you share the datatypes.

The big problem with getting things upstream and then changing it later is that you will break the ABI. I highly doubt that's feasible, so can we not just use regsets from the start for ARM?

Quoted 30 lines
Quoted 20 lines
Quoted 20 lines
arch/arm/kvm/reset.c new file +20 -0
+int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
+{
+       struct kvm_regs *cpu_reset;
+
+       switch (vcpu->arch.target) {
+       case KVM_ARM_TARGET_CORTEX_A15:
+               if (vcpu->vcpu_id > a15_max_cpu_idx)
+                       return -EINVAL;
+               cpu_reset = &a15_regs_reset;
+               vcpu->arch.midr = read_cpuid_id();
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       /* Reset core registers */
+       memcpy(&vcpu->arch.regs, cpu_reset, sizeof(vcpu->arch.regs));
+
+       /* Reset CP15 registers */
+       kvm_reset_coprocs(vcpu);
Quoted 9 lines

arch/arm/kvm/arm.c line 45

Quoted 3 lines
arch/arm/kvm/arm.c new file +3 -0
+
+       return 0;
+}

This is a nice way to plug in new CPUs but the way the rest of the code is currently written, all the ARMv7 and Cortex-A15 code is merged together. I *strongly* suggest you isolate this from the start, as it will help you see what is architected and what is implementation-specific.

Quoted 2 lines

not entirely sure what you mean. You want a separate coproc.c file for Cortex-A15 specific stuff like coproc_a15.c?

Indeed. I think it will make adding new CPUs a lot clearer and separate the architecture from the implementation.

Cheers,

Will

#36 lore raw

Re: [kvmarm] [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

Quoted 1 line · Christoffer Dall

On Thu, Sep 27, 2012 at 10:13 AM, Will Deacon <[email protected]>

Quoted 8 lines · wrote:
Quoted 3 lines

I think Marc (CC'd) had a go at this with some success.

great, if this improves the code, then I suggest someone rebases an appropriate patch and sends it to the kvmarm mailing list so we can have a look at it, but there are users out there looking to try kvm/arm and we should try to give it to them.

Incoming.

        M.
Signature · 1 line
Who you jivin' with that Cosmik Debris?
#37 lore raw

RE: [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

In reply to Christoffer Dall
Quoted 13 lines

-----Original Message-----
From: kvm-owner at vger.kernel.org [mailto:kvm-owner at vger.kernel.org] On Behalf Of Christoffer Dall
Sent: Monday, October 01, 2012 4:22 AM
To: Will Deacon
Cc: kvm at vger.kernel.org; linux-arm-kernel at lists.infradead.org; kvmarm at lists.cs.columbia.edu; rusty.russell at linaro.org; avi at redhat.com; marc.zyngier at arm.com
Subject: Re: [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

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

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

Quoted 2 lines
arch/arm/include/asm/kvm_arm.h new file +2 -0
+/* Multiprocessor Affinity Register */
+#define MPIDR_CPUID    (0x3 << 0)

I'm fairly sure we already have code under arch/arm/ for dealing with the mpidr. Let's re-use that rather than reinventing it here.

I see some defines in topology.c - do you want some of these factored out into a header file that we can then also use from kvm? If so,

where?

Quoted 42 lines
Quoted 36 lines

I guess either in topology.h or a new header (topology-bits.h).

Quoted 13 lines
Quoted 12 lines

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

Quoted 9 lines
arch/arm/include/asm/kvm_host.h new file +9 -0
+#define EXCEPTION_NONE      0
+#define EXCEPTION_RESET     0x80
+#define EXCEPTION_UNDEFINED 0x40
+#define EXCEPTION_SOFTWARE  0x20
+#define EXCEPTION_PREFETCH  0x10
+#define EXCEPTION_DATA      0x08
+#define EXCEPTION_IMPRECISE 0x04
+#define EXCEPTION_IRQ       0x02
+#define EXCEPTION_FIQ       0x01

Why the noise?

Quoted 1 line

these are simply cruft from a previous life of KVM/ARM.

Ok, then please get rid of them.

Quoted 10 lines
Quoted 9 lines

arch/arm/include/asm/kvm_emulate.h line 52

Quoted 5 lines
arch/arm/include/asm/kvm_emulate.h new file +5 -0
+static inline enum vcpu_mode vcpu_mode(struct kvm_vcpu *vcpu) {
+       u8 mode = __vcpu_mode(vcpu->arch.regs.cpsr);
+       BUG_ON(mode == 0xf);
+       return mode;
+}

I noticed that you have a fair few BUG_ONs throughout the series. Fair enough, but for hyp code is that really the right thing to do? Killing the guest could make more sense, perhaps?

Quoted 5 lines

the idea is to have BUG_ONs that are indeed BUG_ONs that we want to catch explicitly on the host. We have had a pass over the code to change all the BUG_ONs that can be provoked by the guest and inject the proper exceptions into the guest in this case. If you find places where this is not the case, it should be changed, and do let me know.

Quoted 5 lines

Ok, so are you saying that a BUG_ON due to some detected inconsistency with one guest may not necessarily terminate the other guests? BUG_ONs in the host seem like a bad idea if the host is able to continue with a subset of guests.

Quoted 9 lines

No, I'm saying a BUG_ON is an actual BUG, it should not happen and there should be nowhere where a guest can cause a BUG_ON to occur in the host, because that would be a bug.

We basically never kill a guest unless really extreme things happen (like we cannot allocate a pte, in which case we return -ENOMEM). If a guest does something really weird, that guest will receive the appropriate exception (undefined, prefetch abort, ...)

I agree with Will. It seems to be overreacting to kill the entire system.

Quoted 1 line

From the code above, BUG_ON case clearly points out that there happened a

serious bug case. However, killing the corresponding VM may not cause any further problem.
Then leave some logs for debugging and killing the VM seems to be enough.

Let's assume KVM for ARM is distributed with a critical bug. If the case is defended by BUG_ON, it will cause host to shutdown. If the case is defended by killing VM, it will cause VM to shutdown. In my opinion, latter case seems to be better.

I looked for a guide on BUG_ON and found this:
http://yarchive.net/comp/linux/BUG.html

Quoted 72 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines

Review

Quoted 3 lines
(quoted diff) +2 -0
+static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu) {
+       return vcpu_reg(vcpu, 15); }

If you stick a struct pt_regs into struct kvm_regs, you could reuse ARM_pc here etc.

Quoted 8 lines
Quoted 3 lines

I prefer not to, because we'd have those registers presumably for usr mode and then we only define the others explicit. I think it's much clearer to look at kvm_regs today.

I disagree and think that you should reuse as much of the arch/arm/ code as possible. Not only does it make it easier to read by people who are familiar with that code (and in turn get you more reviewers) but it also means that we limit the amount of duplication that we have.

Reusing a struct just for the sake of reusing is not necessarily an improvement. Some times it complicates things, and some times it's misleading. To me, pt_regs carry the meaning that these are the registers for a user space process that traps into the kernel - in KVM we emulate a virtual CPU and that current definition is quite clear.

The argument that more people will review the code if the struct contains a pt_regs field rather than a usr_regs field is completely invalid, because I'm sure everyone that reviews virtualization code will know that user mode is a mode on the cpu and it has some registers and this is the state we store when we context switch a VM - pt_regs could be read as the regs that we stored in the mode that the VM happened to be in when we took an exception, which I would think is crazy, and probably not what you suggest.

Writing the literal 15 for the PC register is not really a problem in terms of duplication - it's nothing that requires separate maintenance.

At this point the priority should really be correctness, readability, and performance, imho.

Quoted 3 lines

I think Marc (CC'd) had a go at this with some success.

great, if this improves the code, then I suggest someone rebases an appropriate patch and sends it to the kvmarm mailing list so we can have a look at it, but there are users out there looking to try kvm/arm and we should try to give it to them.

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines

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

Quoted 6 lines
arch/arm/include/asm/kvm_host.h new file +6 -0
+#ifndef __ARM_KVM_HOST_H__
+#define __ARM_KVM_HOST_H__
+
+#include <asm/kvm.h>
+
+#define KVM_MAX_VCPUS 4

NR_CPUS?

Quoted 15 lines
Quoted 2 lines

well this is defined by KVM generic code, and is common for other architecture.

I mean #define KVM_MAX_CPUS NR_CPUS. The 4 seems arbitrary.

Quoted 10 lines
Quoted 10 lines

arch/arm/kvm/arm.c line 210

Quoted 7 lines
arch/arm/kvm/arm.c new file +7 -0
+int __attribute_const__ kvm_target_cpu(void) {
+       unsigned int midr;
+
+       midr = read_cpuid_id();
+       switch ((midr >> 4) & 0xfff) {
+       case KVM_ARM_TARGET_CORTEX_A15:
+               return KVM_ARM_TARGET_CORTEX_A15;

I have this code already in perf_event.c. Can we move it somewhere common and share it? You should also check that the implementor field

Quoted 4 lines

is 0x41.

Quoted 3 lines
Quoted 3 lines
Quoted 1 line

by all means, you can probably suggest a good place better than I

can...

Quoted 42 lines
Quoted 10 lines

cputype.h?

Quoted 7 lines
Quoted 7 lines

arch/arm/kvm/exports.c line 21

Quoted 3 lines
arch/arm/kvm/exports.c new file +3 -0
+#include <linux/module.h>
+
+EXPORT_SYMBOL_GPL(smp_send_reschedule);

Erm...

We already have arch/arm/kernel/armksyms.c for exports -- please use

that.

Quoted 31 lines
Quoted 8 lines
Quoted 3 lines

However, exporting such low-level operations sounds like a bad idea. How realistic is kvm-as-a-module on ARM anyway?

at this point it's broken, so I'll just remove this and leave this for a fun project for some poor soul at some point if anyone ever needs half the code outside the kernel as a module (the other half needs to be compiled in anyway)

Ok, that suits me. If it's broken, let's not include it in the initial submission.

Quoted 18 lines
Quoted 4 lines
Quoted 4 lines
arch/arm/kvm/arm.c new file +4 -0
+int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct
+kvm_regs *regs) {
+       return -EINVAL;
+}
Quoted 9 lines

arch/arm/kvm/arm.c line 80

Quoted 5 lines
arch/arm/kvm/arm.c new file +5 -0
+
+int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct
+kvm_regs *regs) {
+       return -EINVAL;
+}

Again, all looks like this should be implemented using regsets from what I can tell.

this API has been discussed to death on the KVM lists, and we can of course revive that if the regset makes it nicer - I'd prefer getting this upstream the way that it is now though, where GET_REG / SET_REG seems to be the way forward from a KVM perspective.

Quoted 6 lines
Quoted 3 lines

I'm sure the API has been discussed, but I've not seen that conversation and I'm also not aware about whether or not regsets were considered as a possibility for this stuff. The advantages of using them

are:

Quoted 2 lines

1. It's less code for the arch to implement (and most of what

you

Quoted 62 lines
Quoted 3 lines

need, you already have).

2. You can move the actual copying code into core KVM, like we

have

Quoted 48 lines

for ptrace.

3. New KVM ports (e.g. arm64) can reuse the core copying code easily.

Furthermore, some registers (typically) floating point and GPRs will already have regsets for the ptrace code, so that can be reused if you share the datatypes.

The big problem with getting things upstream and then changing it later is that you will break the ABI. I highly doubt that's feasible, so can we not just use regsets from the start for ARM?

Quoted 31 lines
Quoted 21 lines
Quoted 21 lines
arch/arm/kvm/reset.c new file +21 -0
+int kvm_reset_vcpu(struct kvm_vcpu *vcpu) {
+       struct kvm_regs *cpu_reset;
+
+       switch (vcpu->arch.target) {
+       case KVM_ARM_TARGET_CORTEX_A15:
+               if (vcpu->vcpu_id > a15_max_cpu_idx)
+                       return -EINVAL;
+               cpu_reset = &a15_regs_reset;
+               vcpu->arch.midr = read_cpuid_id();
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       /* Reset core registers */
+       memcpy(&vcpu->arch.regs, cpu_reset,
+ sizeof(vcpu->arch.regs));
+
+       /* Reset CP15 registers */
+       kvm_reset_coprocs(vcpu);
+
Quoted 9 lines

Review

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

This is a nice way to plug in new CPUs but the way the rest of the code is currently written, all the ARMv7 and Cortex-A15 code is merged together. I
*strongly* suggest you isolate this from the start, as it will help you see what is architected and what is implementation-specific.

Quoted 3 lines

not entirely sure what you mean. You want a separate coproc.c file for
Cortex-A15 specific stuff like coproc_a15.c?

Quoted 6 lines

Indeed. I think it will make adding new CPUs a lot clearer and separate the architecture from the implementation.

Cheers,

Will

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

#38 lore raw

Re: [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

Quoted 85 lines · Min-gyu Kim
Quoted 13 lines

-----Original Message-----
From: kvm-owner at vger.kernel.org [mailto:kvm-owner at vger.kernel.org] On Behalf Of Christoffer Dall
Sent: Monday, October 01, 2012 4:22 AM
To: Will Deacon
Cc: kvm at vger.kernel.org; linux-arm-kernel at lists.infradead.org; kvmarm at lists.cs.columbia.edu; rusty.russell at linaro.org; avi at redhat.com; marc.zyngier at arm.com
Subject: Re: [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

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

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

Quoted 2 lines
arch/arm/include/asm/kvm_arm.h new file +2 -0
+/* Multiprocessor Affinity Register */
+#define MPIDR_CPUID    (0x3 << 0)

I'm fairly sure we already have code under arch/arm/ for dealing with the mpidr. Let's re-use that rather than reinventing it here.

I see some defines in topology.c - do you want some of these factored out into a header file that we can then also use from kvm? If so,

where?

Quoted 42 lines
Quoted 36 lines

I guess either in topology.h or a new header (topology-bits.h).

Quoted 13 lines
Quoted 12 lines

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

Quoted 9 lines
arch/arm/include/asm/kvm_host.h new file +9 -0
+#define EXCEPTION_NONE      0
+#define EXCEPTION_RESET     0x80
+#define EXCEPTION_UNDEFINED 0x40
+#define EXCEPTION_SOFTWARE  0x20
+#define EXCEPTION_PREFETCH  0x10
+#define EXCEPTION_DATA      0x08
+#define EXCEPTION_IMPRECISE 0x04
+#define EXCEPTION_IRQ       0x02
+#define EXCEPTION_FIQ       0x01

Why the noise?

Quoted 1 line

these are simply cruft from a previous life of KVM/ARM.

Ok, then please get rid of them.

Quoted 10 lines
Quoted 9 lines

arch/arm/include/asm/kvm_emulate.h line 52

Quoted 5 lines
arch/arm/include/asm/kvm_emulate.h new file +5 -0
+static inline enum vcpu_mode vcpu_mode(struct kvm_vcpu *vcpu) {
+       u8 mode = __vcpu_mode(vcpu->arch.regs.cpsr);
+       BUG_ON(mode == 0xf);
+       return mode;
+}

I noticed that you have a fair few BUG_ONs throughout the series. Fair enough, but for hyp code is that really the right thing to do? Killing the guest could make more sense, perhaps?

Quoted 5 lines

the idea is to have BUG_ONs that are indeed BUG_ONs that we want to catch explicitly on the host. We have had a pass over the code to change all the BUG_ONs that can be provoked by the guest and inject the proper exceptions into the guest in this case. If you find places where this is not the case, it should be changed, and do let me know.

Quoted 5 lines

Ok, so are you saying that a BUG_ON due to some detected inconsistency with one guest may not necessarily terminate the other guests? BUG_ONs in the host seem like a bad idea if the host is able to continue with a subset of guests.

Quoted 9 lines

No, I'm saying a BUG_ON is an actual BUG, it should not happen and there should be nowhere where a guest can cause a BUG_ON to occur in the host, because that would be a bug.

We basically never kill a guest unless really extreme things happen (like we cannot allocate a pte, in which case we return -ENOMEM). If a guest does something really weird, that guest will receive the appropriate exception (undefined, prefetch abort, ...)

I agree with Will. It seems to be overreacting to kill the entire system.

From the code above, BUG_ON case clearly points out that there happened a serious bug case. However, killing the corresponding VM may not cause any further problem.
Then leave some logs for debugging and killing the VM seems to be enough.

Quoted 9 lines

Let's assume KVM for ARM is distributed with a critical bug. If the case is defended by BUG_ON, it will cause host to shutdown. If the case is defended by killing VM, it will cause VM to shutdown. In my opinion, latter case seems to be better.

I looked for a guide on BUG_ON and found this:
http://yarchive.net/comp/linux/BUG.html

I completely agree with all this, no further argument is needed. The point of a BUG_ON is to explicitly state the reason for a bug that will anyhow cause the host kernel to overall malfunction. The above bug_on statement is long gone (see new patches), and if you see other cases like this, the code should have been tested and we can remove the BUG_ON.

Quoted 189 lines
Quoted 72 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines

Review

Quoted 3 lines
(quoted diff) +2 -0
+static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu) {
+       return vcpu_reg(vcpu, 15); }

If you stick a struct pt_regs into struct kvm_regs, you could reuse ARM_pc here etc.

Quoted 8 lines
Quoted 3 lines

I prefer not to, because we'd have those registers presumably for usr mode and then we only define the others explicit. I think it's much clearer to look at kvm_regs today.

I disagree and think that you should reuse as much of the arch/arm/ code as possible. Not only does it make it easier to read by people who are familiar with that code (and in turn get you more reviewers) but it also means that we limit the amount of duplication that we have.

Reusing a struct just for the sake of reusing is not necessarily an improvement. Some times it complicates things, and some times it's misleading. To me, pt_regs carry the meaning that these are the registers for a user space process that traps into the kernel - in KVM we emulate a virtual CPU and that current definition is quite clear.

The argument that more people will review the code if the struct contains a pt_regs field rather than a usr_regs field is completely invalid, because I'm sure everyone that reviews virtualization code will know that user mode is a mode on the cpu and it has some registers and this is the state we store when we context switch a VM - pt_regs could be read as the regs that we stored in the mode that the VM happened to be in when we took an exception, which I would think is crazy, and probably not what you suggest.

Writing the literal 15 for the PC register is not really a problem in terms of duplication - it's nothing that requires separate maintenance.

At this point the priority should really be correctness, readability, and performance, imho.

Quoted 3 lines

I think Marc (CC'd) had a go at this with some success.

great, if this improves the code, then I suggest someone rebases an appropriate patch and sends it to the kvmarm mailing list so we can have a look at it, but there are users out there looking to try kvm/arm and we should try to give it to them.

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines

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

Quoted 6 lines
arch/arm/include/asm/kvm_host.h new file +6 -0
+#ifndef __ARM_KVM_HOST_H__
+#define __ARM_KVM_HOST_H__
+
+#include <asm/kvm.h>
+
+#define KVM_MAX_VCPUS 4

NR_CPUS?

Quoted 15 lines
Quoted 2 lines

well this is defined by KVM generic code, and is common for other architecture.

I mean #define KVM_MAX_CPUS NR_CPUS. The 4 seems arbitrary.

Quoted 10 lines
Quoted 10 lines

arch/arm/kvm/arm.c line 210

Quoted 7 lines
arch/arm/kvm/arm.c new file +7 -0
+int __attribute_const__ kvm_target_cpu(void) {
+       unsigned int midr;
+
+       midr = read_cpuid_id();
+       switch ((midr >> 4) & 0xfff) {
+       case KVM_ARM_TARGET_CORTEX_A15:
+               return KVM_ARM_TARGET_CORTEX_A15;

I have this code already in perf_event.c. Can we move it somewhere common and share it? You should also check that the implementor field

Quoted 4 lines

is 0x41.

Quoted 3 lines
Quoted 3 lines
Quoted 1 line

by all means, you can probably suggest a good place better than I

can...

Quoted 42 lines
Quoted 10 lines

cputype.h?

Quoted 7 lines
Quoted 7 lines

arch/arm/kvm/exports.c line 21

Quoted 3 lines
arch/arm/kvm/exports.c new file +3 -0
+#include <linux/module.h>
+
+EXPORT_SYMBOL_GPL(smp_send_reschedule);

Erm...

We already have arch/arm/kernel/armksyms.c for exports -- please use

that.

Quoted 31 lines
Quoted 8 lines
Quoted 3 lines

However, exporting such low-level operations sounds like a bad idea. How realistic is kvm-as-a-module on ARM anyway?

at this point it's broken, so I'll just remove this and leave this for a fun project for some poor soul at some point if anyone ever needs half the code outside the kernel as a module (the other half needs to be compiled in anyway)

Ok, that suits me. If it's broken, let's not include it in the initial submission.

Quoted 18 lines
Quoted 4 lines
Quoted 4 lines
arch/arm/kvm/arm.c new file +4 -0
+int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct
+kvm_regs *regs) {
+       return -EINVAL;
+}
Quoted 9 lines

arch/arm/kvm/arm.c line 80

Quoted 5 lines
arch/arm/kvm/arm.c new file +5 -0
+
+int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct
+kvm_regs *regs) {
+       return -EINVAL;
+}

Again, all looks like this should be implemented using regsets from what I can tell.

this API has been discussed to death on the KVM lists, and we can of course revive that if the regset makes it nicer - I'd prefer getting this upstream the way that it is now though, where GET_REG / SET_REG seems to be the way forward from a KVM perspective.

Quoted 6 lines
Quoted 3 lines

I'm sure the API has been discussed, but I've not seen that conversation and I'm also not aware about whether or not regsets were considered as a possibility for this stuff. The advantages of using them

are:

Quoted 2 lines

1. It's less code for the arch to implement (and most of what

you

Quoted 62 lines
Quoted 3 lines

need, you already have).

2. You can move the actual copying code into core KVM, like we

have

Quoted 48 lines

for ptrace.

3. New KVM ports (e.g. arm64) can reuse the core copying code easily.

Furthermore, some registers (typically) floating point and GPRs will already have regsets for the ptrace code, so that can be reused if you share the datatypes.

The big problem with getting things upstream and then changing it later is that you will break the ABI. I highly doubt that's feasible, so can we not just use regsets from the start for ARM?

Quoted 31 lines
Quoted 21 lines
Quoted 21 lines
arch/arm/kvm/reset.c new file +21 -0
+int kvm_reset_vcpu(struct kvm_vcpu *vcpu) {
+       struct kvm_regs *cpu_reset;
+
+       switch (vcpu->arch.target) {
+       case KVM_ARM_TARGET_CORTEX_A15:
+               if (vcpu->vcpu_id > a15_max_cpu_idx)
+                       return -EINVAL;
+               cpu_reset = &a15_regs_reset;
+               vcpu->arch.midr = read_cpuid_id();
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       /* Reset core registers */
+       memcpy(&vcpu->arch.regs, cpu_reset,
+ sizeof(vcpu->arch.regs));
+
+       /* Reset CP15 registers */
+       kvm_reset_coprocs(vcpu);
+
Quoted 9 lines

Review

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

This is a nice way to plug in new CPUs but the way the rest of the code is currently written, all the ARMv7 and Cortex-A15 code is merged together. I
*strongly* suggest you isolate this from the start, as it will help you see what is architected and what is implementation-specific.

Quoted 3 lines

not entirely sure what you mean. You want a separate coproc.c file for
Cortex-A15 specific stuff like coproc_a15.c?

Quoted 6 lines

Indeed. I think it will make adding new CPUs a lot clearer and separate the architecture from the implementation.

Cheers,

Will

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

#39 lore raw

RE: [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

In reply to Min-gyu Kim
Quoted 39 lines · Min-gyu Kim
Quoted 39 lines

-----Original Message-----
From: kvm-owner at vger.kernel.org [mailto:kvm-owner at vger.kernel.org] On Behalf Of Christoffer Dall
Sent: Monday, October 01, 2012 4:22 AM
To: Will Deacon
Cc: kvm at vger.kernel.org; linux-arm-kernel at lists.infradead.org; kvmarm at lists.cs.columbia.edu; rusty.russell at linaro.org; avi at redhat.com; marc.zyngier at arm.com
Subject: Re: [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

Quoted 17 lines · Will Deacon
Quoted 10 lines · Christoffer Dall
Quoted 9 lines

arch/arm/include/asm/kvm_emulate.h line 52

Quoted 5 lines
arch/arm/include/asm/kvm_emulate.h new file +5 -0
+static inline enum vcpu_mode vcpu_mode(struct kvm_vcpu *vcpu) {
+       u8 mode = __vcpu_mode(vcpu->arch.regs.cpsr);
+       BUG_ON(mode == 0xf);
+       return mode;
+}

I noticed that you have a fair few BUG_ONs throughout the series. Fair enough, but for hyp code is that really the right thing to do? Killing the guest could make more sense, perhaps?

Quoted 5 lines

the idea is to have BUG_ONs that are indeed BUG_ONs that we want to catch explicitly on the host. We have had a pass over the code to change all the BUG_ONs that can be provoked by the guest and inject the proper exceptions into the guest in this case. If you find places where this is not the case, it should be changed, and do let me know.

Quoted 5 lines

Ok, so are you saying that a BUG_ON due to some detected inconsistency with one guest may not necessarily terminate the other guests? BUG_ONs in the host seem like a bad idea if the host is able to continue with a subset of guests.

No, I'm saying a BUG_ON is an actual BUG, it should not happen and there should be nowhere where a guest can cause a BUG_ON to occur in the host, because that would be a bug.

Quoted 7 lines
Quoted 5 lines

We basically never kill a guest unless really extreme things happen (like we cannot allocate a pte, in which case we return -ENOMEM). If a guest does something really weird, that guest will receive the appropriate exception (undefined, prefetch abort, ...)

I agree with Will. It seems to be overreacting to kill the entire system.

No. If we manage to put the guest in an undefined state, we don't know what has happened. Something has gone horribly wrong. Most likely, vcpu isn't a vcpu pointer at all, or has been so horribly corrupted that "killing the guest" is just a complicated way of messing ourselves up further.

The system by this stage is so damaged that we're best off panicing, and avoiding corrupting things further.

Cheers,
Rusty.

#40 lore raw

Re: [kvmarm] [PATCH 06/15] KVM: ARM: Initial skeleton to compile KVM support

In reply to Will Deacon
Quoted 15 lines · Will Deacon
Quoted 4 lines
arch/arm/kvm/arm.c new file +4 -0
+       case KVM_GET_REG_LIST: {
+               struct kvm_reg_list __user *user_list = argp;
+               struct kvm_reg_list reg_list;
+               unsigned n;

arch/arm/kvm/arm.c line 306

Quoted 10 lines
arch/arm/kvm/arm.c new file +10 -0
+
+               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))
+                       return -EFAULT;
+               if (n < reg_list.n)
+                       return -E2BIG;
+               return kvm_arm_copy_reg_indices(vcpu, user_list->reg);
Quoted 1 line

kvm_reg_list sounds like it could be done using a regset instead.

Wouldn't those regsets be userspace oriented?

For example, the GPRs returned here include all the shadowed interrupt registers (or however they're called) while most user oriented APIs would only include the user visible registers.

FWIW, we're trying to move to an architecture independent ABI for KVM registers, but that's a lot of work since we need to make sure all the weird x86 registers (and non-register state) fit into that. Maybe that ABI will be regset based, but I don't want to block the ARM port on this.

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

PATCH 7/15 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              |  109 ++++++++++++
 arch/arm/include/asm/kvm_asm.h              |   20 ++
 arch/arm/include/asm/kvm_mmu.h              |   36 ++++
 arch/arm/include/asm/pgtable-3level-hwdef.h |    4 
 arch/arm/kvm/arm.c                          |  236 +++++++++++++++++++++++++++
 arch/arm/kvm/exports.c                      |   16 ++
 arch/arm/kvm/init.S                         |  137 ++++++++++++++++
 arch/arm/kvm/interrupts.S                   |   48 +++++
 arch/arm/kvm/mmu.c                          |  189 ++++++++++++++++++++++
 mm/memory.c                                 |    2 
 10 files changed, 797 insertions(+)
 create mode 100644 arch/arm/include/asm/kvm_mmu.h
arch/arm/include/asm/kvm_arm.h +109 -0
index 2f9d28e..6e46541 100644
@@ -19,10 +19,119 @@
 #ifndef __ARM_KVM_ARM_H__
 #define __ARM_KVM_ARM_H__
 
+#include <asm/types.h>
+
 /* Supported Processor Types */
 #define CORTEX_A15	(0xC0F)
 
 /* Multiprocessor Affinity Register */
 #define MPIDR_CPUID	(0x3 << 0)
 
+/* Hyp Configuration Register (HCR) bits */
+#define HCR_TGE		(1 << 27)
+#define HCR_TVM		(1 << 26)
+#define HCR_TTLB	(1 << 25)
+#define HCR_TPU		(1 << 24)
+#define HCR_TPC		(1 << 23)
+#define HCR_TSW		(1 << 22)
+#define HCR_TAC		(1 << 21)
+#define HCR_TIDCP	(1 << 20)
+#define HCR_TSC		(1 << 19)
+#define HCR_TID3	(1 << 18)
+#define HCR_TID2	(1 << 17)
+#define HCR_TID1	(1 << 16)
+#define HCR_TID0	(1 << 15)
+#define HCR_TWE		(1 << 14)
+#define HCR_TWI		(1 << 13)
+#define HCR_DC		(1 << 12)
+#define HCR_BSU		(3 << 10)
+#define HCR_BSU_IS	(1 << 10)
+#define HCR_FB		(1 << 9)
+#define HCR_VA		(1 << 8)
+#define HCR_VI		(1 << 7)
+#define HCR_VF		(1 << 6)
+#define HCR_AMO		(1 << 5)
+#define HCR_IMO		(1 << 4)
+#define HCR_FMO		(1 << 3)
+#define HCR_PTW		(1 << 2)
+#define HCR_SWIO	(1 << 1)
+#define HCR_VM		1
+
+/*
+ * The bits we set in HCR:
+ * TAC:		Trap ACTLR
+ * TSC:		Trap SMC
+ * TSW:		Trap cache operations by set/way
+ * TWI:		Trap WFI
+ * TIDCP:	Trap L2CTLR/L2ECTLR
+ * BSU_IS:	Upgrade barriers to the inner shareable domain
+ * FB:		Force broadcast of all maintainance operations
+ * AMO:		Override CPSR.A and enable signaling with VA
+ * IMO:		Override CPSR.I and enable signaling with VI
+ * FMO:		Override CPSR.F and enable signaling with VF
+ * SWIO:	Turn set/way invalidates into set/way clean+invalidate
+ */
+#define HCR_GUEST_MASK (HCR_TSC | HCR_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \
+			HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \
+			HCR_SWIO | HCR_TIDCP)
+
+/* Hyp System Control Register (HSCTLR) bits */
+#define HSCTLR_TE	(1 << 30)
+#define HSCTLR_EE	(1 << 25)
+#define HSCTLR_FI	(1 << 21)
+#define HSCTLR_WXN	(1 << 19)
+#define HSCTLR_I	(1 << 12)
+#define HSCTLR_C	(1 << 2)
+#define HSCTLR_A	(1 << 1)
+#define HSCTLR_M	1
+#define HSCTLR_MASK	(HSCTLR_M | HSCTLR_A | HSCTLR_C | HSCTLR_I | \
+			 HSCTLR_WXN | HSCTLR_FI | HSCTLR_EE | HSCTLR_TE)
+
+/* TTBCR and HTCR Registers bits */
+#define TTBCR_EAE	(1 << 31)
+#define TTBCR_IMP	(1 << 30)
+#define TTBCR_SH1	(3 << 28)
+#define TTBCR_ORGN1	(3 << 26)
+#define TTBCR_IRGN1	(3 << 24)
+#define TTBCR_EPD1	(1 << 23)
+#define TTBCR_A1	(1 << 22)
+#define TTBCR_T1SZ	(3 << 16)
+#define TTBCR_SH0	(3 << 12)
+#define TTBCR_ORGN0	(3 << 10)
+#define TTBCR_IRGN0	(3 << 8)
+#define TTBCR_EPD0	(1 << 7)
+#define TTBCR_T0SZ	3
+#define HTCR_MASK	(TTBCR_T0SZ | TTBCR_IRGN0 | TTBCR_ORGN0 | TTBCR_SH0)
+
+/* Hyp Debug Configuration Register bits */
+#define HDCR_TDRA	(1 << 11)
+#define HDCR_TDOSA	(1 << 10)
+#define HDCR_TDA	(1 << 9)
+#define HDCR_TDE	(1 << 8)
+#define HDCR_HPME	(1 << 7)
+#define HDCR_TPM	(1 << 6)
+#define HDCR_TPMCR	(1 << 5)
+#define HDCR_HPMN_MASK	(0x1F)
+
+/* Virtualization Translation Control Register (VTCR) bits */
+#define VTCR_SH0	(3 << 12)
+#define VTCR_ORGN0	(3 << 10)
+#define VTCR_IRGN0	(3 << 8)
+#define VTCR_SL0	(3 << 6)
+#define VTCR_S		(1 << 4)
+#define VTCR_T0SZ	3
+#define VTCR_MASK	(VTCR_SH0 | VTCR_ORGN0 | VTCR_IRGN0 | VTCR_SL0 | \
+			 VTCR_S | VTCR_T0SZ | VTCR_MASK)
+#define VTCR_HTCR_SH	(VTCR_SH0 | VTCR_ORGN0 | VTCR_IRGN0)
+#define VTCR_SL_L2	0		/* Starting-level: 2 */
+#define VTCR_SL_L1	(1 << 6)	/* Starting-level: 1 */
+#define VTCR_GUEST_SL	VTCR_SL_L1
+#define VTCR_GUEST_T0SZ	0
+#if VTCR_GUEST_SL == 0
+#define VTTBR_X		(14 - VTCR_GUEST_T0SZ)
+#else
+#define VTTBR_X		(5 - VTCR_GUEST_T0SZ)
+#endif
+
+
 #endif /* __ARM_KVM_ARM_H__ */
arch/arm/include/asm/kvm_asm.h +20 -0
index 44591f9..6c40e55 100644
@@ -26,5 +26,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 +36 -0
new file mode 100644
index 0000000..8252921
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __ARM_KVM_MMU_H__
+#define __ARM_KVM_MMU_H__
+
+/*
+ * The architecture supports 40-bit IPA as input to the 2nd stage translations
+ * and PTRS_PER_PGD2 could therefore be 1024.
+ *
+ * To save a bit of memory and to avoid alignment issues we assume 39-bit IPA
+ * for now, but remember that the level-1 table must be aligned to its size.
+ */
+#define PTRS_PER_PGD2	512
+#define PGD2_ORDER	get_order(PTRS_PER_PGD2 * sizeof(pgd_t))
+
+int create_hyp_mappings(void *from, void *to);
+int create_hyp_io_mappings(void *from, void *to, phys_addr_t);
+void free_hyp_pmds(void);
+
+#endif /* __ARM_KVM_MMU_H__ */
arch/arm/include/asm/pgtable-3level-hwdef.h +4 -0
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 +236 -0

260 lines not on this page · raw message

arch/arm/kvm/exports.c +16 -0
index 3e38c95..8ebdf07 100644
@@ -17,5 +17,21 @@
  */
 
 #include <linux/module.h>
+#include <asm/kvm_asm.h>
+
+EXPORT_SYMBOL_GPL(__kvm_hyp_init);
+EXPORT_SYMBOL_GPL(__kvm_hyp_init_end);
+
+EXPORT_SYMBOL_GPL(__kvm_hyp_exit);
+EXPORT_SYMBOL_GPL(__kvm_hyp_exit_end);
+
+EXPORT_SYMBOL_GPL(__kvm_hyp_vector);
+
+EXPORT_SYMBOL_GPL(__kvm_hyp_code_start);
+EXPORT_SYMBOL_GPL(__kvm_hyp_code_end);
+
+EXPORT_SYMBOL_GPL(__kvm_vcpu_run);
+
+EXPORT_SYMBOL_GPL(__kvm_flush_vm_context);
 
 EXPORT_SYMBOL_GPL(smp_send_reschedule);
arch/arm/kvm/init.S +137 -0

142 lines not on this page · raw message

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

53 lines not on this page · raw message

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

192 lines not on this page · raw message

mm/memory.c +2 -0
index 5736170..0e58fdd 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)
 {
#42 lore raw

PATCH 8/15 KVM: ARM: Memory virtualization setup

In reply to Christoffer Dall

From: Christoffer Dall <[email protected]>

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

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

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

We pre-allocate page table memory to be able to synchronize using a spinlock and be called under rcu_read_lock from the MMU notifiers. We steal the mmu_memory_cache implementation from x86 and adapt for our specific usage.

We support MMU notifiers (thanks to Marc Zyngier) through kvm_unmap_hva and kvm_set_spte_hva.

Finally, define kvm_phys_addr_ioremap() to map a device at a guest IPA, which is used by VGIC support to map the virtual CPU interface registers to the guest. This support is added by Marc Zyngier.

---
 arch/arm/include/asm/kvm_asm.h  |    2 
 arch/arm/include/asm/kvm_host.h |   18 ++
 arch/arm/include/asm/kvm_mmu.h  |    9 +
 arch/arm/kvm/Kconfig            |    1 
 arch/arm/kvm/arm.c              |   38 ++++
 arch/arm/kvm/exports.c          |    1 
 arch/arm/kvm/interrupts.S       |    8 +
 arch/arm/kvm/mmu.c              |  377 +++++++++++++++++++++++++++++++++++++++
 8 files changed, 453 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_asm.h +2 -0
index 6c40e55..201ec1f 100644
@@ -29,6 +29,7 @@
 #define ARM_EXCEPTION_HVC	  7
 
 #ifndef __ASSEMBLY__
+struct kvm;
 struct kvm_vcpu;
 
 extern char __kvm_hyp_init[];
@@ -43,6 +44,7 @@ extern char __kvm_hyp_code_start[];
 extern char __kvm_hyp_code_end[];
 
 extern void __kvm_flush_vm_context(void);
+extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
 
 extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
 #endif
arch/arm/include/asm/kvm_host.h +18 -0
index 24959f4..f0c72b9 100644
@@ -169,4 +169,22 @@ 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);
+
+#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 8252921..11f4c3a 100644
@@ -33,4 +33,13 @@ int create_hyp_mappings(void *from, void *to);
 int create_hyp_io_mappings(void *from, void *to, phys_addr_t);
 void free_hyp_pmds(void);
 
+int kvm_alloc_stage2_pgd(struct kvm *kvm);
+void kvm_free_stage2_pgd(struct kvm *kvm);
+int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
+			  phys_addr_t pa, unsigned long size);
+
+int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run);
+
+void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);
+
 #endif /* __ARM_KVM_MMU_H__ */
arch/arm/kvm/Kconfig +1 -0
index a07ddcc..47c5500 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 +37 -1
index 6f35aec..b97ebd0 100644
@@ -82,12 +82,34 @@ void kvm_arch_sync_events(struct kvm *kvm)
 {
 }
 
+/**
+ * kvm_arch_init_vm - initializes a VM data structure
+ * @kvm:	pointer to the KVM struct
+ */
 int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 {
+	int ret = 0;
+
 	if (type)
 		return -EINVAL;
 
-	return 0;
+	ret = kvm_alloc_stage2_pgd(kvm);
+	if (ret)
+		goto out_fail_alloc;
+	spin_lock_init(&kvm->arch.pgd_lock);
+
+	ret = create_hyp_mappings(kvm, kvm + 1);
+	if (ret)
+		goto out_free_stage2_pgd;
+
+	/* Mark the initial VMID generation invalid */
+	kvm->arch.vmid_gen = 0;
+
+	return ret;
+out_free_stage2_pgd:
+	kvm_free_stage2_pgd(kvm);
+out_fail_alloc:
+	return ret;
 }
 
 int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
@@ -105,10 +127,16 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)
 	return 0;
 }
 
+/**
+ * kvm_arch_destroy_vm - destroy the VM data structure
+ * @kvm:	pointer to the KVM struct
+ */
 void kvm_arch_destroy_vm(struct kvm *kvm)
 {
 	int i;
 
+	kvm_free_stage2_pgd(kvm);
+
 	for (i = 0; i < KVM_MAX_VCPUS; ++i) {
 		if (kvm->vcpus[i]) {
 			kvm_arch_vcpu_free(kvm->vcpus[i]);
@@ -190,7 +218,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:
@@ -199,6 +233,8 @@ out:
 
 void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
 {
+	kvm_mmu_free_memory_caches(vcpu);
+	kmem_cache_free(kvm_vcpu_cache, vcpu);
 }
 
 void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
arch/arm/kvm/exports.c +1 -0
index 8ebdf07..f39f823 100644
@@ -33,5 +33,6 @@ EXPORT_SYMBOL_GPL(__kvm_hyp_code_end);
 EXPORT_SYMBOL_GPL(__kvm_vcpu_run);
 
 EXPORT_SYMBOL_GPL(__kvm_flush_vm_context);
+EXPORT_SYMBOL_GPL(__kvm_tlb_flush_vmid);
 
 EXPORT_SYMBOL_GPL(smp_send_reschedule);
arch/arm/kvm/interrupts.S +8 -0
index bf09801..edf9ed5 100644
@@ -31,6 +31,14 @@ __kvm_hyp_code_start:
 	.globl __kvm_hyp_code_start
 
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@  Flush per-VMID TLBs
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+ENTRY(__kvm_tlb_flush_vmid)
+	bx	lr
+ENDPROC(__kvm_tlb_flush_vmid)
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 @  Flush TLBs and instruction caches of current CPU for all VMIDs
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
arch/arm/kvm/mmu.c +377 -0

394 lines not on this page · raw message

#43 lore raw

PATCH 9/15 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.h        |   21 +++++++++++
 arch/arm/include/asm/kvm_arm.h    |    1 +
 arch/arm/kvm/arm.c                |   70 +++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/trace.h              |   23 ++++++++++++
 include/linux/kvm.h               |    1 +
 6 files changed, 137 insertions(+), 4 deletions(-)
Documentation/virtual/kvm/api.txt +21 -4
index 67640c6..605986f 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.h +21 -0
index a13b582..131e632 100644
@@ -22,6 +22,7 @@
 #include <asm/types.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))
@@ -85,4 +86,24 @@ struct kvm_reg_list {
 #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/include/asm/kvm_arm.h +1 -0
index 6e46541..0f641c1 100644
@@ -74,6 +74,7 @@
 #define HCR_GUEST_MASK (HCR_TSC | HCR_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \
 			HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \
 			HCR_SWIO | HCR_TIDCP)
+#define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
 
 /* Hyp System Control Register (HSCTLR) bits */
 #define HSCTLR_TE	(1 << 30)
arch/arm/kvm/arm.c +70 -0

88 lines not on this page · raw message

arch/arm/kvm/trace.h +23 -0
index f8869c1..a283eef 100644
@@ -39,7 +39,30 @@ 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)
+);
 
 #endif /* _TRACE_KVM_H */
 
include/linux/kvm.h +1 -0
index a960f66..8091b1d 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: IRQ: irq = (2*vcpu_index). FIQ: irq = (2*vcpu_indx + 1).
 	 */
 	union {
 		__u32 irq;
#44 lore raw

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

Quoted 25 lines · Christoffer Dall
arch/arm/include/asm/kvm.h +10 -0
index a13b582..131e632 100644
@@ -22,6 +22,7 @@
 #include <asm/types.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))
@@ -85,4 +86,24 @@ struct kvm_reg_list {
 #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 */

arch/arm/include/asm/kvm.h struct kvm_reg_list { · line 107

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

This define, and those referring to PPIs and SPIs sound highly GIC-specific. Is that really appropriate for kvm.h? Do you mandate a single GIC as the only interrupt controller?

Quoted 36 lines
arch/arm/include/asm/kvm_arm.h +1 -0
index 6e46541..0f641c1 100644
@@ -74,6 +74,7 @@
 #define HCR_GUEST_MASK (HCR_TSC | HCR_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \
 			HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \
 			HCR_SWIO | HCR_TIDCP)
+#define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
 
 /* Hyp System Control Register (HSCTLR) bits */
 #define HSCTLR_TE	(1 << 30)
arch/arm/kvm/arm.c +2 -0
index b97ebd0..8a87fc7 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
@@ -271,6 +272,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)
@@ -311,6 +313,74 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 	return -EINVAL;
 }
 

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

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+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) - 1;
+	else /* KVM_ARM_IRQ_CPU_FIQ */
+		bit_index = ffs(HCR_VF) - 1;

__ffs

Will

#45 lore raw

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

Quoted 32 lines · Will Deacon
Quoted 31 lines · Christoffer Dall
arch/arm/include/asm/kvm.h +16 -0
index a13b582..131e632 100644
@@ -22,6 +22,7 @@
 #include <asm/types.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))
@@ -85,4 +86,24 @@ struct kvm_reg_list {
 #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
Quoted 9 lines

arch/arm/include/asm/kvm.h struct kvm_reg_list { · line 107

Quoted 4 lines
arch/arm/include/asm/kvm.h +4 -0
+#define KVM_ARM_IRQ_CPU_FIQ          1
+
+/* Highest supported SPI, from VGIC_NR_IRQS */
+#define KVM_ARM_IRQ_GIC_MAX          127

This define, and those referring to PPIs and SPIs sound highly GIC-specific. Is that really appropriate for kvm.h? Do you mandate a single GIC as the only interrupt controller?

you can add a another gic with another in-kernel gic emulation if someone makes such one that's different from the vgic specifications by defining another irq type.

devices must interact with a gic available in the kernel, so I think referring to PPIs and SPIs is very appropriate in kvm.h for a user space device emulation that must inject either a PPI or an SPI.

We can call them TYPE_GIC_V2_XXX or something like that if you feel this is cleaner.

Quoted 36 lines
Quoted 36 lines
arch/arm/include/asm/kvm_arm.h +1 -0
index 6e46541..0f641c1 100644
@@ -74,6 +74,7 @@
 #define HCR_GUEST_MASK (HCR_TSC | HCR_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \
                      HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \
                      HCR_SWIO | HCR_TIDCP)
+#define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
 
 /* Hyp System Control Register (HSCTLR) bits */
 #define HSCTLR_TE    (1 << 30)
arch/arm/kvm/arm.c +2 -0
index b97ebd0..8a87fc7 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
@@ -271,6 +272,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)
@@ -311,6 +313,74 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
      return -EINVAL;
 }
 
Quoted 13 lines

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

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+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) - 1;
+     else /* KVM_ARM_IRQ_CPU_FIQ */
+             bit_index = ffs(HCR_VF) - 1;

__ffs

fixed

-Christoffer

#46 lore raw

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

Hi Christoffer,

Quoted 47 lines · Christoffer Dall
Quoted 32 lines · Will Deacon
Quoted 31 lines · Christoffer Dall
arch/arm/include/asm/kvm.h +16 -0
index a13b582..131e632 100644
@@ -22,6 +22,7 @@
 #include <asm/types.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))
@@ -85,4 +86,24 @@ struct kvm_reg_list {
 #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
Quoted 9 lines

arch/arm/include/asm/kvm.h struct kvm_reg_list { · line 107

Quoted 4 lines
arch/arm/include/asm/kvm.h +4 -0
+#define KVM_ARM_IRQ_CPU_FIQ          1
+
+/* Highest supported SPI, from VGIC_NR_IRQS */
+#define KVM_ARM_IRQ_GIC_MAX          127

This define, and those referring to PPIs and SPIs sound highly GIC-specific. Is that really appropriate for kvm.h? Do you mandate a single GIC as the only interrupt controller?

you can add a another gic with another in-kernel gic emulation if someone makes such one that's different from the vgic specifications by defining another irq type.

Quoted 6 lines

devices must interact with a gic available in the kernel, so I think referring to PPIs and SPIs is very appropriate in kvm.h for a user space device emulation that must inject either a PPI or an SPI.

We can call them TYPE_GIC_V2_XXX or something like that if you feel this is cleaner.

It's more that the GIC isn't really part of the architecture, so it would be cleaner to have the GIC-specifics separated out from the architectural part of KVM. That will also make it easier when adding support for future versions of the GIC.

If core KVM needs the concept of a per-cpu interrupt, just call it IRQ_TYPE_PERCPU or something rather than PPI.

Will

#47 lore raw

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

Quoted 57 lines · Will Deacon

Hi Christoffer,

Quoted 47 lines · Christoffer Dall
Quoted 32 lines · Will Deacon
Quoted 31 lines · Christoffer Dall
arch/arm/include/asm/kvm.h +16 -0
index a13b582..131e632 100644
@@ -22,6 +22,7 @@
 #include <asm/types.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))
@@ -85,4 +86,24 @@ struct kvm_reg_list {
 #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
Quoted 9 lines

arch/arm/include/asm/kvm.h struct kvm_reg_list { · line 107

Quoted 4 lines
arch/arm/include/asm/kvm.h +4 -0
+#define KVM_ARM_IRQ_CPU_FIQ          1
+
+/* Highest supported SPI, from VGIC_NR_IRQS */
+#define KVM_ARM_IRQ_GIC_MAX          127

This define, and those referring to PPIs and SPIs sound highly GIC-specific. Is that really appropriate for kvm.h? Do you mandate a single GIC as the only interrupt controller?

you can add a another gic with another in-kernel gic emulation if someone makes such one that's different from the vgic specifications by defining another irq type.

Quoted 6 lines

devices must interact with a gic available in the kernel, so I think referring to PPIs and SPIs is very appropriate in kvm.h for a user space device emulation that must inject either a PPI or an SPI.

We can call them TYPE_GIC_V2_XXX or something like that if you feel this is cleaner.

Quoted 8 lines

It's more that the GIC isn't really part of the architecture, so it would be cleaner to have the GIC-specifics separated out from the architectural part of KVM. That will also make it easier when adding support for future versions of the GIC.

If core KVM needs the concept of a per-cpu interrupt, just call it IRQ_TYPE_PERCPU or something rather than PPI.

that we have already, KVM_ARM_IRQ_TYPE_CPU

then how do you propose an interface for a user space emulation of a board that uses the vgic where a device needs to inject a PPI to the kernel that emulates the vgic?

I don't see the dire need for this separation: the API is extendable and covers all the needs at this point.

#48 lore raw

PATCH 10/15 KVM: ARM: World-switch implementation

In reply to Christoffer Dall

From: Christoffer Dall <[email protected]>

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: Switching from host kernel to Hyp-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 store the necessary state on the Hyp stack, which will look like this (growing downwards, see the hyp_hvc handler):
...
stack_page + 4: spsr (Host-SVC cpsr)
stack_page : lr_usr
--------------: stack bottom

Hyp-ABI: Switching from Hyp-mode to host kernel SVC mode:
When returning from Hyp mode to SVC mode, another HVC instruction is executed from Hyp mode, which is taken in the hyp_svc handler. The bottom of the Hyp is derived from the Hyp stack pointer (only a single page aligned stack is used per CPU) and the initial SVC registers are used to restore the host state.

Hyp-ABI: Change the HVBAR:
When removing the KVM module we want to reset our hold on Hyp mode. This is accomplished by calling HVC #0xff from the host kernel (VMID==0) with the desired new HVBAR in r0.

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

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

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

To support VFP/NEON we trap those instructions using the HPCTR. When we trap, we switch the FPU. After a guest exit, the VFP state is returned to the host. When disabling access to floating point instructions, we also mask FPEXC_EN in order to avoid the guest receiving Undefined instruction exceptions before we have a chance to switch back the floating point state. We are reusing vfp_hard_struct, so we depend on VFPv3 being enabled in the host kernel, if not, we still trap cp10 and cp11 in order to inject an undefined instruction exception whenever the guest tries to use VFP/NEON. VFP/NEON developed by Antionios Motakis and Rusty Russell.

---
 arch/arm/include/asm/kvm_arm.h  |   38 ++
 arch/arm/include/asm/kvm_host.h |    9 
 arch/arm/kernel/asm-offsets.c   |   44 ++
 arch/arm/kvm/arm.c              |  165 +++++++++
 arch/arm/kvm/interrupts.S       |  712 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 965 insertions(+), 3 deletions(-)
arch/arm/include/asm/kvm_arm.h +38 -0
index 0f641c1..ee345a6 100644
@@ -104,6 +104,18 @@
 #define TTBCR_T0SZ	3
 #define HTCR_MASK	(TTBCR_T0SZ | TTBCR_IRGN0 | TTBCR_ORGN0 | TTBCR_SH0)
 
+/* Hyp System Trap Register */
+#define HSTR_T(x)	(1 << x)
+#define HSTR_TTEE	(1 << 16)
+#define HSTR_TJDBX	(1 << 17)
+
+/* Hyp Coprocessor Trap Register */
+#define HCPTR_TCP(x)	(1 << x)
+#define HCPTR_TCP_MASK	(0x3fff)
+#define HCPTR_TASE	(1 << 15)
+#define HCPTR_TTA	(1 << 20)
+#define HCPTR_TCPAC	(1 << 31)
+
 /* Hyp Debug Configuration Register bits */
 #define HDCR_TDRA	(1 << 11)
 #define HDCR_TDOSA	(1 << 10)
@@ -134,5 +146,31 @@
 #define VTTBR_X		(5 - VTCR_GUEST_T0SZ)
 #endif
 
+/* Hyp Syndrome Register (HSR) bits */
+#define HSR_EC_SHIFT	(26)
+#define HSR_EC		(0x3fU << HSR_EC_SHIFT)
+#define HSR_IL		(1U << 25)
+#define HSR_ISS		(HSR_IL - 1)
+#define HSR_ISV_SHIFT	(24)
+#define HSR_ISV		(1U << HSR_ISV_SHIFT)
+
+#define HSR_EC_UNKNOWN	(0x00)
+#define HSR_EC_WFI	(0x01)
+#define HSR_EC_CP15_32	(0x03)
+#define HSR_EC_CP15_64	(0x04)
+#define HSR_EC_CP14_MR	(0x05)
+#define HSR_EC_CP14_LS	(0x06)
+#define HSR_EC_CP_0_13	(0x07)
+#define HSR_EC_CP10_ID	(0x08)
+#define HSR_EC_JAZELLE	(0x09)
+#define HSR_EC_BXJ	(0x0A)
+#define HSR_EC_CP14_64	(0x0C)
+#define HSR_EC_SVC_HYP	(0x11)
+#define HSR_EC_HVC	(0x12)
+#define HSR_EC_SMC	(0x13)
+#define HSR_EC_IABT	(0x20)
+#define HSR_EC_IABT_HYP	(0x21)
+#define HSR_EC_DABT	(0x24)
+#define HSR_EC_DABT_HYP	(0x25)
 
 #endif /* __ARM_KVM_ARM_H__ */
arch/arm/include/asm/kvm_host.h +9 -0
index f0c72b9..ca4c079 100644
@@ -20,6 +20,7 @@
 #define __ARM_KVM_HOST_H__
 
 #include <asm/kvm.h>
+#include <asm/fpstate.h>
 
 #define KVM_MAX_VCPUS 4
 #define KVM_MEMORY_SLOTS 32
@@ -137,6 +138,14 @@ struct kvm_vcpu_arch {
 	u32 hifar;		/* Hyp 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.
+	 */
 	/* IO related fields */
 	struct {
 		bool sign_extend;	/* for byte/halfword loads */
arch/arm/kernel/asm-offsets.c +44 -0

55 lines not on this page · raw message

arch/arm/kvm/arm.c +164 -1

194 lines not on this page · raw message

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

749 lines not on this page · raw message

#49 lore raw

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

Quoted 30 lines · Christoffer Dall
arch/arm/kernel/asm-offsets.c +15 -0
index 1429d89..cd8fc86 100644
@@ -13,6 +13,7 @@
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/dma-mapping.h>
+#include <linux/kvm_host.h>
 #include <asm/cacheflush.h>
 #include <asm/glue-df.h>
 #include <asm/glue-pf.h>
@@ -144,5 +145,48 @@ 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_MPIDR,           offsetof(struct kvm_vcpu, arch.cp15[c0_MPIDR]));
+  DEFINE(VCPU_CSSELR,          offsetof(struct kvm_vcpu, arch.cp15[c0_CSSELR]));
+  DEFINE(VCPU_SCTLR,           offsetof(struct kvm_vcpu, arch.cp15[c1_SCTLR]));
+  DEFINE(VCPU_CPACR,           offsetof(struct kvm_vcpu, arch.cp15[c1_CPACR]));
+  DEFINE(VCPU_TTBR0,           offsetof(struct kvm_vcpu, arch.cp15[c2_TTBR0]));
+  DEFINE(VCPU_TTBR1,           offsetof(struct kvm_vcpu, arch.cp15[c2_TTBR1]));
+  DEFINE(VCPU_TTBCR,           offsetof(struct kvm_vcpu, arch.cp15[c2_TTBCR]));
+  DEFINE(VCPU_DACR,            offsetof(struct kvm_vcpu, arch.cp15[c3_DACR]));
+  DEFINE(VCPU_DFSR,            offsetof(struct kvm_vcpu, arch.cp15[c5_DFSR]));
+  DEFINE(VCPU_IFSR,            offsetof(struct kvm_vcpu, arch.cp15[c5_IFSR]));
+  DEFINE(VCPU_ADFSR,           offsetof(struct kvm_vcpu, arch.cp15[c5_ADFSR]));

arch/arm/kernel/asm-offsets.c int main(void) · line 171

Quoted 10 lines
arch/arm/kernel/asm-offsets.c +10 -0
+  DEFINE(VCPU_AIFSR,           offsetof(struct kvm_vcpu, arch.cp15[c5_AIFSR]));
+  DEFINE(VCPU_DFAR,            offsetof(struct kvm_vcpu, arch.cp15[c6_DFAR]));
+  DEFINE(VCPU_IFAR,            offsetof(struct kvm_vcpu, arch.cp15[c6_IFAR]));
+  DEFINE(VCPU_PRRR,            offsetof(struct kvm_vcpu, arch.cp15[c10_PRRR]));
+  DEFINE(VCPU_NMRR,            offsetof(struct kvm_vcpu, arch.cp15[c10_NMRR]));
+  DEFINE(VCPU_VBAR,            offsetof(struct kvm_vcpu, arch.cp15[c12_VBAR]));
+  DEFINE(VCPU_CID,             offsetof(struct kvm_vcpu, arch.cp15[c13_CID]));
+  DEFINE(VCPU_TID_URW,         offsetof(struct kvm_vcpu, arch.cp15[c13_TID_URW]));
+  DEFINE(VCPU_TID_URO,         offsetof(struct kvm_vcpu, arch.cp15[c13_TID_URO]));
+  DEFINE(VCPU_TID_PRIV,                offsetof(struct kvm_vcpu, arch.cp15[c13_TID_PRIV]));

Could you instead define an offset for arch.cp15, then use scaled offsets from that in the assembly code?

Quoted 78 lines
arch/arm/kvm/arm.c +49 -0
index 8a87fc7..087f9d1 100644
@@ -41,6 +41,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");
@@ -50,6 +51,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)
 {
@@ -273,6 +278,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)
@@ -305,12 +311,169 @@ 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_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;
+
+       if (!need_new_vmid_gen(kvm))
+               return;
+
+       spin_lock(&kvm_vmid_lock);
+
+       /* First user of a new VMID generation? */

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

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+       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);

Why on_each_cpu? The maintenance operations should be broadcast, right?

Quoted 4 lines
arch/arm/kvm/arm.c +4 -0
+       }
+
+       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 380

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);
+       kvm->arch.vttbr = pgd_phys & ((1LLU << 40) - 1)
+                         & ~((2 << VTTBR_X) - 1);
+       kvm->arch.vttbr |= (u64)(kvm->arch.vmid) << 48;
+
+       spin_unlock(&kvm_vmid_lock);
+}

This smells like a watered-down version of the ASID allocator. Now, I can't really see much code sharing going on here, but perhaps your case is simpler... do you anticipate running more than 255 VMs in parallel? If not, then you could just invalidate the relevant TLB entries on VM shutdown and avoid the rollover case.

Quoted 18 lines
arch/arm/kvm/interrupts.S +6 -0
index edf9ed5..cc9448b 100644
@@ -23,6 +23,12 @@
 #include <asm/asm-offsets.h>
 #include <asm/kvm_asm.h>
 #include <asm/kvm_arm.h>
+#include <asm/vfpmacros.h>
+
+#define VCPU_USR_REG(_reg_nr)  (VCPU_USR_REGS + (_reg_nr * 4))
+#define VCPU_USR_SP            (VCPU_USR_REG(13))
+#define VCPU_FIQ_REG(_reg_nr)  (VCPU_FIQ_REGS + (_reg_nr * 4))
+#define VCPU_FIQ_SPSR          (VCPU_FIQ_REG(7))
 
        .text
        .align  PAGE_SHIFT

arch/arm/kvm/interrupts.S __kvm_hyp_code_start: · line 41

Quoted 2 lines
arch/arm/kvm/interrupts.S +0 -0
@@ -34,7 +40,33 @@ __kvm_hyp_code_start:
 @  Flush per-VMID TLBs
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

This comment syntax crops up a few times in your .S files but doesn't match anything currently under arch/arm/. Please can you follow what we do there and use /* */ ?

Quoted 5 lines
arch/arm/kvm/interrupts.S +4 -0
 ENTRY(__kvm_tlb_flush_vmid)
+       hvc     #0                      @ Switch to Hyp mode
+       push    {r2, r3}
+
+       add     r0, r0, #KVM_VTTBR

arch/arm/kvm/interrupts.S __kvm_hyp_code_start: · line 66

Quoted 10 lines
arch/arm/kvm/interrupts.S +10 -0
+       ldrd    r2, r3, [r0]
+       mcrr    p15, 6, r2, r3, c2      @ Write VTTBR
+       isb
+       mcr     p15, 0, r0, c8, c3, 0   @ TLBIALLIS (rt ignored)
+       dsb
+       isb
+       mov     r2, #0
+       mov     r3, #0
+       mcrr    p15, 6, r2, r3, c2      @ Back to VMID #0
+       isb

Do you need this isb, given that you're about to do an hvc?

Quoted 10 lines
arch/arm/kvm/interrupts.S +3 -0
+       pop     {r2, r3}
+       hvc     #0                      @ Back to SVC
        bx      lr
 ENDPROC(__kvm_tlb_flush_vmid)
 
@@ -42,26 +74,702 @@ ENDPROC(__kvm_tlb_flush_vmid)
 @  Flush TLBs and instruction caches of current CPU for all VMIDs
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
+/*

arch/arm/kvm/interrupts.S ENDPROC(__kvm_tlb_flush_vmid) · line 87

Quoted 10 lines
arch/arm/kvm/interrupts.S +9 -0
+ * void __kvm_flush_vm_context(void);
+ */
 ENTRY(__kvm_flush_vm_context)
+       hvc     #0                      @ switch to hyp-mode
+
+       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
+       dsb
+       isb

Likewise.

Quoted 24 lines
arch/arm/kvm/interrupts.S +21 -0
+       hvc     #0                      @ switch back to svc-mode, see hyp_svc
        bx      lr
 ENDPROC(__kvm_flush_vm_context)
 
+/* These are simply for the macros to work - value don't have meaning */
+.equ usr, 0
+.equ svc, 1
+.equ abt, 2
+.equ und, 3
+.equ irq, 4
+.equ fiq, 5
+
+.macro store_mode_state base_reg, mode
+       .if \mode == usr
+       mrs     r2, SP_usr
+       mov     r3, lr
+       stmdb   \base_reg!, {r2, r3}
+       .elseif \mode != fiq
+       mrs     r2, SP_\mode
+       mrs     r3, LR_\mode
+       mrs     r4, SPSR_\mode
+       stmdb   \base_reg!, {r2, r3, r4}
+       .else
+       mrs     r2, r8_fiq

arch/arm/kvm/interrupts.S ENDPROC(__kvm_tlb_flush_vmid) · line 164

Quoted 10 lines
arch/arm/kvm/interrupts.S +10 -0
+       mrs     r3, r9_fiq
+       mrs     r4, r10_fiq
+       mrs     r5, r11_fiq
+       mrs     r6, r12_fiq
+       mrs     r7, SP_fiq
+       mrs     r8, LR_fiq
+       mrs     r9, SPSR_fiq
+       stmdb   \base_reg!, {r2-r9}
+       .endif
+.endm

Perhaps you could stick the assembly macros into a separate file, like we do in assembler.h, so the code is more readable and they can be reused if need-be?

Will

#50 lore raw

Re: [kvmarm] [PATCH 10/15] KVM: ARM: World-switch implementation

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

ENTRY(__kvm_tlb_flush_vmid)

arch/arm/kvm/interrupts.S +4 -0
+       hvc     #0                      @ Switch to Hyp mode
+       push    {r2, r3}
+
+       add     r0, r0, #KVM_VTTBR

arch/arm/kvm/interrupts.S __kvm_hyp_code_start: · line 66

Quoted 10 lines
arch/arm/kvm/interrupts.S +10 -0
+       ldrd    r2, r3, [r0]
+       mcrr    p15, 6, r2, r3, c2      @ Write VTTBR
+       isb
+       mcr     p15, 0, r0, c8, c3, 0   @ TLBIALLIS (rt ignored)
+       dsb
+       isb
+       mov     r2, #0
+       mov     r3, #0
+       mcrr    p15, 6, r2, r3, c2      @ Back to VMID #0
+       isb
Quoted 5 lines

Do you need this isb, given that you're about to do an hvc?

Quoted 3 lines
arch/arm/kvm/interrupts.S +2 -0
+       pop     {r2, r3}
+       hvc     #0                      @ Back to SVC
        bx      lr

...you probably don't want to do the memory accesses involved in the 'pop' under the wrong VMID ?

-- PMM

#51 lore raw

Re: [kvmarm] [PATCH 10/15] KVM: ARM: World-switch implementation

Quoted 18 lines · Peter Maydell
Quoted 17 lines · Will Deacon
Quoted 15 lines · Christoffer Dall

ENTRY(__kvm_tlb_flush_vmid)

arch/arm/kvm/interrupts.S +14 -0
+       hvc     #0                      @ Switch to Hyp mode
+       push    {r2, r3}
+
+       add     r0, r0, #KVM_VTTBR
+       ldrd    r2, r3, [r0]
+       mcrr    p15, 6, r2, r3, c2      @ Write VTTBR
+       isb
+       mcr     p15, 0, r0, c8, c3, 0   @ TLBIALLIS (rt ignored)
+       dsb
+       isb
+       mov     r2, #0
+       mov     r3, #0
+       mcrr    p15, 6, r2, r3, c2      @ Back to VMID #0
+       isb
Quoted 8 lines
Quoted 5 lines

Do you need this isb, given that you're about to do an hvc?

Quoted 3 lines
arch/arm/kvm/interrupts.S +2 -0
+       pop     {r2, r3}
+       hvc     #0                      @ Back to SVC
        bx      lr

...you probably don't want to do the memory accesses involved in the 'pop' under the wrong VMID ?

Well, we're still in HYP mode when performing the pop, so the VMID is pretty much irrelevant. Same for the initial push, actually. As long as we're sure VTTBR has been updated when we do the exception return, I think we're safe.

        M.
Signature · 1 line
Fast, cheap, reliable. Pick two.
#52 lore raw

Re: [kvmarm] [PATCH 10/15] KVM: ARM: World-switch implementation

Quoted 26 lines · Marc Zyngier
Quoted 24 lines · Peter Maydell
Quoted 22 lines · Will Deacon
Quoted 5 lines · Christoffer Dall

ENTRY(__kvm_tlb_flush_vmid)

arch/arm/kvm/interrupts.S +4 -0
+       hvc     #0                      @ Switch to Hyp mode
+       push    {r2, r3}
+
+       add     r0, r0, #KVM_VTTBR

arch/arm/kvm/interrupts.S __kvm_hyp_code_start: · line 66

Quoted 10 lines
arch/arm/kvm/interrupts.S +10 -0
+       ldrd    r2, r3, [r0]
+       mcrr    p15, 6, r2, r3, c2      @ Write VTTBR
+       isb
+       mcr     p15, 0, r0, c8, c3, 0   @ TLBIALLIS (rt ignored)
+       dsb
+       isb
+       mov     r2, #0
+       mov     r3, #0
+       mcrr    p15, 6, r2, r3, c2      @ Back to VMID #0
+       isb

Do you need this isb, given that you're about to do an hvc?

Quoted 3 lines
arch/arm/kvm/interrupts.S +2 -0
+       pop     {r2, r3}
+       hvc     #0                      @ Back to SVC
        bx      lr
Quoted 8 lines
Quoted 2 lines

...you probably don't want to do the memory accesses involved in the 'pop' under the wrong VMID ?

Well, we're still in HYP mode when performing the pop, so the VMID is pretty much irrelevant. Same for the initial push, actually. As long as we're sure VTTBR has been updated when we do the exception return, I think we're safe.

yeah we're safe, but I can't find anywhere that says the ISB is implied from exception handling/hvc, even though I seem to recall having read this, so I put this here not to worry other readers.

-Christoffer

#53 lore raw

Re: [kvmarm] [PATCH 10/15] KVM: ARM: World-switch implementation

Quoted 9 lines · Christoffer Dall
Quoted 5 lines · Marc Zyngier

Well, we're still in HYP mode when performing the pop, so the VMID is pretty much irrelevant. Same for the initial push, actually. As long as we're sure VTTBR has been updated when we do the exception return, I think we're safe.

yeah we're safe, but I can't find anywhere that says the ISB is implied from exception handling/hvc, even though I seem to recall having read this, so I put this here not to worry other readers.

"TLB maintenance operations and the memory order model" in section B3.10.1 of the ARM ARM says that exception entry forces completion of TLB maintenance ops. Section B5.6.3 ("Synchronization of changes to system control registers") says that taking an exception synchronizes context, so the VTTBR update is guaranteed to have happened.

-- PMM

#54 lore raw

Re: [kvmarm] [PATCH 10/15] KVM: ARM: World-switch implementation

Quoted 12 lines · Peter Maydell
Quoted 9 lines · Christoffer Dall
Quoted 5 lines · Marc Zyngier

Well, we're still in HYP mode when performing the pop, so the VMID is pretty much irrelevant. Same for the initial push, actually. As long as we're sure VTTBR has been updated when we do the exception return, I think we're safe.

yeah we're safe, but I can't find anywhere that says the ISB is implied from exception handling/hvc, even though I seem to recall having read this, so I put this here not to worry other readers.

Quoted 6 lines

"TLB maintenance operations and the memory order model" in section B3.10.1 of the ARM ARM says that exception entry forces completion of TLB maintenance ops. Section B5.6.3 ("Synchronization of changes to system control registers") says that taking an exception synchronizes context, so the VTTBR update is guaranteed to have happened.

thanks - I was counting on your help here :)

-Christoffer

#55 lore raw

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

In reply to Will Deacon
Quoted 36 lines · Will Deacon
Quoted 35 lines · Christoffer Dall
arch/arm/kernel/asm-offsets.c +20 -0
index 1429d89..cd8fc86 100644
@@ -13,6 +13,7 @@
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/dma-mapping.h>
+#include <linux/kvm_host.h>
 #include <asm/cacheflush.h>
 #include <asm/glue-df.h>
 #include <asm/glue-pf.h>
@@ -144,5 +145,48 @@ 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_MPIDR,           offsetof(struct kvm_vcpu, arch.cp15[c0_MPIDR]));
+  DEFINE(VCPU_CSSELR,          offsetof(struct kvm_vcpu, arch.cp15[c0_CSSELR]));
+  DEFINE(VCPU_SCTLR,           offsetof(struct kvm_vcpu, arch.cp15[c1_SCTLR]));
+  DEFINE(VCPU_CPACR,           offsetof(struct kvm_vcpu, arch.cp15[c1_CPACR]));
+  DEFINE(VCPU_TTBR0,           offsetof(struct kvm_vcpu, arch.cp15[c2_TTBR0]));
+  DEFINE(VCPU_TTBR1,           offsetof(struct kvm_vcpu, arch.cp15[c2_TTBR1]));
+  DEFINE(VCPU_TTBCR,           offsetof(struct kvm_vcpu, arch.cp15[c2_TTBCR]));
+  DEFINE(VCPU_DACR,            offsetof(struct kvm_vcpu, arch.cp15[c3_DACR]));
+  DEFINE(VCPU_DFSR,            offsetof(struct kvm_vcpu, arch.cp15[c5_DFSR]));
+  DEFINE(VCPU_IFSR,            offsetof(struct kvm_vcpu, arch.cp15[c5_IFSR]));
+  DEFINE(VCPU_ADFSR,           offsetof(struct kvm_vcpu, arch.cp15[c5_ADFSR]));
+  DEFINE(VCPU_AIFSR,           offsetof(struct kvm_vcpu, arch.cp15[c5_AIFSR]));
+  DEFINE(VCPU_DFAR,            offsetof(struct kvm_vcpu, arch.cp15[c6_DFAR]));
+  DEFINE(VCPU_IFAR,            offsetof(struct kvm_vcpu, arch.cp15[c6_IFAR]));
+  DEFINE(VCPU_PRRR,            offsetof(struct kvm_vcpu, arch.cp15[c10_PRRR]));
+  DEFINE(VCPU_NMRR,            offsetof(struct kvm_vcpu, arch.cp15[c10_NMRR]));
Quoted 9 lines

arch/arm/kernel/asm-offsets.c int main(void) · line 171

Quoted 5 lines
arch/arm/kernel/asm-offsets.c +5 -0
+  DEFINE(VCPU_VBAR,            offsetof(struct kvm_vcpu, arch.cp15[c12_VBAR]));
+  DEFINE(VCPU_CID,             offsetof(struct kvm_vcpu, arch.cp15[c13_CID]));
+  DEFINE(VCPU_TID_URW,         offsetof(struct kvm_vcpu, arch.cp15[c13_TID_URW]));
+  DEFINE(VCPU_TID_URO,         offsetof(struct kvm_vcpu, arch.cp15[c13_TID_URO]));
+  DEFINE(VCPU_TID_PRIV,                offsetof(struct kvm_vcpu, arch.cp15[c13_TID_PRIV]));

Could you instead define an offset for arch.cp15, then use scaled offsets from that in the assembly code?

that would require changing the enum in kvm_host.h to defines and either wrap that whole file in #ifndef __ASSEMBLY__ or move those defines to kvm_asm.h, not sure which I think is the most pretty:

arch/arm/include/asm/kvm_asm.h +28 -0
index 5315c69..99c0faf 100644
@@ -19,6 +19,34 @@
 #ifndef __ARM_KVM_ASM_H__
 #define __ARM_KVM_ASM_H__
 
+/* 0 is reserved as an invalid value. */
+#define c0_MPIDR	1	/* MultiProcessor ID Register */
+#define c0_CSSELR	2	/* Cache Size Selection Register */
+#define c1_SCTLR	3	/* System Control Register */
+#define c1_ACTLR	4	/* Auxilliary Control Register */
+#define c1_CPACR	5	/* Coprocessor Access Control */
+#define c2_TTBR0	6	/* Translation Table Base Register 0 */
+#define c2_TTBR0_high	7	/* TTBR0 top 32 bits */
+#define c2_TTBR1	8	/* Translation Table Base Register 1 */
+#define c2_TTBR1_high	9	/* TTBR1 top 32 bits */
+#define c2_TTBCR	10	/* Translation Table Base Control R. */
+#define c3_DACR		11	/* Domain Access Control Register */
+#define c5_DFSR		12	/* Data Fault Status Register */
+#define c5_IFSR		13	/* Instruction Fault Status Register */
+#define c5_ADFSR	14	/* Auxilary Data Fault Status R */
+#define c5_AIFSR	15	/* Auxilary Instrunction Fault Status R */
+#define c6_DFAR		16	/* Data Fault Address Register */
+#define c6_IFAR		17	/* Instruction Fault Address Register */
+#define c9_L2CTLR	18	/* Cortex A15 L2 Control Register */
+#define c10_PRRR	19	/* Primary Region Remap Register */
+#define c10_NMRR	20	/* Normal Memory Remap Register */
+#define c12_VBAR	21	/* Vector Base Address Register */
+#define c13_CID		22	/* Context ID Register */
+#define c13_TID_URW	23	/* Thread ID, User R/W */
+#define c13_TID_URO	24	/* Thread ID, User R/O */
+#define c13_TID_PRIV	25	/* Thread ID, Priveleged */
+#define NR_CP15_REGS	26	/* Number of regs (incl. invalid) */
+
 #define ARM_EXCEPTION_RESET	  0
 #define ARM_EXCEPTION_UNDEFINED   1
 #define ARM_EXCEPTION_SOFTWARE    2
arch/arm/include/asm/kvm_host.h +2 -32
index 9c4fbd4..f9b2ca6 100644
@@ -21,6 +21,7 @@
 
 #include <asm/kvm.h>
 #include <asm/fpstate.h>
+#include <asm/kvm_asm.h>
 
 #define KVM_MAX_VCPUS NR_CPUS
 #define KVM_MEMORY_SLOTS 32
@@ -73,37 +74,6 @@ struct kvm_mmu_memory_cache {
 	void *objects[KVM_NR_MEM_OBJS];
 };
 
-/* 0 is reserved as an invalid value. */
-enum cp15_regs {
-	c0_MPIDR=1,		/* MultiProcessor ID Register */
-	c0_CSSELR,		/* Cache Size Selection Register */
-	c1_SCTLR,		/* System Control Register */
-	c1_ACTLR,		/* Auxilliary Control Register */
-	c1_CPACR,		/* Coprocessor Access Control */
-	c2_TTBR0,		/* Translation Table Base Register 0 */
-	c2_TTBR0_high,		/* TTBR0 top 32 bits */
-	c2_TTBR1,		/* Translation Table Base Register 1 */
-	c2_TTBR1_high,		/* TTBR1 top 32 bits */
-	c2_TTBCR,		/* Translation Table Base Control R. */
-	c3_DACR,		/* Domain Access Control Register */
-	c5_DFSR,		/* Data Fault Status Register */
-	c5_IFSR,		/* Instruction Fault Status Register */
-	c5_ADFSR,		/* Auxilary Data Fault Status Register */
-	c5_AIFSR,		/* Auxilary Instruction Fault Status Register */
-	c6_DFAR,		/* Data Fault Address Register */
-	c6_IFAR,		/* Instruction Fault Address Register */
-	c9_L2CTLR,		/* Cortex A15 L2 Control Register */
-	c10_PRRR,		/* Primary Region Remap Register */
-	c10_NMRR,		/* Normal Memory Remap Register */
-	c12_VBAR,		/* Vector Base Address Register */
-	c13_CID,		/* Context ID Register */
-	c13_TID_URW,		/* Thread ID, User R/W */
-	c13_TID_URO,		/* Thread ID, User R/O */
-	c13_TID_PRIV,		/* Thread ID, Priveleged */
-
-	nr_cp15_regs
-};
-
 struct kvm_vcpu_arch {
 	struct kvm_regs regs;
 
@@ -111,7 +81,7 @@ struct kvm_vcpu_arch {
 	DECLARE_BITMAP(features, KVM_VCPU_MAX_FEATURES);
 
 	/* System control coprocessor (cp15) */
-	u32 cp15[nr_cp15_regs];
+	u32 cp15[NR_CP15_REGS];
 
 	/* The CPU type we expose to the VM */
 	u32 midr;
@@ -203,4 +173,5 @@ unsigned long kvm_arm_num_coproc_regs(struct

kvm_vcpu *vcpu);
struct kvm_one_reg;
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 *);
+
#endif /* __ARM_KVM_HOST_H__ */

arch/arm/kernel/asm-offsets.c +1 -21
index cf0b50e..1c4181e 100644
@@ -148,27 +148,7 @@ int main(void)
 #ifdef CONFIG_KVM_ARM_HOST
   DEFINE(VCPU_KVM,		offsetof(struct kvm_vcpu, kvm));
   DEFINE(VCPU_MIDR,		offsetof(struct kvm_vcpu, arch.midr));
-  DEFINE(VCPU_MPIDR,		offsetof(struct kvm_vcpu, arch.cp15[c0_MPIDR]));
-  DEFINE(VCPU_CSSELR,		offsetof(struct kvm_vcpu, arch.cp15[c0_CSSELR]));
-  DEFINE(VCPU_SCTLR,		offsetof(struct kvm_vcpu, arch.cp15[c1_SCTLR]));
-  DEFINE(VCPU_CPACR,		offsetof(struct kvm_vcpu, arch.cp15[c1_CPACR]));
-  DEFINE(VCPU_TTBR0,		offsetof(struct kvm_vcpu, arch.cp15[c2_TTBR0]));
-  DEFINE(VCPU_TTBR1,		offsetof(struct kvm_vcpu, arch.cp15[c2_TTBR1]));
-  DEFINE(VCPU_TTBCR,		offsetof(struct kvm_vcpu, arch.cp15[c2_TTBCR]));
-  DEFINE(VCPU_DACR,		offsetof(struct kvm_vcpu, arch.cp15[c3_DACR]));
-  DEFINE(VCPU_DFSR,		offsetof(struct kvm_vcpu, arch.cp15[c5_DFSR]));
-  DEFINE(VCPU_IFSR,		offsetof(struct kvm_vcpu, arch.cp15[c5_IFSR]));
-  DEFINE(VCPU_ADFSR,		offsetof(struct kvm_vcpu, arch.cp15[c5_ADFSR]));
-  DEFINE(VCPU_AIFSR,		offsetof(struct kvm_vcpu, arch.cp15[c5_AIFSR]));
-  DEFINE(VCPU_DFAR,		offsetof(struct kvm_vcpu, arch.cp15[c6_DFAR]));
-  DEFINE(VCPU_IFAR,		offsetof(struct kvm_vcpu, arch.cp15[c6_IFAR]));
-  DEFINE(VCPU_PRRR,		offsetof(struct kvm_vcpu, arch.cp15[c10_PRRR]));
-  DEFINE(VCPU_NMRR,		offsetof(struct kvm_vcpu, arch.cp15[c10_NMRR]));
-  DEFINE(VCPU_VBAR,		offsetof(struct kvm_vcpu, arch.cp15[c12_VBAR]));
-  DEFINE(VCPU_CID,		offsetof(struct kvm_vcpu, arch.cp15[c13_CID]));
-  DEFINE(VCPU_TID_URW,		offsetof(struct kvm_vcpu, arch.cp15[c13_TID_URW]));
-  DEFINE(VCPU_TID_URO,		offsetof(struct kvm_vcpu, arch.cp15[c13_TID_URO]));
-  DEFINE(VCPU_TID_PRIV,		offsetof(struct kvm_vcpu, arch.cp15[c13_TID_PRIV]));
+  DEFINE(VCPU_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));
arch/arm/kvm/coproc.c +2 -2
index 15977a6..759396a 100644
@@ -61,7 +61,7 @@ struct coproc_reg {
 	void (*reset)(struct kvm_vcpu *, const struct coproc_reg *);
 
 	/* Index into vcpu->arch.cp15[], or 0 if we don't need to save it. */
-	enum cp15_regs reg;
+	unsigned long reg;
 
 	/* Value (usually reset value) */
 	u64 val;
@@ -1097,7 +1097,7 @@ void kvm_reset_coprocs(struct kvm_vcpu *vcpu)
 	table = get_target_table(vcpu->arch.target, &num);
 	reset_coproc_regs(vcpu, table, num);
 
-	for (num = 1; num < nr_cp15_regs; num++)
+	for (num = 1; num < NR_CP15_REGS; num++)
 		if (vcpu->arch.cp15[num] == 0x42424242)
 			panic("Didn't reset vcpu->arch.cp15[%zi]", num);
 }
arch/arm/kvm/interrupts.S +44 -43

127 lines not on this page · raw message

Quoted 82 lines
Quoted 82 lines
arch/arm/kvm/arm.c +53 -0
index 8a87fc7..087f9d1 100644
@@ -41,6 +41,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");
@@ -50,6 +51,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)
 {
@@ -273,6 +278,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)
@@ -305,12 +311,169 @@ 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_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;
+
+       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;
+
Quoted 9 lines

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

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

Why on_each_cpu? The maintenance operations should be broadcast, right?

we need each cpu (that runs guests) to exit guests and pick up the new vmid in their vttbr.

Quoted 12 lines
Quoted 12 lines
arch/arm/kvm/arm.c +12 -0
+       }
+
+       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);
+       kvm->arch.vttbr = pgd_phys & ((1LLU << 40) - 1)
+                         & ~((2 << VTTBR_X) - 1);
+       kvm->arch.vttbr |= (u64)(kvm->arch.vmid) << 48;
+
Quoted 9 lines

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

Quoted 2 lines
arch/arm/kvm/arm.c +2 -0
+       spin_unlock(&kvm_vmid_lock);
+}

This smells like a watered-down version of the ASID allocator. Now, I can't really see much code sharing going on here, but perhaps your case is simpler... do you anticipate running more than 255 VMs in parallel? If not, then you could just invalidate the relevant TLB entries on VM shutdown and avoid the rollover case.

I want to support running more than 255 VMs in parallel. I think trying to share code with the ASID allocator complicates things without any real benefit.

Quoted 15 lines
Quoted 15 lines
arch/arm/kvm/interrupts.S +6 -0
index edf9ed5..cc9448b 100644
@@ -23,6 +23,12 @@
 #include <asm/asm-offsets.h>
 #include <asm/kvm_asm.h>
 #include <asm/kvm_arm.h>
+#include <asm/vfpmacros.h>
+
+#define VCPU_USR_REG(_reg_nr)  (VCPU_USR_REGS + (_reg_nr * 4))
+#define VCPU_USR_SP            (VCPU_USR_REG(13))
+#define VCPU_FIQ_REG(_reg_nr)  (VCPU_FIQ_REGS + (_reg_nr * 4))
+#define VCPU_FIQ_SPSR          (VCPU_FIQ_REG(7))
 
Quoted 10 lines

arch/arm/kvm/interrupts.S __kvm_hyp_code_start: · line 41

Quoted 5 lines
arch/arm/kvm/interrupts.S +0 -0
        .text
        .align  PAGE_SHIFT
@@ -34,7 +40,33 @@ __kvm_hyp_code_start:
 @  Flush per-VMID TLBs
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

This comment syntax crops up a few times in your .S files but doesn't match anything currently under arch/arm/. Please can you follow what we do there and use /* */ ?

sure

Quoted 16 lines
Quoted 5 lines
arch/arm/kvm/interrupts.S +4 -0
 ENTRY(__kvm_tlb_flush_vmid)
+       hvc     #0                      @ Switch to Hyp mode
+       push    {r2, r3}
+
+       add     r0, r0, #KVM_VTTBR

arch/arm/kvm/interrupts.S __kvm_hyp_code_start: · line 56

Quoted 10 lines
arch/arm/kvm/interrupts.S +10 -0
+       ldrd    r2, r3, [r0]
+       mcrr    p15, 6, r2, r3, c2      @ Write VTTBR
+       isb
+       mcr     p15, 0, r0, c8, c3, 0   @ TLBIALLIS (rt ignored)
+       dsb
+       isb
+       mov     r2, #0
+       mov     r3, #0
+       mcrr    p15, 6, r2, r3, c2      @ Back to VMID #0
+       isb
Quoted 2 lines

Do you need this isb, given that you're about to do an hvc?

they're gone

Quoted 9 lines
Quoted 9 lines
arch/arm/kvm/interrupts.S +2 -0
+       pop     {r2, r3}
+       hvc     #0                      @ Back to SVC
        bx      lr
 ENDPROC(__kvm_tlb_flush_vmid)
 
@@ -42,26 +74,702 @@ ENDPROC(__kvm_tlb_flush_vmid)
 @  Flush TLBs and instruction caches of current CPU for all VMIDs
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
Quoted 14 lines

arch/arm/kvm/interrupts.S ENDPROC(__kvm_tlb_flush_vmid) · line 87

Quoted 11 lines
arch/arm/kvm/interrupts.S +10 -0
+/*
+ * void __kvm_flush_vm_context(void);
+ */
 ENTRY(__kvm_flush_vm_context)
+       hvc     #0                      @ switch to hyp-mode
+
+       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
+       dsb
+       isb

Likewise.

ditto

Quoted 30 lines
Quoted 30 lines
arch/arm/kvm/interrupts.S +27 -0
+       hvc     #0                      @ switch back to svc-mode, see hyp_svc
        bx      lr
 ENDPROC(__kvm_flush_vm_context)
 
+/* These are simply for the macros to work - value don't have meaning */
+.equ usr, 0
+.equ svc, 1
+.equ abt, 2
+.equ und, 3
+.equ irq, 4
+.equ fiq, 5
+
+.macro store_mode_state base_reg, mode
+       .if \mode == usr
+       mrs     r2, SP_usr
+       mov     r3, lr
+       stmdb   \base_reg!, {r2, r3}
+       .elseif \mode != fiq
+       mrs     r2, SP_\mode
+       mrs     r3, LR_\mode
+       mrs     r4, SPSR_\mode
+       stmdb   \base_reg!, {r2, r3, r4}
+       .else
+       mrs     r2, r8_fiq
+       mrs     r3, r9_fiq
+       mrs     r4, r10_fiq
+       mrs     r5, r11_fiq
+       mrs     r6, r12_fiq
+       mrs     r7, SP_fiq
+       mrs     r8, LR_fiq
Quoted 9 lines

arch/arm/kvm/interrupts.S ENDPROC(__kvm_tlb_flush_vmid) · line 121

Quoted 4 lines
arch/arm/kvm/interrupts.S +4 -0
+       mrs     r9, SPSR_fiq
+       stmdb   \base_reg!, {r2-r9}
+       .endif
+.endm

Perhaps you could stick the assembly macros into a separate file, like we do in assembler.h, so the code is more readable and they can be reused if need-be?

This is a lot of code to stick in a header file (hard to read within C constructs, no assembly syntax highlighting, cannot use @ for end-of-line comments), but I factored it out to interrupts_header.S, which makes it nicer to read interrupts.S and it should be easy to factor out pieces if ever needed anywhere else.

-Christoffer

#56 lore raw

PATCH 11/15 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  |    7 
 arch/arm/include/asm/kvm_emulate.h |    6 
 arch/arm/include/asm/kvm_host.h    |    5 
 arch/arm/kvm/arm.c                 |  166 ++++++++++
 arch/arm/kvm/coproc.c              |  592 ++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/emulate.c             |  207 +++++++++++++
 arch/arm/kvm/trace.h               |   30 ++
 8 files changed, 1020 insertions(+), 2 deletions(-)
arch/arm/include/asm/kvm_arm.h +9 -0
index ee345a6..ae586c1 100644
@@ -76,6 +76,11 @@
 			HCR_SWIO | HCR_TIDCP)
 #define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
 
+/* System Control Register (SCTLR) bits */
+#define SCTLR_TE	(1 << 30)
+#define SCTLR_EE	(1 << 25)
+#define SCTLR_V		(1 << 13)
+
 /* Hyp System Control Register (HSCTLR) bits */
 #define HSCTLR_TE	(1 << 30)
 #define HSCTLR_EE	(1 << 25)
@@ -153,6 +158,10 @@
 #define HSR_ISS		(HSR_IL - 1)
 #define HSR_ISV_SHIFT	(24)
 #define HSR_ISV		(1U << HSR_ISV_SHIFT)
+#define HSR_CV_SHIFT	(24)
+#define HSR_CV		(1U << HSR_CV_SHIFT)
+#define HSR_COND_SHIFT	(20)
+#define HSR_COND	(0xfU << HSR_COND_SHIFT)
 
 #define HSR_EC_UNKNOWN	(0x00)
 #define HSR_EC_WFI	(0x01)
arch/arm/include/asm/kvm_coproc.h +7 -0
index b6d023d..c451fb4 100644
@@ -21,4 +21,11 @@
 
 void kvm_reset_coprocs(struct kvm_vcpu *vcpu);
 
+int kvm_handle_cp10_id(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp_0_13_access(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run);
+void kvm_coproc_table_init(void);
 #endif /* __ARM_KVM_COPROC_H__ */
arch/arm/include/asm/kvm_emulate.h +6 -0
index 9e29335..6ddfae2 100644
@@ -51,6 +51,12 @@ static inline enum vcpu_mode vcpu_mode(struct kvm_vcpu *vcpu)
 	return mode;
 }
 
+int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run);
+void kvm_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);
+
 /*
  * Return the SPSR for the specified mode of the virtual CPU.
  */
arch/arm/include/asm/kvm_host.h +5 -0
index ca4c079..c98dcd7 100644
@@ -109,6 +109,7 @@ enum cp15_regs {
 	c5_AIFSR,		/* Auxilary Instruction Fault Status Register */
 	c6_DFAR,		/* Data Fault Address Register */
 	c6_IFAR,		/* Instruction Fault Address Register */
+	c9_L2CTLR,		/* Cortex A15 L2 Control Register */
 	c10_PRRR,		/* Primary Region Remap Register */
 	c10_NMRR,		/* Normal Memory Remap Register */
 	c12_VBAR,		/* Vector Base Address Register */
@@ -146,6 +147,10 @@ struct kvm_vcpu_arch {
 	 * Anything that is not used directly from assembly code goes
 	 * here.
 	 */
+	/* dcache set/way operation pending */
+	int last_pcpu;
+	cpumask_t require_dcache_flush;
+
 	/* IO related fields */
 	struct {
 		bool sign_extend;	/* for byte/halfword loads */
arch/arm/kvm/arm.c +164 -2

207 lines not on this page · raw message

arch/arm/kvm/coproc.c +592 -0

600 lines not on this page · raw message

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

217 lines not on this page · raw message

arch/arm/kvm/trace.h +30 -0
index a283eef..772e045 100644
@@ -64,6 +64,36 @@ TRACE_EVENT(kvm_irq_line,
 		  __entry->type, __entry->vcpu_idx, __entry->irq_num, __entry->level)
 );
 
+/* Architecturally implementation defined CP15 register access */
+TRACE_EVENT(kvm_emulate_cp15_imp,
+	TP_PROTO(unsigned long Op1, unsigned long Rt1, unsigned long CRn,
+		 unsigned long CRm, unsigned long Op2, bool is_write),
+	TP_ARGS(Op1, Rt1, CRn, CRm, Op2, is_write),
+
+	TP_STRUCT__entry(
+		__field(	unsigned int,	Op1		)
+		__field(	unsigned int,	Rt1		)
+		__field(	unsigned int,	CRn		)
+		__field(	unsigned int,	CRm		)
+		__field(	unsigned int,	Op2		)
+		__field(	bool,		is_write	)
+	),
+
+	TP_fast_assign(
+		__entry->is_write		= is_write;
+		__entry->Op1			= Op1;
+		__entry->Rt1			= Rt1;
+		__entry->CRn			= CRn;
+		__entry->CRm			= CRm;
+		__entry->Op2			= Op2;
+	),
+
+	TP_printk("Implementation defined CP15: %s\tp15, %u, r%u, c%u, c%u, %u",
+			(__entry->is_write) ? "mcr" : "mrc",
+			__entry->Op1, __entry->Rt1, __entry->CRn,
+			__entry->CRm, __entry->Op2)
+);
+
 #endif /* _TRACE_KVM_H */
 
 #undef TRACE_INCLUDE_PATH
#57 lore raw

PATCH 12/15 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 |   46 +++++
 arch/arm/include/asm/kvm_coproc.h |    9 +
 arch/arm/include/asm/kvm_host.h   |    4 
 arch/arm/kvm/coproc.c             |  332 +++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/guest.c              |    9 +
 5 files changed, 396 insertions(+), 4 deletions(-)
Documentation/virtual/kvm/api.txt +46 -0
index 605986f..035d4cb 100644
@@ -1756,6 +1756,13 @@ 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:32>
 
+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
 
 Capability: KVM_CAP_ONE_REG
@@ -2048,6 +2055,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 c451fb4..05943ec 100644
@@ -27,5 +27,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 c98dcd7..97608d7 100644
@@ -26,6 +26,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 NUM_FEATURES 0
 
@@ -191,6 +192,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 +332 -0

358 lines not on this page · raw message

arch/arm/kvm/guest.c +5 -4
index 19a5389..8ad3df7 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 }
@@ -109,7 +110,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);
 }
 
 /**
@@ -128,7 +129,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)
@@ -141,7 +142,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)
@@ -154,7 +155,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,
#58 lore raw

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

In reply to Christoffer Dall

From: Christoffer Dall <[email protected]>

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

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 may be a subtle bug hidden in, 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             |  155 ++++++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/trace.h           |   28 +++++++
 4 files changed, 193 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_arm.h +9 -0
index ae586c1..4cff3b7 100644
@@ -158,11 +158,20 @@
 #define HSR_ISS		(HSR_IL - 1)
 #define HSR_ISV_SHIFT	(24)
 #define HSR_ISV		(1U << HSR_ISV_SHIFT)
+#define HSR_FSC		(0x3f)
+#define HSR_FSC_TYPE	(0x3c)
+#define HSR_WNR		(1 << 6)
 #define HSR_CV_SHIFT	(24)
 #define HSR_CV		(1U << HSR_CV_SHIFT)
 #define HSR_COND_SHIFT	(20)
 #define HSR_COND	(0xfU << HSR_COND_SHIFT)
 
+#define FSC_FAULT	(0x04)
+#define FSC_PERM	(0x0c)
+
+/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
+#define HPFAR_MASK	(~0xf)
+
 #define HSR_EC_UNKNOWN	(0x00)
 #define HSR_EC_WFI	(0x01)
 #define HSR_EC_CP15_32	(0x03)
arch/arm/include/asm/kvm_asm.h +2 -0
index 201ec1f..40ee099 100644
@@ -43,6 +43,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 +154 -1

173 lines not on this page · raw message

arch/arm/kvm/trace.h +28 -0
index 772e045..40606c9 100644
@@ -39,6 +39,34 @@ 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 hdfar, unsigned long hifar,
+		 unsigned long ipa),
+	TP_ARGS(vcpu_pc, hsr, hdfar, hifar, ipa),
+
+	TP_STRUCT__entry(
+		__field(	unsigned long,	vcpu_pc		)
+		__field(	unsigned long,	hsr		)
+		__field(	unsigned long,	hdfar		)
+		__field(	unsigned long,	hifar		)
+		__field(	unsigned long,	ipa		)
+	),
+
+	TP_fast_assign(
+		__entry->vcpu_pc		= vcpu_pc;
+		__entry->hsr			= hsr;
+		__entry->hdfar			= hdfar;
+		__entry->hifar			= hifar;
+		__entry->ipa			= ipa;
+	),
+
+	TP_printk("guest fault at PC %#08lx (hdfar %#08lx, hifar %#08lx, "
+		  "ipa %#08lx, hsr %#08lx",
+		  __entry->vcpu_pc, __entry->hdfar, __entry->hifar,
+		  __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),
#59 lore raw

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

Quoted 170 lines

-----Original Message-----
From: kvm-owner at vger.kernel.org [mailto:kvm-owner at vger.kernel.org] On Behalf Of Christoffer Dall
Sent: Sunday, September 16, 2012 12:36 AM
To: kvm at vger.kernel.org; linux-arm-kernel at lists.infradead.org; kvmarm at lists.cs.columbia.edu
Subject: [PATCH 13/15] KVM: ARM: Handle guest faults in KVM

From: Christoffer Dall <[email protected]>

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 may be a subtle bug hidden in, 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             |  155
++++++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/trace.h           |   28 +++++++
 4 files changed, 193 insertions(+), 1 deletion(-)
(quoted diff) +0 -0

b/arch/arm/include/asm/kvm_arm.h index ae586c1..4cff3b7 100644

arch/arm/include/asm/kvm_arm.h +9 -0
@@ -158,11 +158,20 @@
 #define HSR_ISS		(HSR_IL - 1)
 #define HSR_ISV_SHIFT	(24)
 #define HSR_ISV		(1U << HSR_ISV_SHIFT)
+#define HSR_FSC		(0x3f)
+#define HSR_FSC_TYPE	(0x3c)
+#define HSR_WNR		(1 << 6)
 #define HSR_CV_SHIFT	(24)
 #define HSR_CV		(1U << HSR_CV_SHIFT)
 #define HSR_COND_SHIFT	(20)
 #define HSR_COND	(0xfU << HSR_COND_SHIFT)
 
+#define FSC_FAULT	(0x04)
+#define FSC_PERM	(0x0c)
+
+/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
+#define HPFAR_MASK	(~0xf)
+
 #define HSR_EC_UNKNOWN	(0x00)
 #define HSR_EC_WFI	(0x01)
 #define HSR_EC_CP15_32	(0x03)
(quoted diff) +0 -0

b/arch/arm/include/asm/kvm_asm.h index 201ec1f..40ee099 100644

arch/arm/include/asm/kvm_asm.h +2 -0
@@ -43,6 +43,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 index renamed from arch/arm/kvm/mmu.c +0 -0

ea17a97..52cc280 100644

arch/arm/kvm/mmu.c +11 -0
@@ -21,10 +21,16 @@
 #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 <asm/kvm_asm.h>
+#include <trace/events/kvm.h>
+
+#include "trace.h"
 
 static DEFINE_MUTEX(kvm_hyp_pgd_mutex);
 
@@ -490,9 +496,156 @@ 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
+	 * (or another VM) may have used this page at the same virtual

address

arch/arm/kvm/mmu.c +2 -0
+	 * 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

arch/arm/kvm/mmu.c +1 -0
+	 * we are using a VIPT cache we need to invalidate the entire

icache -

arch/arm/kvm/mmu.c +35 -0
+	 * 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();
+	}
+}
+
+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, pfn_existing = KVM_PFN_ERR_BAD;
+	int ret;
+	bool write_fault, writable;
+	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;
+
+	if (fault_status == FSC_PERM && !write_fault) {
+		kvm_err("Unexpected L2 read permission error\n");
+		return -EFAULT;
+	}
+
+	/*
+	 * If this is a write fault (think COW) we need to make sure the
+	 * existing page, which other CPUs might still read, doesn't go

away

arch/arm/kvm/mmu.c +2 -0
+	 * from under us, by calling gfn_to_pfn_prot(write_fault=true).
+	 * Therefore, we call gfn_to_pfn_prot(write_fault=false), which

will

arch/arm/kvm/mmu.c +3 -0
+	 * pin the existing page, then we get a new page for the user space
+	 * pte and map this in the stage-2 table where we also make sure to
+	 * flush the TLB for the VM, if there was an existing entry (the

entry

arch/arm/kvm/mmu.c +12 -0
+	 * was updated setting the write flag to the potentially new page).
+	 */
+	if (fault_status == FSC_PERM) {
+		pfn_existing = gfn_to_pfn_prot(vcpu->kvm, gfn, false, NULL);
+		if (is_error_pfn(pfn_existing))
+			return -EFAULT;
+	}
+
+	pfn = gfn_to_pfn_prot(vcpu->kvm, gfn, write_fault, &writable);
+	if (is_error_pfn(pfn)) {
+		ret = -EFAULT;
+		goto out_put_existing;

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

Quoted 10 lines
arch/arm/kvm/mmu.c +10 -0
+	}
+
+	/* We need minimum second+third level pages */
+	ret = mmu_topup_memory_cache(memcache, 2, KVM_NR_MEM_OBJS);
+	if (ret)
+		goto out;
+	new_pte = pfn_pte(pfn, PAGE_KVM_GUEST);
+	if (writable)
+		pte_val(new_pte) |= L_PTE2_WRITE;
+	coherent_icache_guest_page(vcpu->kvm, gfn);

why don't you flush icache only when guest has mapped executable page as __sync_icache_dcache function does currently?

Quoted 130 lines
arch/arm/kvm/mmu.c +10 -0
+
+	spin_lock(&vcpu->kvm->arch.pgd_lock);
+	stage2_set_pte(vcpu->kvm, memcache, fault_ipa, &new_pte);
+	spin_unlock(&vcpu->kvm->arch.pgd_lock);
+
+out:
+	/*
+	 * XXX TODO FIXME:
+	 * This is _really_ *weird* !!!
+	 * We should only be calling the _dirty verison when we map

something

arch/arm/kvm/mmu.c +71 -1
+	 * writable, but this causes memory failures in guests under heavy
+	 * memory pressure on the host and heavy swapping.
+	 */
+	kvm_release_pfn_dirty(pfn);
+out_put_existing:
+	if (!is_error_pfn(pfn_existing))
+		kvm_release_pfn_clean(pfn_existing);
+	return ret;
+}
+
+/**
+ * kvm_handle_guest_abort - handles all 2nd stage aborts
+ * @vcpu:	the VCPU pointer
+ * @run:	the kvm_run structure
+ *
+ * Any abort that gets to the host is almost guaranteed to be caused by
+a
+ * missing second stage translation table entry, which can mean that
+either the
+ * guest simply needs more memory and we must allocate an appropriate
+page or it
+ * can mean that the guest tried to access I/O memory, which is
+emulated by user
+ * space. The distinction is based on the IPA causing the fault and
+whether this
+ * memory region has been registered as standard RAM by user space.
+ */
 int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)  {
-	return -EINVAL;
+	unsigned long hsr_ec;
+	unsigned long fault_status;
+	phys_addr_t fault_ipa;
+	struct kvm_memory_slot *memslot = NULL;
+	bool is_iabt;
+	gfn_t gfn;
+	int ret;
+
+	hsr_ec = vcpu->arch.hsr >> HSR_EC_SHIFT;
+	is_iabt = (hsr_ec == HSR_EC_IABT);
+	fault_ipa = ((phys_addr_t)vcpu->arch.hpfar & HPFAR_MASK) << 8;
+
+	trace_kvm_guest_fault(*vcpu_pc(vcpu), vcpu->arch.hsr,
+			      vcpu->arch.hdfar, vcpu->arch.hifar, fault_ipa);
+
+	/* Check the stage-2 fault is trans. fault or write fault */
+	fault_status = (vcpu->arch.hsr & HSR_FSC_TYPE);
+	if (fault_status != FSC_FAULT && fault_status != FSC_PERM) {
+		kvm_err("Unsupported fault status: EC=%#lx DFCS=%#lx\n",
+			hsr_ec, fault_status);
+		return -EFAULT;
+	}
+
+	gfn = fault_ipa >> PAGE_SHIFT;
+	if (!kvm_is_visible_gfn(vcpu->kvm, gfn)) {
+		if (is_iabt) {
+			/* Prefetch Abort on I/O address */
+			kvm_inject_pabt(vcpu, vcpu->arch.hifar);
+			return 1;
+		}
+
+		kvm_pr_unimpl("I/O address abort...");
+		return 0;
+	}
+
+	memslot = gfn_to_memslot(vcpu->kvm, gfn);
+	if (!memslot->user_alloc) {
+		kvm_err("non user-alloc memslots not supported\n");
+		return -EINVAL;
+	}
+
+	ret = user_mem_abort(vcpu, fault_ipa, gfn, memslot,
+			     is_iabt, fault_status);
+	return ret ? ret : 1;
 }
 
 static void handle_hva_to_gpa(struct kvm *kvm, unsigned long hva, diff --

git a/arch/arm/kvm/trace.h b/arch/arm/kvm/trace.h index 772e045..40606c9 100644

arch/arm/kvm/trace.h +28 -1
@@ -39,6 +39,34 @@ 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 hdfar, unsigned long hifar,
+		 unsigned long ipa),
+	TP_ARGS(vcpu_pc, hsr, hdfar, hifar, ipa),
+
+	TP_STRUCT__entry(
+		__field(	unsigned long,	vcpu_pc		)
+		__field(	unsigned long,	hsr		)
+		__field(	unsigned long,	hdfar		)
+		__field(	unsigned long,	hifar		)
+		__field(	unsigned long,	ipa		)
+	),
+
+	TP_fast_assign(
+		__entry->vcpu_pc		= vcpu_pc;
+		__entry->hsr			= hsr;
+		__entry->hdfar			= hdfar;
+		__entry->hifar			= hifar;
+		__entry->ipa			= ipa;
+	),
+
+	TP_printk("guest fault at PC %#08lx (hdfar %#08lx, hifar %#08lx, "
+		  "ipa %#08lx, hsr %#08lx",
+		  __entry->vcpu_pc, __entry->hdfar, __entry->hifar,
+		  __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),
 
--

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

#60 lore raw

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

Quoted 29 lines
Quoted 29 lines
arch/arm/kvm/mmu.c +4 -0
+
+     /*
+      * If this is a write fault (think COW) we need to make sure the
+      * existing page, which other CPUs might still read, doesn't go

away

(quoted diff) +2 -0
+      * from under us, by calling gfn_to_pfn_prot(write_fault=true).
+      * Therefore, we call gfn_to_pfn_prot(write_fault=false), which

will

arch/arm/kvm/mmu.c +3 -0
+      * pin the existing page, then we get a new page for the user space
+      * pte and map this in the stage-2 table where we also make sure to
+      * flush the TLB for the VM, if there was an existing entry (the

entry

arch/arm/kvm/mmu.c +17 -0
+      * was updated setting the write flag to the potentially new page).
+      */
+     if (fault_status == FSC_PERM) {
+             pfn_existing = gfn_to_pfn_prot(vcpu->kvm, gfn, false, NULL);
+             if (is_error_pfn(pfn_existing))
+                     return -EFAULT;
+     }
+
+     pfn = gfn_to_pfn_prot(vcpu->kvm, gfn, write_fault, &writable);
+     if (is_error_pfn(pfn)) {
+             ret = -EFAULT;
+             goto out_put_existing;
+     }
+
+     /* We need minimum second+third level pages */
+     ret = mmu_topup_memory_cache(memcache, 2, KVM_NR_MEM_OBJS);
+     if (ret)
Quoted 10 lines

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

Quoted 5 lines
arch/arm/kvm/mmu.c +5 -0
+             goto out;
+     new_pte = pfn_pte(pfn, PAGE_KVM_GUEST);
+     if (writable)
+             pte_val(new_pte) |= L_PTE2_WRITE;
+     coherent_icache_guest_page(vcpu->kvm, gfn);

why don't you flush icache only when guest has mapped executable page as __sync_icache_dcache function does currently?

because we don't know if the guest will map the page executable. The guest may read the page through a normal load, which causes the fault, and subsequently execute it (even possible through different guest mappings). The only way to see this happening would be to mark all pages as non-executable and catch the fault when it occurs - unfortunately the HPFAR which gives us the IPA is not populated on execute never faults, so we would have to translate the PC's va to ipa using cp15 functionality when this happens, which is then also racy with other CPUs. So the question is really if this will even be an optimization, but it's definitely something that requires further investigation.

-Christoffer

#61 lore raw

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

Quoted 20 lines

-----Original Message-----
From: kvm-owner at vger.kernel.org [mailto:kvm-owner at vger.kernel.org] On Behalf Of Christoffer Dall
Sent: Tuesday, September 25, 2012 9:39 PM
To: Min-gyu Kim
Cc: kvm at vger.kernel.org; linux-arm-kernel at lists.infradead.org; kvmarm at lists.cs.columbia.edu; ???
Subject: Re: [PATCH 13/15] KVM: ARM: Handle guest faults in KVM

Quoted 9 lines
Quoted 9 lines
arch/arm/kvm/mmu.c +4 -0
+
+     /*
+      * If this is a write fault (think COW) we need to make sure the
+      * existing page, which other CPUs might still read, doesn't go

away

(quoted diff) +2 -0
+      * from under us, by calling gfn_to_pfn_prot(write_fault=true).
+      * Therefore, we call gfn_to_pfn_prot(write_fault=false), which

will
+ * pin the existing page, then we get a new page for the user

Quoted 7 lines

space

Quoted 1 line
Quoted 1 line

+ * pte and map this in the stage-2 table where we also make sure

to

Quoted 4 lines
Quoted 4 lines

+ * flush the TLB for the VM, if there was an existing entry
+ (the
entry
+ * was updated setting the write flag to the potentially new

page).

Quoted 3 lines
Quoted 3 lines

arch/arm/kvm/mmu.c out:

Quoted 3 lines
arch/arm/kvm/mmu.c +3 -0
+      */
+     if (fault_status == FSC_PERM) {
+             pfn_existing = gfn_to_pfn_prot(vcpu->kvm, gfn, false,

NULL);

Quoted 24 lines
Quoted 13 lines
Quoted 13 lines
arch/arm/kvm/mmu.c +13 -0
+             if (is_error_pfn(pfn_existing))
+                     return -EFAULT;
+     }
+
+     pfn = gfn_to_pfn_prot(vcpu->kvm, gfn, write_fault, &writable);
+     if (is_error_pfn(pfn)) {
+             ret = -EFAULT;
+             goto out_put_existing;
+     }
+
+     /* We need minimum second+third level pages */
+     ret = mmu_topup_memory_cache(memcache, 2, KVM_NR_MEM_OBJS);
+     if (ret)
Quoted 10 lines

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

Quoted 5 lines
arch/arm/kvm/mmu.c +5 -0
+             goto out;
+     new_pte = pfn_pte(pfn, PAGE_KVM_GUEST);
+     if (writable)
+             pte_val(new_pte) |= L_PTE2_WRITE;
+     coherent_icache_guest_page(vcpu->kvm, gfn);

why don't you flush icache only when guest has mapped executable page as __sync_icache_dcache function does currently?

Quoted 10 lines

because we don't know if the guest will map the page executable. The guest may read the page through a normal load, which causes the fault, and subsequently execute it (even possible through different guest mappings). The only way to see this happening would be to mark all pages as non- executable and catch the fault when it occurs - unfortunately the HPFAR which gives us the IPA is not populated on execute never faults, so we would have to translate the PC's va to ipa using cp15 functionality when this happens, which is then also racy with other CPUs. So the question is really if this will even be an optimization, but it's definitely something that requires further investigation.

OK. I understand your point.

But if guest maps a page for execution, guest will flush Icache from __sync_icache_dcache. Then coherent_icache_guest_page doesn't seem to be
necessary again. One thing I'm not sure in this case is when guest maps for kernel executable page(module loading) and it reuses the kernel executable page
from host(module unloading). But in that case, I think it is possible to reduce
the number of flush by limiting the address range for flush.

Quoted 6 lines

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

#62 lore raw

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

Quoted 69 lines · Min-gyu Kim
Quoted 20 lines

-----Original Message-----
From: kvm-owner at vger.kernel.org [mailto:kvm-owner at vger.kernel.org] On Behalf Of Christoffer Dall
Sent: Tuesday, September 25, 2012 9:39 PM
To: Min-gyu Kim
Cc: kvm at vger.kernel.org; linux-arm-kernel at lists.infradead.org; kvmarm at lists.cs.columbia.edu; ???
Subject: Re: [PATCH 13/15] KVM: ARM: Handle guest faults in KVM

Quoted 9 lines
Quoted 9 lines
arch/arm/kvm/mmu.c +4 -0
+
+     /*
+      * If this is a write fault (think COW) we need to make sure the
+      * existing page, which other CPUs might still read, doesn't go

away

(quoted diff) +2 -0
+      * from under us, by calling gfn_to_pfn_prot(write_fault=true).
+      * Therefore, we call gfn_to_pfn_prot(write_fault=false), which

will
+ * pin the existing page, then we get a new page for the user

Quoted 7 lines

space

Quoted 1 line
Quoted 1 line

+ * pte and map this in the stage-2 table where we also make sure

to

Quoted 4 lines
Quoted 4 lines

+ * flush the TLB for the VM, if there was an existing entry
+ (the
entry
+ * was updated setting the write flag to the potentially new

page).

Quoted 3 lines
Quoted 3 lines

arch/arm/kvm/mmu.c out:

Quoted 3 lines
arch/arm/kvm/mmu.c +3 -0
+      */
+     if (fault_status == FSC_PERM) {
+             pfn_existing = gfn_to_pfn_prot(vcpu->kvm, gfn, false,

NULL);

Quoted 24 lines
Quoted 13 lines
Quoted 13 lines
arch/arm/kvm/mmu.c +13 -0
+             if (is_error_pfn(pfn_existing))
+                     return -EFAULT;
+     }
+
+     pfn = gfn_to_pfn_prot(vcpu->kvm, gfn, write_fault, &writable);
+     if (is_error_pfn(pfn)) {
+             ret = -EFAULT;
+             goto out_put_existing;
+     }
+
+     /* We need minimum second+third level pages */
+     ret = mmu_topup_memory_cache(memcache, 2, KVM_NR_MEM_OBJS);
+     if (ret)
Quoted 10 lines

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

Quoted 5 lines
arch/arm/kvm/mmu.c +5 -0
+             goto out;
+     new_pte = pfn_pte(pfn, PAGE_KVM_GUEST);
+     if (writable)
+             pte_val(new_pte) |= L_PTE2_WRITE;
+     coherent_icache_guest_page(vcpu->kvm, gfn);

why don't you flush icache only when guest has mapped executable page as __sync_icache_dcache function does currently?

Quoted 10 lines

because we don't know if the guest will map the page executable. The guest may read the page through a normal load, which causes the fault, and subsequently execute it (even possible through different guest mappings). The only way to see this happening would be to mark all pages as non- executable and catch the fault when it occurs - unfortunately the HPFAR which gives us the IPA is not populated on execute never faults, so we would have to translate the PC's va to ipa using cp15 functionality when this happens, which is then also racy with other CPUs. So the question is really if this will even be an optimization, but it's definitely something that requires further investigation.

OK. I understand your point.

Quoted 11 lines

But if guest maps a page for execution, guest will flush Icache from __sync_icache_dcache. Then coherent_icache_guest_page doesn't seem to be
necessary again. One thing I'm not sure in this case is when guest maps for kernel executable page(module loading) and it reuses the kernel executable page
from host(module unloading). But in that case, I think it is possible to reduce
the number of flush by limiting the address range for flush.

the guest kernel will flush the dcache when it maps the page initially. However, when we swap on the host we might use that same page at the same virtual address as the original guest in another guest or on the host, and the icache will now contain incorrect code that can be executed from the guest in case of vipt caches. In the case of pipt caches, if the page is used for instructions on any virtual address, incorrect entries can be executed from the icache once this page is used for guest instructions again. If you have suggestions on how to optimize this, it would be great, but I see not good way around it.

#63 lore raw

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

In reply to Min-gyu Kim
Quoted 69 lines · Min-gyu Kim
Quoted 20 lines

-----Original Message-----
From: kvm-owner at vger.kernel.org [mailto:kvm-owner at vger.kernel.org] On Behalf Of Christoffer Dall
Sent: Tuesday, September 25, 2012 9:39 PM
To: Min-gyu Kim
Cc: kvm at vger.kernel.org; linux-arm-kernel at lists.infradead.org; kvmarm at lists.cs.columbia.edu; ???
Subject: Re: [PATCH 13/15] KVM: ARM: Handle guest faults in KVM

Quoted 9 lines
Quoted 9 lines
arch/arm/kvm/mmu.c +4 -0
+
+     /*
+      * If this is a write fault (think COW) we need to make sure the
+      * existing page, which other CPUs might still read, doesn't go

away

(quoted diff) +2 -0
+      * from under us, by calling gfn_to_pfn_prot(write_fault=true).
+      * Therefore, we call gfn_to_pfn_prot(write_fault=false), which

will
+ * pin the existing page, then we get a new page for the user

Quoted 7 lines

space

Quoted 1 line
Quoted 1 line

+ * pte and map this in the stage-2 table where we also make sure

to

Quoted 4 lines
Quoted 4 lines

+ * flush the TLB for the VM, if there was an existing entry
+ (the
entry
+ * was updated setting the write flag to the potentially new

page).

Quoted 3 lines
Quoted 3 lines

arch/arm/kvm/mmu.c out:

Quoted 3 lines
arch/arm/kvm/mmu.c +3 -0
+      */
+     if (fault_status == FSC_PERM) {
+             pfn_existing = gfn_to_pfn_prot(vcpu->kvm, gfn, false,

NULL);

Quoted 24 lines
Quoted 13 lines
Quoted 13 lines
arch/arm/kvm/mmu.c +13 -0
+             if (is_error_pfn(pfn_existing))
+                     return -EFAULT;
+     }
+
+     pfn = gfn_to_pfn_prot(vcpu->kvm, gfn, write_fault, &writable);
+     if (is_error_pfn(pfn)) {
+             ret = -EFAULT;
+             goto out_put_existing;
+     }
+
+     /* We need minimum second+third level pages */
+     ret = mmu_topup_memory_cache(memcache, 2, KVM_NR_MEM_OBJS);
+     if (ret)
Quoted 10 lines

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

Quoted 5 lines
arch/arm/kvm/mmu.c +5 -0
+             goto out;
+     new_pte = pfn_pte(pfn, PAGE_KVM_GUEST);
+     if (writable)
+             pte_val(new_pte) |= L_PTE2_WRITE;
+     coherent_icache_guest_page(vcpu->kvm, gfn);

why don't you flush icache only when guest has mapped executable page as __sync_icache_dcache function does currently?

Quoted 10 lines

because we don't know if the guest will map the page executable. The guest may read the page through a normal load, which causes the fault, and subsequently execute it (even possible through different guest mappings). The only way to see this happening would be to mark all pages as non- executable and catch the fault when it occurs - unfortunately the HPFAR which gives us the IPA is not populated on execute never faults, so we would have to translate the PC's va to ipa using cp15 functionality when this happens, which is then also racy with other CPUs. So the question is really if this will even be an optimization, but it's definitely something that requires further investigation.

OK. I understand your point.

Quoted 9 lines

But if guest maps a page for execution, guest will flush Icache from __sync_icache_dcache. Then coherent_icache_guest_page doesn't seem to be
necessary again. One thing I'm not sure in this case is when guest maps for kernel executable page(module loading) and it reuses the kernel executable page
from host(module unloading). But in that case, I think it is possible to reduce
the number of flush by limiting the address range for flush.

I think you're missing the major point:
When the guest maps a page for execution, it knows it has to synchronize Icache and Dcache. But the guest never knows when we swap out a page because of memory pressure.

When the guest eventually faults that page back in, chances are it will be a different physical page, and the cache content may be inconsistent. We must then sync Icache/Dcache for this page.

Now, as Christoffer mentioned, there's a number of schemes we could potentially use to mitigate this effect (using the XN bit in the Stage2 page tables), but it remains to be seen how effective it will be.

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

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

In reply to Christoffer Dall
Quoted 38 lines · Christoffer Dall
Quoted 29 lines
Quoted 29 lines
arch/arm/kvm/mmu.c +4 -0
+
+     /*
+      * If this is a write fault (think COW) we need to make sure the
+      * existing page, which other CPUs might still read, doesn't go

away

(quoted diff) +2 -0
+      * from under us, by calling gfn_to_pfn_prot(write_fault=true).
+      * Therefore, we call gfn_to_pfn_prot(write_fault=false), which

will

arch/arm/kvm/mmu.c +3 -0
+      * pin the existing page, then we get a new page for the user space
+      * pte and map this in the stage-2 table where we also make sure to
+      * flush the TLB for the VM, if there was an existing entry (the

entry

arch/arm/kvm/mmu.c +17 -0
+      * was updated setting the write flag to the potentially new page).
+      */
+     if (fault_status == FSC_PERM) {
+             pfn_existing = gfn_to_pfn_prot(vcpu->kvm, gfn, false, NULL);
+             if (is_error_pfn(pfn_existing))
+                     return -EFAULT;
+     }
+
+     pfn = gfn_to_pfn_prot(vcpu->kvm, gfn, write_fault, &writable);
+     if (is_error_pfn(pfn)) {
+             ret = -EFAULT;
+             goto out_put_existing;
+     }
+
+     /* We need minimum second+third level pages */
+     ret = mmu_topup_memory_cache(memcache, 2, KVM_NR_MEM_OBJS);
+     if (ret)
Quoted 8 lines

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

Quoted 5 lines
arch/arm/kvm/mmu.c +5 -0
+             goto out;
+     new_pte = pfn_pte(pfn, PAGE_KVM_GUEST);
+     if (writable)
+             pte_val(new_pte) |= L_PTE2_WRITE;
+     coherent_icache_guest_page(vcpu->kvm, gfn);

why don't you flush icache only when guest has mapped executable page as __sync_icache_dcache function does currently?

Quoted 9 lines

because we don't know if the guest will map the page executable. The guest may read the page through a normal load, which causes the fault, and subsequently execute it (even possible through different guest mappings). The only way to see this happening would be to mark all pages as non-executable and catch the fault when it occurs - unfortunately the HPFAR which gives us the IPA is not populated on execute never faults, so we would have to translate the PC's va to ipa using cp15 functionality when this happens, which is then also racy with other CPUs.

I think you can avoid the race in the stage 2 XN case. In the Hyp exception handler entered because of a stage 2 XN bit you can get the IPA via the CP15 ATS1CPR and PAR registers. If the address translation failed because the same guest running on other CPU changed the stage 1 page table, you can simply return to the guest rather than switching to host with incomplete information. The guest may handle its own stage 1 fault and eventually trigger another stage 2 permission and Hyp will try the address translation again. That's a very rare situation, so just returning without handling it would not cause any performance issues.

Signature · 1 line
Catalin
#65 lore raw

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

Quoted 50 lines · Catalin Marinas
Quoted 38 lines · Christoffer Dall
Quoted 29 lines
Quoted 29 lines
arch/arm/kvm/mmu.c +4 -0
+
+     /*
+      * If this is a write fault (think COW) we need to make sure the
+      * existing page, which other CPUs might still read, doesn't go

away

(quoted diff) +2 -0
+      * from under us, by calling gfn_to_pfn_prot(write_fault=true).
+      * Therefore, we call gfn_to_pfn_prot(write_fault=false), which

will

arch/arm/kvm/mmu.c +3 -0
+      * pin the existing page, then we get a new page for the user space
+      * pte and map this in the stage-2 table where we also make sure to
+      * flush the TLB for the VM, if there was an existing entry (the

entry

arch/arm/kvm/mmu.c +17 -0
+      * was updated setting the write flag to the potentially new page).
+      */
+     if (fault_status == FSC_PERM) {
+             pfn_existing = gfn_to_pfn_prot(vcpu->kvm, gfn, false, NULL);
+             if (is_error_pfn(pfn_existing))
+                     return -EFAULT;
+     }
+
+     pfn = gfn_to_pfn_prot(vcpu->kvm, gfn, write_fault, &writable);
+     if (is_error_pfn(pfn)) {
+             ret = -EFAULT;
+             goto out_put_existing;
+     }
+
+     /* We need minimum second+third level pages */
+     ret = mmu_topup_memory_cache(memcache, 2, KVM_NR_MEM_OBJS);
+     if (ret)
Quoted 8 lines

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

Quoted 5 lines
arch/arm/kvm/mmu.c +5 -0
+             goto out;
+     new_pte = pfn_pte(pfn, PAGE_KVM_GUEST);
+     if (writable)
+             pte_val(new_pte) |= L_PTE2_WRITE;
+     coherent_icache_guest_page(vcpu->kvm, gfn);

why don't you flush icache only when guest has mapped executable page as __sync_icache_dcache function does currently?

Quoted 9 lines

because we don't know if the guest will map the page executable. The guest may read the page through a normal load, which causes the fault, and subsequently execute it (even possible through different guest mappings). The only way to see this happening would be to mark all pages as non-executable and catch the fault when it occurs - unfortunately the HPFAR which gives us the IPA is not populated on execute never faults, so we would have to translate the PC's va to ipa using cp15 functionality when this happens, which is then also racy with other CPUs.

Quoted 11 lines

I think you can avoid the race in the stage 2 XN case. In the Hyp exception handler entered because of a stage 2 XN bit you can get the IPA via the CP15 ATS1CPR and PAR registers. If the address translation failed because the same guest running on other CPU changed the stage 1 page table, you can simply return to the guest rather than switching to host with incomplete information. The guest may handle its own stage 1 fault and eventually trigger another stage 2 permission and Hyp will try the address translation again. That's a very rare situation, so just returning without handling it would not cause any performance issues.

you're right that the race is not a big issue, but it's not clear to me that the trapping + ATS1CPR will be faster than just flushing icache - we'll have to measure this.

#66 lore raw

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

Quoted 63 lines · Christoffer Dall
Quoted 50 lines · Catalin Marinas
Quoted 38 lines · Christoffer Dall
Quoted 29 lines
Quoted 29 lines
arch/arm/kvm/mmu.c +4 -0
+
+     /*
+      * If this is a write fault (think COW) we need to make sure the
+      * existing page, which other CPUs might still read, doesn't go

away

(quoted diff) +2 -0
+      * from under us, by calling gfn_to_pfn_prot(write_fault=true).
+      * Therefore, we call gfn_to_pfn_prot(write_fault=false), which

will

arch/arm/kvm/mmu.c +3 -0
+      * pin the existing page, then we get a new page for the user space
+      * pte and map this in the stage-2 table where we also make sure to
+      * flush the TLB for the VM, if there was an existing entry (the

entry

arch/arm/kvm/mmu.c +17 -0
+      * was updated setting the write flag to the potentially new page).
+      */
+     if (fault_status == FSC_PERM) {
+             pfn_existing = gfn_to_pfn_prot(vcpu->kvm, gfn, false, NULL);
+             if (is_error_pfn(pfn_existing))
+                     return -EFAULT;
+     }
+
+     pfn = gfn_to_pfn_prot(vcpu->kvm, gfn, write_fault, &writable);
+     if (is_error_pfn(pfn)) {
+             ret = -EFAULT;
+             goto out_put_existing;
+     }
+
+     /* We need minimum second+third level pages */
+     ret = mmu_topup_memory_cache(memcache, 2, KVM_NR_MEM_OBJS);
+     if (ret)
Quoted 8 lines

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

Quoted 5 lines
arch/arm/kvm/mmu.c +5 -0
+             goto out;
+     new_pte = pfn_pte(pfn, PAGE_KVM_GUEST);
+     if (writable)
+             pte_val(new_pte) |= L_PTE2_WRITE;
+     coherent_icache_guest_page(vcpu->kvm, gfn);

why don't you flush icache only when guest has mapped executable page as __sync_icache_dcache function does currently?

Quoted 9 lines

because we don't know if the guest will map the page executable. The guest may read the page through a normal load, which causes the fault, and subsequently execute it (even possible through different guest mappings). The only way to see this happening would be to mark all pages as non-executable and catch the fault when it occurs - unfortunately the HPFAR which gives us the IPA is not populated on execute never faults, so we would have to translate the PC's va to ipa using cp15 functionality when this happens, which is then also racy with other CPUs.

Quoted 11 lines

I think you can avoid the race in the stage 2 XN case. In the Hyp exception handler entered because of a stage 2 XN bit you can get the IPA via the CP15 ATS1CPR and PAR registers. If the address translation failed because the same guest running on other CPU changed the stage 1 page table, you can simply return to the guest rather than switching to host with incomplete information. The guest may handle its own stage 1 fault and eventually trigger another stage 2 permission and Hyp will try the address translation again. That's a very rare situation, so just returning without handling it would not cause any performance issues.

Quoted 3 lines

you're right that the race is not a big issue, but it's not clear to me that the trapping + ATS1CPR will be faster than just flushing icache - we'll have to measure this.

I agree, it needs measuring first as it may not be worth the hassle.

Signature · 1 line
Catalin
#67 lore raw

PATCH 14/15 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 instruciton 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_emulate.h |   23 ++
 arch/arm/include/asm/kvm_host.h    |    3 
 arch/arm/include/asm/kvm_mmu.h     |    1 
 arch/arm/kvm/arm.c                 |   14 +
 arch/arm/kvm/emulate.c             |  448 ++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/exports.c             |    1 
 arch/arm/kvm/interrupts.S          |   40 +++
 arch/arm/kvm/mmu.c                 |  266 +++++++++++++++++++++
 arch/arm/kvm/trace.h               |   21 ++
 11 files changed, 819 insertions(+), 3 deletions(-)
arch/arm/include/asm/kvm_arm.h +3 -0
index 4cff3b7..21cb240 100644
@@ -158,8 +158,11 @@
 #define HSR_ISS		(HSR_IL - 1)
 #define HSR_ISV_SHIFT	(24)
 #define HSR_ISV		(1U << HSR_ISV_SHIFT)
+#define HSR_SRT_SHIFT	(16)
+#define HSR_SRT_MASK	(0xf << HSR_SRT_SHIFT)
 #define HSR_FSC		(0x3f)
 #define HSR_FSC_TYPE	(0x3c)
+#define HSR_SSE		(1 << 21)
 #define HSR_WNR		(1 << 6)
 #define HSR_CV_SHIFT	(24)
 #define HSR_CV		(1U << HSR_CV_SHIFT)
arch/arm/include/asm/kvm_asm.h +2 -0
index 40ee099..5315c69 100644
@@ -49,6 +49,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_emulate.h +23 -0
index 6ddfae2..68489f2 100644
@@ -22,6 +22,27 @@
 #include <linux/kvm_host.h>
 #include <asm/kvm_asm.h>
 
+/*
+ * The in-kernel MMIO emulation code wants to use a copy of run->mmio,
+ * which is an anonymous type. Use our own type instead.
+ */
+struct kvm_exit_mmio {
+	phys_addr_t	phys_addr;
+	u8		data[8];
+	u32		len;
+	bool		is_write;
+};
+
+static inline void kvm_prepare_mmio(struct kvm_run *run,
+				    struct kvm_exit_mmio *mmio)
+{
+	run->mmio.phys_addr	= mmio->phys_addr;
+	run->mmio.len		= mmio->len;
+	run->mmio.is_write	= mmio->is_write;
+	memcpy(run->mmio.data, mmio->data, mmio->len);
+	run->exit_reason	= KVM_EXIT_MMIO;
+}
+
 u32 *vcpu_reg_mode(struct kvm_vcpu *vcpu, u8 reg_num, enum vcpu_mode mode);
 
 static inline u8 __vcpu_mode(u32 cpsr)
@@ -52,6 +73,8 @@ static inline enum vcpu_mode vcpu_mode(struct kvm_vcpu *vcpu)
 }
 
 int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_emulate_mmio_ls(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
+			unsigned long instr, 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);
arch/arm/include/asm/kvm_host.h +3 -0
index 97608d7..3fec9ad 100644
@@ -152,6 +152,9 @@ 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 {
 		bool sign_extend;	/* for byte/halfword loads */
arch/arm/include/asm/kvm_mmu.h +1 -0
index 11f4c3a..c3f90b0 100644
@@ -38,6 +38,7 @@ void kvm_free_stage2_pgd(struct kvm *kvm);
 int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
 			  phys_addr_t pa, unsigned long size);
 
+int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run);
 int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run);
 
 void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);
arch/arm/kvm/arm.c +13 -1
index 8dc5887..06a3368 100644
@@ -570,6 +570,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 	if (unlikely(!vcpu->arch.target))
 		return -ENOEXEC;
 
+	if (run->exit_reason == KVM_EXIT_MMIO) {
+		ret = kvm_handle_mmio_return(vcpu, vcpu->run);
+		if (ret)
+			return ret;
+	}
+
 	if (vcpu->sigset_active)
 		sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
 
@@ -607,7 +613,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_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_vcpu_run(vcpu);
+		}
 
 		vcpu->mode = OUTSIDE_GUEST_MODE;
 		vcpu->arch.last_pcpu = smp_processor_id();
arch/arm/kvm/emulate.c +448 -0

459 lines not on this page · raw message

arch/arm/kvm/exports.c +1 -0
index f39f823..843c3bc 100644
@@ -34,5 +34,6 @@ EXPORT_SYMBOL_GPL(__kvm_vcpu_run);
 
 EXPORT_SYMBOL_GPL(__kvm_flush_vm_context);
 EXPORT_SYMBOL_GPL(__kvm_tlb_flush_vmid);
+EXPORT_SYMBOL_GPL(__kvm_va_to_pa);
 
 EXPORT_SYMBOL_GPL(smp_send_reschedule);
arch/arm/kvm/interrupts.S +40 -0
index cc9448b..ab78477 100644
@@ -128,6 +128,7 @@ ENDPROC(__kvm_flush_vm_context)
 	VFPFMXR FPEXC, r2	@ FPEXC	(last, in case !EN)
 .endm
 
+
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 @  Hypervisor world-switch code
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@ -520,6 +521,45 @@ after_vfp_restore:
 	bx	lr			@ return to IOCTL
 
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@  Translate VA to PA
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+@ 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)
+	hvc	#0			@ switch to hyp-mode
+
+	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}
+	hvc	#0			@ Back to SVC
+	bx	lr
+
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 @  Hypervisor exception vector and handlers
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
arch/arm/kvm/mmu.c +264 -2

284 lines not on this page · raw message

arch/arm/kvm/trace.h +21 -0
index 40606c9..7199b58 100644
@@ -92,6 +92,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,
#68 lore raw

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

Quoted 19 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 instruciton 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

[...]

Quoted 43 lines
arch/arm/kvm/emulate.c +43 -0
+
+/******************************************************************************
+ * Load-Store instruction emulation
+ *****************************************************************************/
+
+/*
+ * Must be ordered with LOADS first and WRITES afterwards
+ * for easy distinction when doing MMIO.
+ */
+#define NUM_LD_INSTR  9
+enum INSTR_LS_INDEXES {
+       INSTR_LS_LDRBT, INSTR_LS_LDRT, INSTR_LS_LDR, INSTR_LS_LDRB,
+       INSTR_LS_LDRD, INSTR_LS_LDREX, INSTR_LS_LDRH, INSTR_LS_LDRSB,
+       INSTR_LS_LDRSH,
+       INSTR_LS_STRBT, INSTR_LS_STRT, INSTR_LS_STR, INSTR_LS_STRB,
+       INSTR_LS_STRD, INSTR_LS_STREX, INSTR_LS_STRH,
+       NUM_LS_INSTR
+};
+
+static u32 ls_instr[NUM_LS_INSTR][2] = {
+       {0x04700000, 0x0d700000}, /* LDRBT */
+       {0x04300000, 0x0d700000}, /* LDRT  */
+       {0x04100000, 0x0c500000}, /* LDR   */
+       {0x04500000, 0x0c500000}, /* LDRB  */
+       {0x000000d0, 0x0e1000f0}, /* LDRD  */
+       {0x01900090, 0x0ff000f0}, /* LDREX */
+       {0x001000b0, 0x0e1000f0}, /* LDRH  */
+       {0x001000d0, 0x0e1000f0}, /* LDRSB */
+       {0x001000f0, 0x0e1000f0}, /* LDRSH */
+       {0x04600000, 0x0d700000}, /* STRBT */
+       {0x04200000, 0x0d700000}, /* STRT  */
+       {0x04000000, 0x0c500000}, /* STR   */
+       {0x04400000, 0x0c500000}, /* STRB  */
+       {0x000000f0, 0x0e1000f0}, /* STRD  */
+       {0x01800090, 0x0ff000f0}, /* STREX */
+       {0x000000b0, 0x0e1000f0}  /* STRH  */
+};
+
+static inline int get_arm_ls_instr_index(u32 instr)
+{
+       return kvm_instr_index(instr, ls_instr, NUM_LS_INSTR);
+}
+

arch/arm/kvm/emulate.c u32 *vcpu_reg_mode(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode) · line 214

Quoted 10 lines
arch/arm/kvm/emulate.c +10 -0
+/*
+ * Load-Store instruction decoding
+ */
+#define INSTR_LS_TYPE_BIT              26
+#define INSTR_LS_RD_MASK               0x0000f000
+#define INSTR_LS_RD_SHIFT              12
+#define INSTR_LS_RN_MASK               0x000f0000
+#define INSTR_LS_RN_SHIFT              16
+#define INSTR_LS_RM_MASK               0x0000000f
+#define INSTR_LS_OFFSET12_MASK         0x00000fff

I'm afraid you're not going to thank me much for this, but it's high time we unified the various instruction decoding functions we have under arch/arm/ and this seems like a good opportunity for that. For example, look at the following snippets (there is much more in the files I list) in addition to what you have:

asm/ptrace.h
-------------
#define PSR_T_BIT 0x00000020
#define PSR_F_BIT 0x00000040
#define PSR_I_BIT 0x00000080
#define PSR_A_BIT 0x00000100
#define PSR_E_BIT 0x00000200
#define PSR_J_BIT 0x01000000
#define PSR_Q_BIT 0x08000000
#define PSR_V_BIT 0x10000000
#define PSR_C_BIT 0x20000000
#define PSR_Z_BIT 0x40000000
#define PSR_N_BIT 0x80000000

mm/alignment.c
--------------
#define LDST_I_BIT(i) (i & (1 << 26)) /* Immediate constant */
#define LDST_P_BIT(i) (i & (1 << 24)) /* Preindex */
#define LDST_U_BIT(i) (i & (1 << 23)) /* Add offset */
#define LDST_W_BIT(i) (i & (1 << 21)) /* Writeback */
#define LDST_L_BIT(i) (i & (1 << 20)) /* Load */

kernel/kprobes*.c
-----------------
static void __kprobes
emulate_ldr(struct kprobe *p, struct pt_regs *regs)

{
	kprobe_opcode_t insn = p->opcode;
	unsigned long pc = (unsigned long)p->addr + 8;
	int rt = (insn >> 12) & 0xf;
	int rn = (insn >> 16) & 0xf;
	int rm = insn & 0xf;

kernel/opcodes.c
----------------

static const unsigned short cc_map[16] = {
	0xF0F0,			/* EQ == Z set            */
	0x0F0F,			/* NE                     */
	0xCCCC,			/* CS == C set            */
	0x3333,			/* CC                     */
	0xFF00,			/* MI == N set            */
	0x00FF,			/* PL                     */
	0xAAAA,			/* VS == V set            */
	0x5555,			/* VC                     */
	0x0C0C,			/* HI == C set && Z clear */
	0xF3F3,			/* LS == C clear || Z set */
	0xAA55,			/* GE == (N==V)           */
	0x55AA,			/* LT == (N!=V)           */
	0x0A05,			/* GT == (!Z && (N==V))   */
	0xF5FA,			/* LE == (Z || (N!=V))    */
	0xFFFF,			/* AL always              */
	0			/* NV                     */
};

kernel/swp_emulate.c
--------------------

#define EXTRACT_REG_NUM(instruction, offset) \
	(((instruction) & (0xf << (offset))) >> (offset))

#define RN_OFFSET 16
#define RT_OFFSET 12
#define RT2_OFFSET 0

There are also bits and pieces with the patching frameworks and module relocations that could benefit from some code sharing. Now, I think Dave had some ideas about moving a load of this code into a common disassembler under arch/arm/ so it would be great to tie that in here and implement that for load/store instructions. Then other users can augment the number of supported instruction classes as and when it is required.

Will

#69 lore raw

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

Quoted 74 lines · Will Deacon
Quoted 19 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 instruciton 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

[...]

Quoted 51 lines
arch/arm/kvm/emulate.c +51 -0
+
+/******************************************************************************
+ * Load-Store instruction emulation
+ *****************************************************************************/
+
+/*
+ * Must be ordered with LOADS first and WRITES afterwards
+ * for easy distinction when doing MMIO.
+ */
+#define NUM_LD_INSTR  9
+enum INSTR_LS_INDEXES {
+       INSTR_LS_LDRBT, INSTR_LS_LDRT, INSTR_LS_LDR, INSTR_LS_LDRB,
+       INSTR_LS_LDRD, INSTR_LS_LDREX, INSTR_LS_LDRH, INSTR_LS_LDRSB,
+       INSTR_LS_LDRSH,
+       INSTR_LS_STRBT, INSTR_LS_STRT, INSTR_LS_STR, INSTR_LS_STRB,
+       INSTR_LS_STRD, INSTR_LS_STREX, INSTR_LS_STRH,
+       NUM_LS_INSTR
+};
+
+static u32 ls_instr[NUM_LS_INSTR][2] = {
+       {0x04700000, 0x0d700000}, /* LDRBT */
+       {0x04300000, 0x0d700000}, /* LDRT  */
+       {0x04100000, 0x0c500000}, /* LDR   */
+       {0x04500000, 0x0c500000}, /* LDRB  */
+       {0x000000d0, 0x0e1000f0}, /* LDRD  */
+       {0x01900090, 0x0ff000f0}, /* LDREX */
+       {0x001000b0, 0x0e1000f0}, /* LDRH  */
+       {0x001000d0, 0x0e1000f0}, /* LDRSB */
+       {0x001000f0, 0x0e1000f0}, /* LDRSH */
+       {0x04600000, 0x0d700000}, /* STRBT */
+       {0x04200000, 0x0d700000}, /* STRT  */
+       {0x04000000, 0x0c500000}, /* STR   */
+       {0x04400000, 0x0c500000}, /* STRB  */
+       {0x000000f0, 0x0e1000f0}, /* STRD  */
+       {0x01800090, 0x0ff000f0}, /* STREX */
+       {0x000000b0, 0x0e1000f0}  /* STRH  */
+};
+
+static inline int get_arm_ls_instr_index(u32 instr)
+{
+       return kvm_instr_index(instr, ls_instr, NUM_LS_INSTR);
+}
+
+/*
+ * Load-Store instruction decoding
+ */
+#define INSTR_LS_TYPE_BIT              26
+#define INSTR_LS_RD_MASK               0x0000f000
+#define INSTR_LS_RD_SHIFT              12
+#define INSTR_LS_RN_MASK               0x000f0000
+#define INSTR_LS_RN_SHIFT              16
Quoted 9 lines

arch/arm/kvm/emulate.c u32 *vcpu_reg_mode(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode) · line 214

Quoted 2 lines
arch/arm/kvm/emulate.c +2 -0
+#define INSTR_LS_RM_MASK               0x0000000f
+#define INSTR_LS_OFFSET12_MASK         0x00000fff

I'm afraid you're not going to thank me much for this, but it's high time we unified the various instruction decoding functions we have under arch/arm/ and this seems like a good opportunity for that. For example, look at the following snippets (there is much more in the files I list) in addition to what you have:

I think it would be great if we had a set of unified decoding functions!

However, I think it's a shame if we can't merge KVM because we want to clean up this code. There would be nothing in the way of breaking API or anything like that preventing us from cleaning this up after the code has been merged.

Please consider reviewing the code for correctness and consider if the code could be merged as is.

On the other hand, if you or Dave or anyone else wants to create a set of patches that cleans this up in a timely manner, I will be happy to merge them for my code as well ;)

Quoted 71 lines

asm/ptrace.h
-------------

#define PSR_T_BIT       0x00000020
#define PSR_F_BIT       0x00000040
#define PSR_I_BIT       0x00000080
#define PSR_A_BIT       0x00000100
#define PSR_E_BIT       0x00000200
#define PSR_J_BIT       0x01000000
#define PSR_Q_BIT       0x08000000
#define PSR_V_BIT       0x10000000
#define PSR_C_BIT       0x20000000
#define PSR_Z_BIT       0x40000000
#define PSR_N_BIT       0x80000000

mm/alignment.c
--------------

#define LDST_I_BIT(i)   (i & (1 << 26))         /* Immediate constant   */
#define LDST_P_BIT(i)   (i & (1 << 24))         /* Preindex             */
#define LDST_U_BIT(i)   (i & (1 << 23))         /* Add offset           */
#define LDST_W_BIT(i)   (i & (1 << 21))         /* Writeback            */
#define LDST_L_BIT(i)   (i & (1 << 20))         /* Load                 */

kernel/kprobes*.c
-----------------
static void __kprobes
emulate_ldr(struct kprobe *p, struct pt_regs *regs)

{
        kprobe_opcode_t insn = p->opcode;
        unsigned long pc = (unsigned long)p->addr + 8;
        int rt = (insn >> 12) & 0xf;
        int rn = (insn >> 16) & 0xf;
        int rm = insn & 0xf;

kernel/opcodes.c
----------------

static const unsigned short cc_map[16] = {
        0xF0F0,                 /* EQ == Z set            */
        0x0F0F,                 /* NE                     */
        0xCCCC,                 /* CS == C set            */
        0x3333,                 /* CC                     */
        0xFF00,                 /* MI == N set            */
        0x00FF,                 /* PL                     */
        0xAAAA,                 /* VS == V set            */
        0x5555,                 /* VC                     */
        0x0C0C,                 /* HI == C set && Z clear */
        0xF3F3,                 /* LS == C clear || Z set */
        0xAA55,                 /* GE == (N==V)           */
        0x55AA,                 /* LT == (N!=V)           */
        0x0A05,                 /* GT == (!Z && (N==V))   */
        0xF5FA,                 /* LE == (Z || (N!=V))    */
        0xFFFF,                 /* AL always              */
        0                       /* NV                     */
};

kernel/swp_emulate.c
--------------------

#define EXTRACT_REG_NUM(instruction, offset) \
        (((instruction) & (0xf << (offset))) >> (offset))

#define RN_OFFSET 16
#define RT_OFFSET 12
#define RT2_OFFSET 0

There are also bits and pieces with the patching frameworks and module relocations that could benefit from some code sharing. Now, I think Dave had some ideas about moving a load of this code into a common disassembler under arch/arm/ so it would be great to tie that in here and implement that for load/store instructions. Then other users can augment the number of supported instruction classes as and when it is required.

#70 lore raw

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

Quoted 92 lines · Christoffer Dall
Quoted 74 lines · Will Deacon
Quoted 19 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 instruciton 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

[...]

Quoted 51 lines
arch/arm/kvm/emulate.c +51 -0
+
+/******************************************************************************
+ * Load-Store instruction emulation
+ *****************************************************************************/
+
+/*
+ * Must be ordered with LOADS first and WRITES afterwards
+ * for easy distinction when doing MMIO.
+ */
+#define NUM_LD_INSTR  9
+enum INSTR_LS_INDEXES {
+       INSTR_LS_LDRBT, INSTR_LS_LDRT, INSTR_LS_LDR, INSTR_LS_LDRB,
+       INSTR_LS_LDRD, INSTR_LS_LDREX, INSTR_LS_LDRH, INSTR_LS_LDRSB,
+       INSTR_LS_LDRSH,
+       INSTR_LS_STRBT, INSTR_LS_STRT, INSTR_LS_STR, INSTR_LS_STRB,
+       INSTR_LS_STRD, INSTR_LS_STREX, INSTR_LS_STRH,
+       NUM_LS_INSTR
+};
+
+static u32 ls_instr[NUM_LS_INSTR][2] = {
+       {0x04700000, 0x0d700000}, /* LDRBT */
+       {0x04300000, 0x0d700000}, /* LDRT  */
+       {0x04100000, 0x0c500000}, /* LDR   */
+       {0x04500000, 0x0c500000}, /* LDRB  */
+       {0x000000d0, 0x0e1000f0}, /* LDRD  */
+       {0x01900090, 0x0ff000f0}, /* LDREX */
+       {0x001000b0, 0x0e1000f0}, /* LDRH  */
+       {0x001000d0, 0x0e1000f0}, /* LDRSB */
+       {0x001000f0, 0x0e1000f0}, /* LDRSH */
+       {0x04600000, 0x0d700000}, /* STRBT */
+       {0x04200000, 0x0d700000}, /* STRT  */
+       {0x04000000, 0x0c500000}, /* STR   */
+       {0x04400000, 0x0c500000}, /* STRB  */
+       {0x000000f0, 0x0e1000f0}, /* STRD  */
+       {0x01800090, 0x0ff000f0}, /* STREX */
+       {0x000000b0, 0x0e1000f0}  /* STRH  */
+};
+
+static inline int get_arm_ls_instr_index(u32 instr)
+{
+       return kvm_instr_index(instr, ls_instr, NUM_LS_INSTR);
+}
+
+/*
+ * Load-Store instruction decoding
+ */
+#define INSTR_LS_TYPE_BIT              26
+#define INSTR_LS_RD_MASK               0x0000f000
+#define INSTR_LS_RD_SHIFT              12
+#define INSTR_LS_RN_MASK               0x000f0000
+#define INSTR_LS_RN_SHIFT              16
Quoted 9 lines

arch/arm/kvm/emulate.c u32 *vcpu_reg_mode(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode) · line 214

Quoted 2 lines
arch/arm/kvm/emulate.c +2 -0
+#define INSTR_LS_RM_MASK               0x0000000f
+#define INSTR_LS_OFFSET12_MASK         0x00000fff

I'm afraid you're not going to thank me much for this, but it's high time we unified the various instruction decoding functions we have under arch/arm/ and this seems like a good opportunity for that. For example, look at the following snippets (there is much more in the files I list) in addition to what you have:

I think it would be great if we had a set of unified decoding functions!

However, I think it's a shame if we can't merge KVM because we want to clean up this code. There would be nothing in the way of breaking API or anything like that preventing us from cleaning this up after the code has been merged.

Quoted 6 lines

Please consider reviewing the code for correctness and consider if the code could be merged as is.

On the other hand, if you or Dave or anyone else wants to create a set of patches that cleans this up in a timely manner, I will be happy to merge them for my code as well ;)

The time I would have available to put into this is rather limited, but I have some initial ideas, as outlined below.

Tixy (who did the kprobes implementation, which is probably the most sophisticated opcode handling we have in the kernel right now) may have ideas too. I would hope that any common framework could reuse a fair chunk of his implementation and test coverage.

I think that a common framework would have to start out a lot more generic that the special-purpose code in current subsystems, at least initially. We should try to move all the knowledge about how instructions are encoded out of subsystems and into the common framework, so far as possible.

We might end up with an interface like this:

Instruction data in memory <-> __arm_mem_to_opcode*() and friends <-> canonical form

canonical form -> __arm_opcode_decode() -> decoded form

decoded form -> __arm_opcode_encode() -> canonical form

The decoded form might look something like this:

struct arm_insn {
	u32			opcode;
	u32			flags;		/* common flags */
	enum arm_insn_type	type;
	u32			insn_flags;	/* insn specific flags */
	enum arm_reg		Rd;
	enum arm_reg		Rn;
	enum arm_reg		Rm;
	enum arm_reg		Rs;
	enum arm_reg		Rt;
	enum arm_reg		Ra;

	/* ... */
};

... and so on. This just a sketch, and deliberately very incomplete.

The basic principle here is that the client gets the architectural, encoding-independent view of the instruction: as far as possible, client code should never need to know anything about how the encoding works. The client code should not even need to know for most purposes whether the instruction is ARM or Thumb, once decoded.

Identifying instruction classes (i.e., is this a VFP/NEON instruction, does this access memory, does this change the PC) etc. should all be abstracted as helper functions / macros.

All cleverness involving tables and hashes to accelerate decode and identify instruction classes should move into the framework, but we can start out with something stupid and simple: if we only need to distinguish a few instruction types to begin with, a simple switch statement for decode may be enough to get started. However, as things mature, a more sophisticated table-driven approach could be

A good starting point would be load/store emulation as this seems to be a common theme, and we would need a credible deployment for any new framework so that we know it's fit for purpose.

So just refactoring the code that appears in the KVM patches could be a good way of getting such a framework off the ground.

Subsystems could be migrated to the new framework incrementally after its initial creation, while extending the framework as needed. This means that the initial that the initial framework could be pretty simple -- we don't need all the functionality all at once.

[...]

Cheers
---Dave

#71 lore raw

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

Quoted 24 lines · Dave Martin
Quoted 16 lines · Christoffer Dall
Quoted 7 lines · Will Deacon

On Sat, Sep 15, 2012 at 04:35:59PM +0100, Christoffer Dall wrote:
I'm afraid you're not going to thank me much for this, but it's high time we unified the various instruction decoding functions we have under arch/arm/ and this seems like a good opportunity for that. For example, look at the following snippets (there is much more in the files I list) in addition to what you have:

I think it would be great if we had a set of unified decoding functions!

However, I think it's a shame if we can't merge KVM because we want to clean up this code. There would be nothing in the way of breaking API or anything like that preventing us from cleaning this up after the code has been merged.

Quoted 6 lines

Please consider reviewing the code for correctness and consider if the code could be merged as is.

On the other hand, if you or Dave or anyone else wants to create a set of patches that cleans this up in a timely manner, I will be happy to merge them for my code as well ;)

Quoted 7 lines

The time I would have available to put into this is rather limited, but I have some initial ideas, as outlined below.

Tixy (who did the kprobes implementation, which is probably the most sophisticated opcode handling we have in the kernel right now) may have ideas too. I would hope that any common framework could reuse a fair chunk of his implementation and test coverage.

To my thinking, the kprobes code is very tailored to the job it needs to do and that turning it into something generic is just going to make everything bigger and more complex - because a generic framework would be bigger (as it's trying to be generic) and then things like kprobes will probably end up having an additional framework layered over the top to bend it to it's purposes. Perhaps I'm being too pessimistic.

It would also requiring an inordinate amount of time to thrash out requirements, design, prototype, and to implement. (I don't think I'm being overly pessimistic about that ;-)

So, unless some-one has serious quantities of spare time lying around...

Signature · 1 line
Tixy
#72 lore raw

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

Quoted 33 lines · Jon Medhurst (Tixy)
Quoted 24 lines · Dave Martin
Quoted 16 lines · Christoffer Dall
Quoted 7 lines · Will Deacon

On Sat, Sep 15, 2012 at 04:35:59PM +0100, Christoffer Dall wrote:
I'm afraid you're not going to thank me much for this, but it's high time we unified the various instruction decoding functions we have under arch/arm/ and this seems like a good opportunity for that. For example, look at the following snippets (there is much more in the files I list) in addition to what you have:

I think it would be great if we had a set of unified decoding functions!

However, I think it's a shame if we can't merge KVM because we want to clean up this code. There would be nothing in the way of breaking API or anything like that preventing us from cleaning this up after the code has been merged.

Quoted 6 lines

Please consider reviewing the code for correctness and consider if the code could be merged as is.

On the other hand, if you or Dave or anyone else wants to create a set of patches that cleans this up in a timely manner, I will be happy to merge them for my code as well ;)

Quoted 7 lines

The time I would have available to put into this is rather limited, but I have some initial ideas, as outlined below.

Tixy (who did the kprobes implementation, which is probably the most sophisticated opcode handling we have in the kernel right now) may have ideas too. I would hope that any common framework could reuse a fair chunk of his implementation and test coverage.

Quoted 6 lines

To my thinking, the kprobes code is very tailored to the job it needs to do and that turning it into something generic is just going to make everything bigger and more complex - because a generic framework would be bigger (as it's trying to be generic) and then things like kprobes will probably end up having an additional framework layered over the top to bend it to it's purposes. Perhaps I'm being too pessimistic.

Perhaps kprobes is a bit of a double-edged example. It's an example of an implementation with some good features, but because it is larger the amount of adaptation required to convert to a common framework would necessarily be larger also.

Yet, kprobes isn't trying to solve radically different problems from other subsystems in the kernel. It doesn't just want to descode and manipulate the properties of instructions, it is actually interested in many of the same properties (for example, whether an instruction is a load or store, whether it modifies the PC etc.) as some other subsystems.

I worry that by default every implementation of this ends up rather deeply tailored to its correcponding subsystem -- so we gradually accumulate more incompatible partially-overlapping duplicates of this functionality over time. This doesn't feel like a good thing.

Quoted 5 lines

It would also requiring an inordinate amount of time to thrash out requirements, design, prototype, and to implement. (I don't think I'm being overly pessimistic about that ;-)

So, unless some-one has serious quantities of spare time lying around...

Well, I don't suggest that we should expect to get there in one go:
such an effort won't ever the off the ground for sure.

If we can consolidate a few simpler subsystems' opcode handling then that would still be a step in the right direction, even if integrating kprobes could not happen until much later.

If we do nothing, the situation will just gradually get worse.

Cheers
---Dave

#73 lore raw

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

In reply to Dave Martin
Quoted 3 lines · Dave Martin

A good starting point would be load/store emulation as this seems to be a common theme, and we would need a credible deployment for any new framework so that we know it's fit for purpose.

Probably not actually, that code is written to be fast, because things like IP stack throughput depend on it - particularly when your network card can only DMA packets to 32-bit aligned addresses (resulting in virtually all network data being misaligned.)

#74 lore raw

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

Quoted 9 lines · Russell King - ARM Linux
Quoted 3 lines · Dave Martin

A good starting point would be load/store emulation as this seems to be a common theme, and we would need a credible deployment for any new framework so that we know it's fit for purpose.

Probably not actually, that code is written to be fast, because things like IP stack throughput depend on it - particularly when your network card can only DMA packets to 32-bit aligned addresses (resulting in virtually all network data being misaligned.)

A fair point, but surely it would still be worth a try?

We might decide that a few particular cases of instruction decode should not use the generic framework for performance reaons, but in most cases being critically dependent on fault-driven software emulation for performance would be a serious mistake in the first place (discussions about the network code notwithstanding).

This is not an argument for being slower just for the sake of it, but it can make sense to factor code on paths where performance is not an issue.

Cheers
---Dave

#75 lore raw

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

Quoted 19 lines · Dave Martin
Quoted 9 lines · Russell King - ARM Linux
Quoted 3 lines · Dave Martin

A good starting point would be load/store emulation as this seems to be a common theme, and we would need a credible deployment for any new framework so that we know it's fit for purpose.

Probably not actually, that code is written to be fast, because things like IP stack throughput depend on it - particularly when your network card can only DMA packets to 32-bit aligned addresses (resulting in virtually all network data being misaligned.)

A fair point, but surely it would still be worth a try?

We might decide that a few particular cases of instruction decode should not use the generic framework for performance reaons, but in most cases being critically dependent on fault-driven software emulation for performance would be a serious mistake in the first place (discussions about the network code notwithstanding).

Quoted 4 lines

This is not an argument for being slower just for the sake of it, but it can make sense to factor code on paths where performance is not an issue.

I'm all for unifying this stuff, but I still think it doesn't qualify for holding back on merging KVM patches. The ARM mode instruction decoding can definitely be cleaned up though to look more like the Thumb2 mode decoding which will be a good step before refactoring to use a more common framework. Currently we decode too many types of instructions (not just the ones with cleared HSR.IV) in ARM mode, so the whole complexity of that code can be reduced.

I'll give that a go before re-sending the KVM patch series.

-Christoffer

#76 lore raw

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

In reply to Christoffer Dall

From: Christoffer Dall <[email protected]>

When the guest executes a WFI instruction the operation is trapped to KVM, which emulates the instruction in software. There is no correlation between a guest executing a WFI instruction and actually putting the hardware into a low-power mode, since a KVM guest is essentially a process and the WFI instruction can be seen as 'sleep' call from this process. Therefore, we block the vcpu when the guest excecutes a wfi instruction and the IRQ or FIQ lines are not raised.

When an interrupt comes in through KVM_IRQ_LINE (see previous patch) we signal the VCPU thread and unflag the VCPU to no longer wait for interrupts.

---
 arch/arm/kvm/arm.c     |   10 ++++++++--
 arch/arm/kvm/emulate.c |   13 ++++++++++++-
 arch/arm/kvm/trace.h   |   16 ++++++++++++++++
 3 files changed, 36 insertions(+), 3 deletions(-)
arch/arm/kvm/arm.c +8 -2
index 06a3368..64fbec7 100644
@@ -318,9 +318,16 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
 	return -EINVAL;
 }
 
+/**
+ * kvm_arch_vcpu_runnable - determine if the vcpu can be scheduled
+ * @v:		The VCPU pointer
+ *
+ * If the guest CPU is not waiting for interrupts or an interrupt line is
+ * asserted, the CPU is by definition runnable.
+ */
 int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
 {
-	return 0;
+	return !!v->arch.irq_lines;
 }
 
 int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v)
@@ -586,7 +593,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		 * Check conditions before entering the guest
 		 */
 		cond_resched();
-
 		update_vttbr(vcpu->kvm);
 
 		local_irq_disable();
arch/arm/kvm/emulate.c +12 -1
index 2670679..fc0fcd3 100644
@@ -154,9 +154,20 @@ static int kvm_instr_index(u32 instr, u32 table[][2], int table_entries)
 	return INSTR_NONE;
 }
 
+/**
+ * kvm_handle_wfi - handle a wait-for-interrupts instruction executed by a guest
+ * @vcpu:	the vcpu pointer
+ * @run:	the kvm_run structure pointer
+ *
+ * Simply sets the wait_for_interrupts flag on the vcpu structure, which will
+ * halt execution of world-switches and schedule other host processes until
+ * there is an incoming IRQ or FIQ to the VM.
+ */
 int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
-	return 0;
+	trace_kvm_wfi(vcpu->arch.regs.pc);
+	kvm_vcpu_block(vcpu);
+	return 1;
 }
 
 
arch/arm/kvm/trace.h +16 -0
index 7199b58..b371138 100644
@@ -143,6 +143,22 @@ TRACE_EVENT(kvm_emulate_cp15_imp,
 			__entry->CRm, __entry->Op2)
 );
 
+TRACE_EVENT(kvm_wfi,
+	TP_PROTO(unsigned long vcpu_pc),
+	TP_ARGS(vcpu_pc),
+
+	TP_STRUCT__entry(
+		__field(	unsigned long,	vcpu_pc		)
+	),
+
+	TP_fast_assign(
+		__entry->vcpu_pc		= vcpu_pc;
+	),
+
+	TP_printk("guest executed wfi at: 0x%08lx", __entry->vcpu_pc)
+);
+
+
 #endif /* _TRACE_KVM_H */
 
 #undef TRACE_INCLUDE_PATH
#77 lore raw

Re: [PATCH 15/15] KVM: ARM: Guest wait-for-interrupts (WFI) support

Quoted 10 lines · Christoffer Dall

From: Christoffer Dall <[email protected]>

When the guest executes a WFI instruction the operation is trapped to KVM, which emulates the instruction in software. There is no correlation between a guest executing a WFI instruction and actually putting the hardware into a low-power mode, since a KVM guest is essentially a process and the WFI instruction can be seen as 'sleep' call from this process. Therefore, we block the vcpu when the guest excecutes a wfi instruction and the IRQ or FIQ lines are not raised.

Quoted 3 lines

When an interrupt comes in through KVM_IRQ_LINE (see previous patch) we signal the VCPU thread and unflag the VCPU to no longer wait for interrupts.

Seems a bit strange tagging this small addition on the end of this series. Can you merge it in with the rest?

Will

#78 lore raw

Re: [PATCH 15/15] KVM: ARM: Guest wait-for-interrupts (WFI) support

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

From: Christoffer Dall <[email protected]>

When the guest executes a WFI instruction the operation is trapped to KVM, which emulates the instruction in software. There is no correlation between a guest executing a WFI instruction and actually putting the hardware into a low-power mode, since a KVM guest is essentially a process and the WFI instruction can be seen as 'sleep' call from this process. Therefore, we block the vcpu when the guest excecutes a wfi instruction and the IRQ or FIQ lines are not raised.

Quoted 7 lines
Quoted 3 lines

When an interrupt comes in through KVM_IRQ_LINE (see previous patch) we signal the VCPU thread and unflag the VCPU to no longer wait for interrupts.

Seems a bit strange tagging this small addition on the end of this series. Can you merge it in with the rest?

sure, I'll tag it in with the world-switch or emulation.

-Christoffer

#79 lore raw
In reply to Christoffer Dall

Hi Christoffer,

Quoted 10 lines · Christoffer Dall

The following series implements KVM support for ARM processors, specifically on the Cortex A-15 platform. We feel this is ready to be merged.

Work is done in collaboration between Columbia University, Virtual Open Systems and ARM/Linaro.

The patch series applies to Linux 3.6-rc5 with a number of merges:
1. git://git.kernel.org/pub/scm/linux/kernel/git/will/linux.git

        branch: io (fc8a08c3a3a)

This is a rebasing branch. If you want to merge it, I can push a separate stable branch for you if you like? That said, I imagine the only patch you want from there is the non-writeback I/O accessors, so I could just stick that somewhere else until it hits mainline.

Let me know,

Will

#80 lore raw
Quoted 14 lines · Will Deacon

Hi Christoffer,

Quoted 10 lines · Christoffer Dall

The following series implements KVM support for ARM processors, specifically on the Cortex A-15 platform. We feel this is ready to be merged.

Work is done in collaboration between Columbia University, Virtual Open Systems and ARM/Linaro.

The patch series applies to Linux 3.6-rc5 with a number of merges:
1. git://git.kernel.org/pub/scm/linux/kernel/git/will/linux.git

        branch: io (fc8a08c3a3a)
Quoted 7 lines

This is a rebasing branch. If you want to merge it, I can push a separate stable branch for you if you like? That said, I imagine the only patch you want from there is the non-writeback I/O accessors, so I could just stick that somewhere else until it hits mainline.

Let me know,

Hi Will,

I actually merged this branch so Marc could merge from my branches, so I included it in the base for this patch series as well, to minimize the diff between the HEAD of kvm-arm-v11-vgic-times and kvm-arm-master at time of sending these patches out. You don't have to prepare the patch separately, as the kernel boots just fine without it now when we have thumb mmio instruction decoding, albeit somewhat slower.

Thanks!
-Christoffer

#81 lore raw
In reply to Christoffer Dall
Quoted 9 lines · Christoffer Dall

The following series implements KVM support for ARM processors, specifically on the Cortex A-15 platform. We feel this is ready to be merged.

Work is done in collaboration between Columbia University, Virtual Open Systems and ARM/Linaro.

From my point of view this looks ready to merge. Once all outstanding

comments have been addressed, please collect ACKs from the ARM maintainers (esp. for the non-kvm parts), and we can finally merge this.

Signature · 1 line
error compiling committee.c: too many arguments to function