KVM: arm64: Base support for the pKVM hypervisor at EL2
Subsystems (5)
Will Deacon
maintainercover KVM: arm64: Base support for the pKVM hypervisor at EL2
Hi all,
This rather large series (based on -rc2) builds on top of the limited pKVM support available upstream and gets us to a point where the hypervisor code at EL2 is capable of running guests in both non-protected and protected mode on the same system. For more background information about pKVM, the following (slightly dated) LWN article may be informative:
https://lwn.net/Articles/836693/
The structure of this series is roughly as follows:
* Patches 01-06 :
- Some small cleanups and minor fixes.
* Patches 07-12 :
- Memory management changes at EL2 to allow the donation of memory from the host to the hypervisor and the "pinning" of shared memory at EL2.
* Patches 13-16 :
- Introduction of shadow VM and vCPU state at EL2 so that the hypervisor can manage guest state using its own private data structures, initially populated from the host structures.
* Patches 17-33 :
- Further memory management changes at EL2 to allow the allocation and reclaim of guest memory by the host. This then allows us to manage guest stage-2 page-tables entirely at EL2, with the host issuing hypercalls to map guest pages in response to faults.
* Patches 34-78 :
- Gradual reduction of EL2 trust in host data; rather than copy blindly between the host and shadow structures, we instead selectively sync/flush between them and reduce the amount of host data that is accessed directly by EL2.
* Patches 79-81 :
- Inject an abort into the host if it tries to access a guest page for which it does not have permission. This will then deliver a SEGV if the access originated from userspace.
* Patches 82-87 :
- Expose hypercalls to protected guests for sharing memory back with the host
* Patches 88-89 :
- Introduce the new machine type and add some documentation.
We considered splitting this into multiple series, but decided to keep everything together initially so that reviewers can more easily get an idea of what we're trying to do and also take it for a spin. The patches are also available in our git tree here:
https://android-kvm.googlesource.com/linux/+/refs/heads/for-upstream/pkvm-base-v1
It's worth pointing out that, although we've been tracking the fd-based proposal around KVM private memory [1], for now the approach taken here interacts directly with anonymous pages using a longterm GUP pin. We're expecting to prototype an fd-based implementation once the discussion at
[2] has converged. In the meantime, we hope to progress the non-protected VM support.
Finally, there are still some features that we have not included in this posting and will come later on:
- Support for read-only memslots and dirty logging for non-protected VMs. We currently document that this doesn't work (setting the memslot flags will fail), but we're working to enable this.
- Support for IOMMU configuration to protect guest memory from DMA attacks by the host.
- Support for optional loading of the guest's initial firmware by the hypervisor.
- Proxying of host interactions with Trustzone, intercepting and validating FF-A [3] calls at EL2.
- Support for restricted MMIO exits to only regions designated as MMIO by the guest. An earlier version of this work was previously posted at [4].
- Hardware debug and PMU support for non-protected guests -- this builds on the separate series posted at [5] and which is now queued for 5.19.
- Guest-side changes to issue the new pKVM hypercalls, for example sharing back the SWIOTLB buffer with the host for virtio traffic.
Please enjoy,
Will, Quentin, Fuad and Marc
[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/r/[email protected]
[3] https://developer.arm.com/documentation/den0077/latest
[4] https://lore.kernel.org/all/[email protected]/
[5] https://lore.kernel.org/all/[email protected]/
--->8
Fuad Tabba (23):
KVM: arm64: Add hyp_spinlock_t static initializer
KVM: arm64: Introduce shadow VM state at EL2
KVM: arm64: Instantiate VM shadow data from EL1
KVM: arm64: Do not allow memslot changes after first VM run under pKVM
KVM: arm64: Add hyp per_cpu variable to track current physical cpu
number
KVM: arm64: Ensure that TLBs and I-cache are private to each vcpu
KVM: arm64: Do not pass the vcpu to __pkvm_host_map_guest()
KVM: arm64: Check directly whether the vcpu is protected
KVM: arm64: Trap debug break and watch from guest
KVM: arm64: Restrict protected VM capabilities
KVM: arm64: Do not support MTE for protected VMs
KVM: arm64: Refactor reset_mpidr to extract its computation
KVM: arm64: Reset sysregs for protected VMs
KVM: arm64: Move pkvm_vcpu_init_traps to shadow vcpu init
KVM: arm64: Fix initializing traps in protected mode
KVM: arm64: Add EL2 entry/exit handlers for pKVM guests
KVM: arm64: Refactor kvm_vcpu_enable_ptrauth() for hyp use
KVM: arm64: Initialize shadow vm state at hyp
KVM: arm64: Add HVC handling for protected guests at EL2
KVM: arm64: Move pstate reset values to kvm_arm.h
KVM: arm64: Move some kvm_psci functions to a shared header
KVM: arm64: Factor out vcpu_reset code for core registers and PSCI
KVM: arm64: Handle PSCI for protected VMs in EL2
Marc Zyngier (20):
KVM: arm64: Handle all ID registers trapped for a protected VM
KVM: arm64: Drop stale comment
KVM: arm64: Check for PTE validity when checking for
executable/cacheable
KVM: arm64: Make vcpu_{read,write}_sys_reg available to HYP code
KVM: arm64: Simplify vgic-v3 hypercalls
KVM: arm64: Add the {flush,sync}_vgic_state() primitives
KVM: arm64: Introduce predicates to check for protected state
KVM: arm64: Add the {flush,sync}_timer_state() primitives
KVM: arm64: Introduce the pkvm_vcpu_{load,put} hypercalls
KVM: arm64: Add current vcpu and shadow_state lookup primitive
KVM: arm64: Skip __kvm_adjust_pc() for protected vcpus
KVM: arm64: Introduce per-EC entry/exit handlers
KVM: arm64: Introduce lazy-ish state sync for non-protected VMs
KVM: arm64: Lazy host FP save/restore
KVM: arm64: Reduce host/shadow vcpu state copying
KVM: arm64: Force injection of a data abort on NISV MMIO exit
KVM: arm64: Donate memory to protected guests
KVM: arm64: Move vgic state between host and shadow vcpu structures
KVM: arm64: Do not update virtual timer state for protected VMs
KVM: arm64: Track the SVE state in the shadow vcpu
Quentin Perret (22):
KVM: arm64: Move hyp refcount manipulation helpers
KVM: arm64: Back hyp_vmemmap for all of memory
KVM: arm64: Implement do_donate() helper for donating memory
KVM: arm64: Prevent the donation of no-map pages
KVM: arm64: Add helpers to pin memory shared with hyp
KVM: arm64: Make hyp stage-1 refcnt correct on the whole range
KVM: arm64: Factor out private range VA allocation
KVM: arm64: Add pcpu fixmap infrastructure at EL2
KVM: arm64: Allow non-coallescable pages in a hyp_pool
KVM: arm64: Add generic hyp_memcache helpers
KVM: arm64: Instantiate guest stage-2 page-tables at EL2
KVM: arm64: Return guest memory from EL2 via dedicated teardown
memcache
KVM: arm64: Add flags to struct hyp_page
KVM: arm64: Consolidate stage-2 init in one function
KVM: arm64: Disallow dirty logging and RO memslots with pKVM
KVM: arm64: Don't access kvm_arm_hyp_percpu_base at EL1
KVM: arm64: Unmap kvm_arm_hyp_percpu_base from the host
KVM: arm64: Explicitly map kvm_vgic_global_state at EL2
KVM: arm64: Don't map host sections in pkvm
KVM: arm64: Add is_pkvm_initialized() helper
KVM: arm64: Refactor enter_exception64()
KVM: arm64: Inject SIGSEGV on illegal accesses
Will Deacon (24):
KVM: arm64: Remove redundant hyp_assert_lock_held() assertions
KVM: arm64: Return error from kvm_arch_init_vm() on allocation failure
KVM: arm64: Ignore 'kvm-arm.mode=protected' when using VHE
KVM: arm64: Extend comment in has_vhe()
KVM: arm64: Unify identifiers used to distinguish host and hypervisor
KVM: arm64: Include asm/kvm_mmu.h in nvhe/mem_protect.h
KVM: arm64: Provide I-cache invalidation by VA at EL2
KVM: arm64: Provide a hypercall for the host to reclaim guest memory
KVM: arm64: Extend memory sharing to allow host-to-guest transitions
KVM: arm64: Use the shadow vCPU structure in handle___kvm_vcpu_run()
KVM: arm64: Handle guest stage-2 page-tables entirely at EL2
KVM: arm64: Maintain a copy of 'kvm_arm_vmid_bits' at EL2
KVM: arm64: Extend memory donation to allow host-to-guest transitions
KVM: arm64: Split up nvhe/fixed_config.h
KVM: arm64: Advertise GICv3 sysreg interface to protected guests
KVM: arm64: Don't expose TLBI hypercalls after de-privilege
KVM: arm64: Support TLB invalidation in guest context
KVM: arm64: Avoid BBM when changing only s/w bits in Stage-2 PTE
KVM: arm64: Extend memory sharing to allow guest-to-host transitions
KVM: arm64: Document the KVM/arm64-specific calls in hypercalls.rst
KVM: arm64: Reformat/beautify PTP hypercall documentation
KVM: arm64: Expose memory sharing hypercalls to protected guests
KVM: arm64: Introduce KVM_VM_TYPE_ARM_PROTECTED machine type for PVMs
Documentation: KVM: Add some documentation for Protected KVM on arm64
.../admin-guide/kernel-parameters.txt | 5 +- Documentation/virt/kvm/api.rst | 7 + Documentation/virt/kvm/arm/hypercalls.rst | 118 ++ Documentation/virt/kvm/arm/index.rst | 2 + Documentation/virt/kvm/arm/pkvm.rst | 96 ++ Documentation/virt/kvm/arm/ptp_kvm.rst | 38 +- arch/arm64/include/asm/kvm_arm.h | 11 +- arch/arm64/include/asm/kvm_asm.h | 28 +- arch/arm64/include/asm/kvm_emulate.h | 92 ++ arch/arm64/include/asm/kvm_host.h | 123 +- arch/arm64/include/asm/kvm_hyp.h | 10 +- arch/arm64/include/asm/kvm_mmu.h | 2 +- arch/arm64/include/asm/kvm_pgtable.h | 8 + arch/arm64/include/asm/kvm_pkvm.h | 257 ++++ arch/arm64/include/asm/virt.h | 15 +- arch/arm64/kernel/cpufeature.c | 10 +- arch/arm64/kernel/image-vars.h | 15 - arch/arm64/kvm/arch_timer.c | 7 +- arch/arm64/kvm/arm.c | 194 ++- arch/arm64/kvm/handle_exit.c | 22 + arch/arm64/kvm/hyp/exception.c | 89 +- arch/arm64/kvm/hyp/hyp-constants.c | 3 + .../arm64/kvm/hyp/include/nvhe/fixed_config.h | 205 --- arch/arm64/kvm/hyp/include/nvhe/gfp.h | 6 +- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 25 +- arch/arm64/kvm/hyp/include/nvhe/memory.h | 33 +- arch/arm64/kvm/hyp/include/nvhe/mm.h | 18 +- arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 119 ++ arch/arm64/kvm/hyp/include/nvhe/spinlock.h | 10 +- .../arm64/kvm/hyp/include/nvhe/trap_handler.h | 2 - arch/arm64/kvm/hyp/nvhe/cache.S | 11 + arch/arm64/kvm/hyp/nvhe/hyp-main.c | 937 +++++++++++++- arch/arm64/kvm/hyp/nvhe/hyp-smp.c | 4 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 1035 +++++++++++++++- arch/arm64/kvm/hyp/nvhe/mm.c | 177 ++- arch/arm64/kvm/hyp/nvhe/page_alloc.c | 42 +- arch/arm64/kvm/hyp/nvhe/pkvm.c | 1095 ++++++++++++++++- arch/arm64/kvm/hyp/nvhe/setup.c | 97 +- arch/arm64/kvm/hyp/nvhe/switch.c | 9 +- arch/arm64/kvm/hyp/nvhe/sys_regs.c | 139 ++- arch/arm64/kvm/hyp/nvhe/tlb.c | 96 +- arch/arm64/kvm/hyp/pgtable.c | 31 +- arch/arm64/kvm/hyp/vgic-v3-sr.c | 27 +- arch/arm64/kvm/mmio.c | 9 + arch/arm64/kvm/mmu.c | 202 ++- arch/arm64/kvm/pkvm.c | 156 ++- arch/arm64/kvm/pmu.c | 16 +- arch/arm64/kvm/psci.c | 28 - arch/arm64/kvm/reset.c | 99 +- arch/arm64/kvm/sys_regs.c | 34 +- arch/arm64/kvm/sys_regs.h | 19 + arch/arm64/kvm/vgic/vgic-v2.c | 9 +- arch/arm64/kvm/vgic/vgic-v3.c | 28 +- arch/arm64/kvm/vgic/vgic.c | 17 +- arch/arm64/kvm/vgic/vgic.h | 6 +- arch/arm64/mm/fault.c | 22 + include/kvm/arm_vgic.h | 3 +- include/linux/arm-smccc.h | 21 + include/uapi/linux/kvm.h | 6 + 59 files changed, 5128 insertions(+), 817 deletions(-) create mode 100644 Documentation/virt/kvm/arm/hypercalls.rst create mode 100644 Documentation/virt/kvm/arm/pkvm.rst delete mode 100644 arch/arm64/kvm/hyp/include/nvhe/fixed_config.h create mode 100644 arch/arm64/kvm/hyp/include/nvhe/pkvm.h
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 1/89 KVM: arm64: Handle all ID registers trapped for a protected VM
From: Marc Zyngier <[email protected]>
A protected VM accessing ID_AA64ISAR2_EL1 gets punished with an UNDEF, while it really should only get a zero back if the register is not handled by the hypervisor emulation (as mandated by the architecture).
Introduce all the missing ID registers (including the unallocated ones), and have them to return 0.
--- arch/arm64/kvm/hyp/nvhe/sys_regs.c | 42 ++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 8 deletions(-)
arch/arm64/kvm/hyp/nvhe/sys_regs.c +34 -8
@@ -246,15 +246,9 @@ u64 pvm_read_id_reg(const struct kvm_vcpu *vcpu, u32 id)case SYS_ID_AA64MMFR2_EL1 :return get_pvm_id_aa64mmfr2 (vcpu);default : -/* -* Should never happen because all cases are covered in -* pvm_sys_reg_descs[]. -*/ -WARN_ON (1 ); -break ; +/* Unhandled ID register, RAZ */ +return 0 ; } - -return 0 ; } static u64 read_id_reg(const struct kvm_vcpu *vcpu,
@@ -335,6 +329,16 @@ static bool pvm_gic_read_sre(struct kvm_vcpu *vcpu,/* Mark the specified system register as an AArch64 feature id register. */ #define AARCH64 (REG ) { SYS_DESC(REG), .access = pvm_access_id_aarch64 } +/* +* sys_reg_desc initialiser for architecturally unallocated cpufeature ID +* register with encoding Op0=3, Op1=0, CRn=0, CRm=crm, Op2=op2 +* (1 <= crm < 8, 0 <= Op2 < 8). +*/ +#define ID_UNALLOCATED (crm, op2) { \ + Op0(3), Op1(0), CRn(0), CRm(crm), Op2(op2), \ + .access = pvm_access_id_aarch64, \ +} +/* Mark the specified system register as Read-As-Zero/Write-Ignored */ #define RAZ_WI (REG ) { SYS_DESC(REG), .access = pvm_access_raz_wi }
@@ -378,24 +382,46 @@ static const struct sys_reg_desc pvm_sys_reg_descs[] = {AARCH32 (SYS_MVFR0_EL1 ),AARCH32 (SYS_MVFR1_EL1 ),AARCH32 (SYS_MVFR2_EL1 ), +ID_UNALLOCATED (3 ,3 ),AARCH32 (SYS_ID_PFR2_EL1 ),AARCH32 (SYS_ID_DFR1_EL1 ),AARCH32 (SYS_ID_MMFR5_EL1 ), +ID_UNALLOCATED (3 ,7 ),/* AArch64 ID registers */ /* CRm=4 */ AARCH64 (SYS_ID_AA64PFR0_EL1 ),AARCH64 (SYS_ID_AA64PFR1_EL1 ), +ID_UNALLOCATED (4 ,2 ), +ID_UNALLOCATED (4 ,3 ),AARCH64 (SYS_ID_AA64ZFR0_EL1 ), +ID_UNALLOCATED (4 ,5 ), +ID_UNALLOCATED (4 ,6 ), +ID_UNALLOCATED (4 ,7 ),AARCH64 (SYS_ID_AA64DFR0_EL1 ),AARCH64 (SYS_ID_AA64DFR1_EL1 ), +ID_UNALLOCATED (5 ,2 ), +ID_UNALLOCATED (5 ,3 ),AARCH64 (SYS_ID_AA64AFR0_EL1 ),AARCH64 (SYS_ID_AA64AFR1_EL1 ), +ID_UNALLOCATED (5 ,6 ), +ID_UNALLOCATED (5 ,7 ),AARCH64 (SYS_ID_AA64ISAR0_EL1 ),AARCH64 (SYS_ID_AA64ISAR1_EL1 ), +AARCH64 (SYS_ID_AA64ISAR2_EL1 ), +ID_UNALLOCATED (6 ,3 ), +ID_UNALLOCATED (6 ,4 ), +ID_UNALLOCATED (6 ,5 ), +ID_UNALLOCATED (6 ,6 ), +ID_UNALLOCATED (6 ,7 ),AARCH64 (SYS_ID_AA64MMFR0_EL1 ),AARCH64 (SYS_ID_AA64MMFR1_EL1 ),AARCH64 (SYS_ID_AA64MMFR2_EL1 ), +ID_UNALLOCATED (7 ,3 ), +ID_UNALLOCATED (7 ,4 ), +ID_UNALLOCATED (7 ,5 ), +ID_UNALLOCATED (7 ,6 ), +ID_UNALLOCATED (7 ,7 ), /* Scalable Vector Registers are restricted. */
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 2/89 KVM: arm64: Remove redundant hyp_assert_lock_held() assertions
In reply to Will Deaconhost_stage2_try() asserts that the KVM host lock is held, so there's no need to duplicate the assertion in its wrappers.
--- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 4 ---- 1 file changed, 4 deletions(-)
arch/arm64/kvm/hyp/nvhe/mem_protect.c +0 -4
@@ -314,15 +314,11 @@ static int host_stage2_adjust_range(u64 addr, struct kvm_mem_range *range)int host_stage2_idmap_locked (phys_addr_t addr,u64 size,enum kvm_pgtable_prot prot) { -hyp_assert_lock_held (&host_kvm.lock); -return host_stage2_try (__host_stage2_idmap, addr, addr + size, prot); }int host_stage2_set_owner_locked (phys_addr_t addr,u64 size,u8 owner_id) { -hyp_assert_lock_held (&host_kvm.lock); -return host_stage2_try (kvm_pgtable_stage2_set_owner, &host_kvm.pgt, addr, size, &host_s2_pool, owner_id); }
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 3/89 KVM: arm64: Return error from kvm_arch_init_vm() on allocation failure
In reply to Will DeaconIf we fail to allocate the 'supported_cpus' cpumask in kvm_arch_init_vm() then be sure to return -ENOMEM instead of success (0) on the failure path.
--- arch/arm64/kvm/arm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
arch/arm64/kvm/arm.c +3 -1
@@ -146,8 +146,10 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)if (ret) goto out_free_stage2_pgd; -if (!zalloc_cpumask_var (&kvm->arch.supported_cpus,GFP_KERNEL )) +if (!zalloc_cpumask_var (&kvm->arch.supported_cpus,GFP_KERNEL )) { + ret = -ENOMEM ; goto out_free_stage2_pgd; + }cpumask_copy (kvm->arch.supported_cpus, cpu_possible_mask);kvm_vgic_early_init (kvm);
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Re: [PATCH 03/89] KVM: arm64: Return error from kvm_arch_init_vm() on allocation failure
Hi,
Quoted 15 lines · Will Deacon
If we fail to allocate the '
supported_cpus' cpumask inkvm_arch_init_vm()then be sure to return-ENOMEMinstead of success (0) on the failure path.Signed-off-by Will Deacon <[email protected]>--- arch/arm64/kvm/arm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)arch/arm64/kvm/arm.c +0
-0@@ -146,8 +146,10 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)if (ret)
arch/arm64/kvm/arm.c int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) · line 155
Quoted 10 lines
arch/arm64/kvm/arm.c +3
-1goto out_free_stage2_pgd; -if (!zalloc_cpumask_var (&kvm->arch.supported_cpus,GFP_KERNEL )) +if (!zalloc_cpumask_var (&kvm->arch.supported_cpus,GFP_KERNEL )) { + ret = -ENOMEM ; goto out_free_stage2_pgd; + }cpumask_copy (kvm->arch.supported_cpus, cpu_possible_mask);kvm_vgic_early_init (kvm);
Thank you for the fix:
This can go in independent of the series. I can send it after rc1 if you prefer to focus on something else.
Thanks,
Alex
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerRe: [PATCH 03/89] KVM: arm64: Return error from kvm_arch_init_vm() on allocation failure
Quoted 27 lines · Alexandru Elisei
Hi,
Quoted 24 lines · Will Deacon
If we fail to allocate the '
supported_cpus' cpumask inkvm_arch_init_vm()then be sure to return-ENOMEMinstead of success (0) on the failure path.Signed-off-by Will Deacon <[email protected]>--- arch/arm64/kvm/arm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)arch/arm64/kvm/arm.c +3
-1@@ -146,8 +146,10 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)if (ret) goto out_free_stage2_pgd; -if (!zalloc_cpumask_var (&kvm->arch.supported_cpus,GFP_KERNEL )) +if (!zalloc_cpumask_var (&kvm->arch.supported_cpus,GFP_KERNEL )) { + ret = -ENOMEM ; goto out_free_stage2_pgd; + }cpumask_copy (kvm->arch.supported_cpus, cpu_possible_mask);
Quoted 5 lines
arch/arm64/kvm/arm.c int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) · line 155
Quoted 1 line
arch/arm64/kvm/arm.c +0
-0kvm_vgic_early_init (kvm);Thank you for the fix:
Reviewed-by Alexandru Elisei <[email protected]>
Thanks!
Quoted 2 lines
This can go in independent of the series. I can send it after rc1 if you prefer to focus on something else.
Cheers, but I reckon I'll post the first 6 patches as their own series at -rc1 anyway.
Will
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 4/89 KVM: arm64: Ignore 'kvm-arm.mode=protected' when using VHE
In reply to Will DeaconIgnore 'kvm-arm.mode=protected' when using VHE so that kvm_get_mode() only returns KVM_MODE_PROTECTED on systems where the feature is available.
--- Documentation/admin-guide/kernel-parameters.txt | 1 - arch/arm64/kernel/cpufeature.c | 10 +--------- arch/arm64/kvm/arm.c | 6 +++++- 3 files changed, 6 insertions(+), 11 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +0 -1
@@ -2438,7 +2438,6 @@ protected: nVHE-based mode with support for guests whose state is kept private from the host. - Not valid if the kernel is running in EL2. Defaults to VHE/nVHE based on hardware support. Setting mode to "protected" will disable kexec and hibernation
arch/arm64/kernel/cpufeature.c +1 -9
@@ -1925,15 +1925,7 @@ static void cpu_enable_mte(struct arm64_cpu_capabilities const *cap)#ifdef CONFIG_KVM static bool is_kvm_protected_mode (const struct arm64_cpu_capabilities *entry,int __unused) { -if (kvm_get_mode () !=KVM_MODE_PROTECTED ) -return false; - -if (is_kernel_in_hyp_mode ()) { -pr_warn ("Protected KVM not available with VHE\n" ); -return false; - } - -return true; +return kvm_get_mode () ==KVM_MODE_PROTECTED ; }#endif /* CONFIG_KVM */
arch/arm64/kvm/arm.c +5 -1
@@ -2167,7 +2167,11 @@ static int __init early_kvm_mode_cfg(char *arg)return -EINVAL ;if (strcmp (arg,"protected" ) ==0 ) { - kvm_mode =KVM_MODE_PROTECTED ; +if (!is_kernel_in_hyp_mode ()) + kvm_mode =KVM_MODE_PROTECTED ; +else +pr_warn_once ("Protected KVM not available with VHE\n" ); +return 0 ; }
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 5/89 KVM: arm64: Extend comment in has_vhe()
In reply to Will Deaconhas_vhe() expands to a compile-time constant when evaluated from the VHE or nVHE code, alternatively checking a static key when called from elsewhere in the kernel. On face value, this looks like a case of premature optimization, but in fact this allows symbol references on VHE-specific code paths to be dropped from the nVHE object.
Expand the comment in has_vhe() to make this clearer, hopefully discouraging anybody from simplifying the code.
--- arch/arm64/include/asm/virt.h | 3 +++ 1 file changed, 3 insertions(+)
arch/arm64/include/asm/virt.h +3 -0
@@ -113,6 +113,9 @@ static __always_inline bool has_vhe(void)/* * Code only run in VHE/NVHE hyp context can assume VHE is present or * absent. Otherwise fall back to caps. +* This allows the compiler to discard VHE-specific code from the +* nVHE object, reducing the number of external symbol references +* needed to link. */ if (is_vhe_hyp_code ())return true;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 6/89 KVM: arm64: Drop stale comment
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
The layout of 'struct kvm_vcpu_arch' has evolved significantly since the initial port of KVM/arm64, so remove the stale comment suggesting that a prefix of the structure is used exclusively from assembly code.
--- arch/arm64/include/asm/kvm_host.h | 5 ----- 1 file changed, 5 deletions(-)
arch/arm64/include/asm/kvm_host.h +0 -5
@@ -339,11 +339,6 @@ struct kvm_vcpu_arch {struct arch_timer_cpu timer_cpu;struct kvm_pmu pmu; -/* -* Anything that is not used directly from assembly code goes -* here. -*/ -/* * Guest registers we preserve during guest debugging. *
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 7/89 KVM: arm64: Move hyp refcount manipulation helpers
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
We will soon need to manipulate struct hyp_page refcounts from outside page_alloc.c, so move the helpers to a header file.
--- arch/arm64/kvm/hyp/include/nvhe/memory.h | 18 ++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/page_alloc.c | 19 ------------------- 2 files changed, 18 insertions(+), 19 deletions(-)
arch/arm64/kvm/hyp/include/nvhe/memory.h +18 -0
@@ -45,4 +45,22 @@ static inline int hyp_page_count(void *addr) return p->refcount; } +static inline void hyp_page_ref_inc (struct hyp_page *p) +{ +BUG_ON (p->refcount ==USHRT_MAX ); + p->refcount++; +} + +static inline int hyp_page_ref_dec_and_test (struct hyp_page *p) +{ +BUG_ON (!p->refcount); + p->refcount--; +return (p->refcount ==0 ); +} + +static inline void hyp_set_page_refcounted (struct hyp_page *p) +{ +BUG_ON (p->refcount); + p->refcount =1 ; +}#endif /* __KVM_HYP_MEMORY_H */
arch/arm64/kvm/hyp/nvhe/page_alloc.c +0 -19
@@ -144,25 +144,6 @@ static struct hyp_page *__hyp_extract_page(struct hyp_pool *pool,return p; } -static inline void hyp_page_ref_inc (struct hyp_page *p) -{ -BUG_ON (p->refcount ==USHRT_MAX ); - p->refcount++; -} - -static inline int hyp_page_ref_dec_and_test (struct hyp_page *p) -{ -BUG_ON (!p->refcount); - p->refcount--; -return (p->refcount ==0 ); -} - -static inline void hyp_set_page_refcounted (struct hyp_page *p) -{ -BUG_ON (p->refcount); - p->refcount =1 ; -} -static void __hyp_put_page (struct hyp_pool *pool,struct hyp_page *p) {if (hyp_page_ref_dec_and_test (p))
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 8/89 KVM: arm64: Back hyp_vmemmap for all of memory
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
The EL2 vmemmap in nVHE Protected mode is currently very sparse: only memory pages owned by the hypervisor itself have a matching struct hyp_page. But since the size of these structs has been reduced significantly, it appears that we can afford backing the vmemmap for all of memory.
This will simplify a lot memory tracking as the hypervisor will have a place to store metadata (e.g. refcounts) that wouldn't otherwise fit in the 4 SW bits we have in the host stage-2 page-table for instance.
--- arch/arm64/include/asm/kvm_pkvm.h | 26 +++++++++++++++++++++++ arch/arm64/kvm/hyp/include/nvhe/mm.h | 14 +------------ arch/arm64/kvm/hyp/nvhe/mm.c | 31 ++++++++++++++++++++++++---- arch/arm64/kvm/hyp/nvhe/page_alloc.c | 4 +--- arch/arm64/kvm/hyp/nvhe/setup.c | 7 +++---- arch/arm64/kvm/pkvm.c | 18 ++-------------- 6 files changed, 60 insertions(+), 40 deletions(-)
arch/arm64/include/asm/kvm_pkvm.h +26 -0
@@ -14,6 +14,32 @@extern struct memblock_region kvm_nvhe_sym (hyp_memory )[];extern unsigned int kvm_nvhe_sym (hyp_memblock_nr ); +static inline unsigned long +hyp_vmemmap_memblock_size (struct memblock_region *reg,size_t vmemmap_entry_size) +{ +unsigned long nr_pages = reg->size >>PAGE_SHIFT ; +unsigned long start, end; + + start = (reg->base >>PAGE_SHIFT ) * vmemmap_entry_size; + end = start + nr_pages * vmemmap_entry_size; + start =ALIGN_DOWN (start,PAGE_SIZE ); + end =ALIGN (end,PAGE_SIZE ); + +return end - start; +} + +static inline unsigned long hyp_vmemmap_pages (size_t vmemmap_entry_size) +{ +unsigned long res =0 , i; + +for (i =0 ; i <kvm_nvhe_sym (hyp_memblock_nr); i++) { + res +=hyp_vmemmap_memblock_size (&kvm_nvhe_sym (hyp_memory)[i], + vmemmap_entry_size); + } + +return res >>PAGE_SHIFT ; +} +static inline unsigned long __hyp_pgtable_max_pages (unsigned long nr_pages) {unsigned long total =0 , i;
arch/arm64/kvm/hyp/include/nvhe/mm.h +1 -13
@@ -15,23 +15,11 @@ extern hyp_spinlock_t pkvm_pgd_lock;int hyp_create_idmap (u32 hyp_va_bits);int hyp_map_vectors (void ); -int hyp_back_vmemmap (phys_addr_t phys,unsigned long size,phys_addr_t back); +int hyp_back_vmemmap (phys_addr_t back);int pkvm_cpu_set_vector (enum arm64_hyp_spectre_vector slot);int pkvm_create_mappings (void *from,void *to,enum kvm_pgtable_prot prot);int pkvm_create_mappings_locked (void *from,void *to,enum kvm_pgtable_prot prot);unsigned long __pkvm_create_private_mapping (phys_addr_t phys,size_t size,enum kvm_pgtable_prot prot); -static inline void hyp_vmemmap_range (phys_addr_t phys,unsigned long size, -unsigned long *start,unsigned long *end) -{ -unsigned long nr_pages = size >>PAGE_SHIFT ; -struct hyp_page *p =hyp_phys_to_page (phys); - - *start = (unsigned long )p; - *end = *start + nr_pages *sizeof (struct hyp_page ); - *start =ALIGN_DOWN (*start,PAGE_SIZE ); - *end =ALIGN (*end,PAGE_SIZE ); -} -#endif /* __KVM_HYP_MM_H */
arch/arm64/kvm/hyp/nvhe/mm.c +27 -4
@@ -105,13 +105,36 @@ int pkvm_create_mappings(void *from, void *to, enum kvm_pgtable_prot prot)return ret; } -int hyp_back_vmemmap (phys_addr_t phys,unsigned long size,phys_addr_t back) +int hyp_back_vmemmap (phys_addr_t back) { -unsigned long start, end; +unsigned long i, start, size, end =0 ; +int ret; -hyp_vmemmap_range (phys, size, &start, &end); +for (i =0 ; i < hyp_memblock_nr; i++) { + start = hyp_memory[i].base; + start =ALIGN_DOWN ((u64 )hyp_phys_to_page (start),PAGE_SIZE ); +/* +* The begining of the hyp_vmemmap region for the current +* memblock may already be backed by the page backing the end +* the previous region, so avoid mapping it twice. +*/ + start =max (start, end); + + end = hyp_memory[i].base + hyp_memory[i].size; + end =PAGE_ALIGN ((u64 )hyp_phys_to_page (end)); +if (start >= end) +continue ; + + size = end - start; + ret =__pkvm_create_mappings (start, size, back,PAGE_HYP ); +if (ret) +return ret; + +memset (hyp_phys_to_virt (back),0 , size); + back += size; + } -return __pkvm_create_mappings (start, end - start, back,PAGE_HYP ); +return 0 ; }static void *__hyp_bp_vect_base;
arch/arm64/kvm/hyp/nvhe/page_alloc.c +1 -3
@@ -230,10 +230,8 @@ int hyp_pool_init(struct hyp_pool *pool, u64 pfn, unsigned int nr_pages,/* Init the vmemmap portion */ p =hyp_phys_to_page (phys); -for (i =0 ; i < nr_pages; i++) { - p[i].order =0 ; +for (i =0 ; i < nr_pages; i++)hyp_set_page_refcounted (&p[i]); - }/* Attach the unused pages to the buddy tree */ for (i = reserved_pages; i < nr_pages; i++)
arch/arm64/kvm/hyp/nvhe/setup.c +3 -4
@@ -31,12 +31,11 @@ static struct hyp_pool hpool;static int divide_memory_pool (void *virt,unsigned long size) { -unsigned long vstart, vend, nr_pages; +unsigned long nr_pages;hyp_early_alloc_init (virt, size); -hyp_vmemmap_range (__hyp_pa (virt), size, &vstart, &vend); - nr_pages = (vend - vstart) >>PAGE_SHIFT ; + nr_pages =hyp_vmemmap_pages (sizeof (struct hyp_page )); vmemmap_base =hyp_early_alloc_contig (nr_pages);if (!vmemmap_base)return -ENOMEM ;
@@ -78,7 +77,7 @@ static int recreate_hyp_mappings(phys_addr_t phys, unsigned long size,if (ret)return ret; - ret =hyp_back_vmemmap (phys, size,hyp_virt_to_phys (vmemmap_base)); + ret =hyp_back_vmemmap (hyp_virt_to_phys (vmemmap_base));if (ret)return ret;
arch/arm64/kvm/pkvm.c +2 -16
@@ -53,7 +53,7 @@ static int __init register_memblock_regions(void)void __init kvm_hyp_reserve (void ) { -u64 nr_pages, prev, hyp_mem_pages =0 ; +u64 hyp_mem_pages =0 ;int ret;if (!is_hyp_mode_available () ||is_kernel_in_hyp_mode ())
@@ -71,21 +71,7 @@ void __init kvm_hyp_reserve(void) hyp_mem_pages +=hyp_s1_pgtable_pages (); hyp_mem_pages +=host_s2_pgtable_pages (); - -/* -* The hyp_vmemmap needs to be backed by pages, but these pages -* themselves need to be present in the vmemmap, so compute the number -* of pages needed by looking for a fixed point. -*/ - nr_pages =0 ; -do { - prev = nr_pages; - nr_pages = hyp_mem_pages + prev; - nr_pages =DIV_ROUND_UP (nr_pages *STRUCT_HYP_PAGE_SIZE , -PAGE_SIZE ); - nr_pages +=__hyp_pgtable_max_pages (nr_pages); - }while (nr_pages != prev); - hyp_mem_pages += nr_pages; + hyp_mem_pages +=hyp_vmemmap_pages (STRUCT_HYP_PAGE_SIZE );/* * Try to allocate a PMD-aligned region to reduce TLB pressure once
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 9/89 KVM: arm64: Unify identifiers used to distinguish host and hypervisor
In reply to Will DeaconThe 'pkvm_component_id' enum type provides constants to refer to the host and the hypervisor, yet this information is duplicated by the 'pkvm_hyp_id' constant.
Remove the definition of 'pkvm_hyp_id' and move the 'pkvm_component_id' type definition to 'mem_protect.h' so that it can be used outside of the memory protection code.
--- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 6 +++++- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 8 -------- arch/arm64/kvm/hyp/nvhe/setup.c | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-)
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +5 -1
@@ -51,7 +51,11 @@ struct host_kvm { };extern struct host_kvm host_kvm; -extern const u8 pkvm_hyp_id; +/* This corresponds to page-table locking order */ +enum pkvm_component_id { +PKVM_ID_HOST , +PKVM_ID_HYP , +};int __pkvm_prot_finalize (void );int __pkvm_host_share_hyp (u64 pfn);
arch/arm64/kvm/hyp/nvhe/mem_protect.c +0 -8
@@ -26,8 +26,6 @@ struct host_kvm host_kvm;static struct hyp_pool host_s2_pool; -const u8 pkvm_hyp_id =1 ; -static void host_lock_component (void ) {hyp_spin_lock (&host_kvm.lock);
@@ -380,12 +378,6 @@ void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt)BUG_ON (ret && ret != -EAGAIN ); } -/* This corresponds to locking order */ -enum pkvm_component_id { -PKVM_ID_HOST , -PKVM_ID_HYP , -}; -struct pkvm_mem_transition {u64 nr_pages;
arch/arm64/kvm/hyp/nvhe/setup.c +1 -1
@@ -197,7 +197,7 @@ static int finalize_host_mappings_walker(u64 addr, u64 end, u32 level, state =pkvm_getstate (kvm_pgtable_hyp_pte_prot (pte));switch (state) {case PKVM_PAGE_OWNED : -return host_stage2_set_owner_locked (phys,PAGE_SIZE , pkvm_hyp_id); +return host_stage2_set_owner_locked (phys,PAGE_SIZE ,PKVM_ID_HYP );case PKVM_PAGE_SHARED_OWNED : prot =pkvm_mkstate (PKVM_HOST_MEM_PROT ,PKVM_PAGE_SHARED_BORROWED );break ;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 10/89 KVM: arm64: Implement do_donate() helper for donating memory
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
Transferring ownership information of a memory region from one component to another can be achieved using a "donate" operation, which results in the previous owner losing access to the underlying pages entirely.
Implement a do_donate() helper, along the same lines as do_{un,}share, and provide this functionality for the host-{to,from}-hyp cases as this will later be used to donate/reclaim memory pages to store VM metadata at EL2.
--- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 2 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 239 ++++++++++++++++++ 2 files changed, 241 insertions(+)
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +2 -0
@@ -60,6 +60,8 @@ enum pkvm_component_id {int __pkvm_prot_finalize (void );int __pkvm_host_share_hyp (u64 pfn);int __pkvm_host_unshare_hyp (u64 pfn); +int __pkvm_host_donate_hyp (u64 pfn,u64 nr_pages); +int __pkvm_hyp_donate_host (u64 pfn,u64 nr_pages);bool addr_is_memory (phys_addr_t phys);int host_stage2_idmap_locked (phys_addr_t addr,u64 size,enum kvm_pgtable_prot prot);
arch/arm64/kvm/hyp/nvhe/mem_protect.c +239 -0
284 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 11/89 KVM: arm64: Prevent the donation of no-map pages
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
Memory regions marked as no-map in DT routinely include TrustZone carevouts and such. Although donating such pages to the hypervisor may not breach confidentiality, it may be used to corrupt its state in uncontrollable ways. To prevent this, let's block host-initiated memory transitions targeting no-map pages altogether in nVHE protected mode as there should be no valid reason to do this currently.
Thankfully, the pKVM EL2 hypervisor has a full copy of the host's list of memblock regions, hence allowing to check for the presence of the MEMBLOCK_NOMAP flag on any given region at EL2 easily.
--- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-)
arch/arm64/kvm/hyp/nvhe/mem_protect.c +16 -6
@@ -193,7 +193,7 @@ struct kvm_mem_range {u64 end; }; -static bool find_mem_range (phys_addr_t addr,struct kvm_mem_range *range) +static struct memblock_region *find_mem_range (phys_addr_t addr,struct kvm_mem_range *range) {int cur, left =0 , right = hyp_memblock_nr;struct memblock_region *reg;
@@ -216,18 +216,28 @@ static bool find_mem_range(phys_addr_t addr, struct kvm_mem_range *range) }else { range->start = reg->base; range->end = end; -return true; +return reg; } } -return false; +return NULL ; }bool addr_is_memory (phys_addr_t phys) {struct kvm_mem_range range; -return find_mem_range (phys, &range); +return !!find_mem_range (phys, &range); +} + +static bool addr_is_allowed_memory (phys_addr_t phys) +{ +struct memblock_region *reg; +struct kvm_mem_range range; + + reg =find_mem_range (phys, &range); + +return reg && !(reg->flags &MEMBLOCK_NOMAP ); }static bool is_in_mem_range (u64 addr,struct kvm_mem_range *range)
@@ -346,7 +356,7 @@ static bool host_stage2_force_pte_cb(u64 addr, u64 end, enum kvm_pgtable_prot prstatic int host_stage2_idmap (u64 addr) {struct kvm_mem_range range; -bool is_memory =find_mem_range (addr, &range); +bool is_memory = !!find_mem_range (addr, &range);enum kvm_pgtable_prot prot;int ret;
@@ -424,7 +434,7 @@ static int __check_page_state_visitor(u64 addr, u64 end, u32 level,struct check_walk_data *d = arg;kvm_pte_t pte = *ptep; -if (kvm_pte_valid (pte) && !addr_is_memory (kvm_pte_to_phys (pte))) +if (kvm_pte_valid (pte) && !addr_is_allowed_memory (kvm_pte_to_phys (pte)))return -EINVAL ;return d->get_page_state (pte) == d->desired ?0 : -EPERM ;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 12/89 KVM: arm64: Add helpers to pin memory shared with hyp
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
Add helpers allowing the hypervisor to check whether a range of pages are currently shared by the host, and 'pin' them if so by blocking host unshare operations until the memory has been unpinned. This will allow the hypervisor to take references on host-provided data-structures (struct kvm and such) and be guaranteed these pages will remain in a stable state until it decides to release them, e.g. during guest teardown.
--- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 3 ++ arch/arm64/kvm/hyp/include/nvhe/memory.h | 7 ++- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 48 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-)
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +3 -0
@@ -69,6 +69,9 @@ int host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id);int kvm_host_prepare_stage2 (void *pgt_pool_base);void handle_host_mem_abort (struct kvm_cpu_context *host_ctxt); +int hyp_pin_shared_mem (void *from,void *to); +void hyp_unpin_shared_mem (void *from,void *to); + static __always_inline void __load_host_stage2(void) {if (static_branch_likely (&kvm_protected_mode_initialized))
arch/arm64/kvm/hyp/include/nvhe/memory.h +6 -1
@@ -51,10 +51,15 @@ static inline void hyp_page_ref_inc(struct hyp_page *p) p->refcount++; } -static inline int hyp_page_ref_dec_and_test (struct hyp_page *p) +static inline void hyp_page_ref_dec (struct hyp_page *p) {BUG_ON (!p->refcount); p->refcount--; +} + +static inline int hyp_page_ref_dec_and_test (struct hyp_page *p) +{ +hyp_page_ref_dec (p);return (p->refcount ==0 ); }
arch/arm64/kvm/hyp/nvhe/mem_protect.c +48 -0
57 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 13/89 KVM: arm64: Include asm/kvm_mmu.h in nvhe/mem_protect.h
In reply to Will Deaconnvhe/mem_protect.h refers to __load_stage2() in the definition of __load_host_stage2() but doesn't include the relevant header.
Include asm/kvm_mmu.h in nvhe/mem_protect.h so that users of the latter don't have to do this themselves.
--- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 1 + 1 file changed, 1 insertion(+)
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +1 -0
@@ -8,6 +8,7 @@#define __KVM_NVHE_MEM_PROTECT__#include <linux/kvm_host.h> #include <asm/kvm_hyp.h> +#include <asm/kvm_mmu.h> #include <asm/kvm_pgtable.h> #include <asm/virt.h> #include <nvhe/spinlock.h>
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 14/89 KVM: arm64: Add hyp_spinlock_t static initializer
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Having a static initializer for hyp_spinlock_t simplifies its use when there isn't an initializing function.
No functional change intended.
--- arch/arm64/kvm/hyp/include/nvhe/spinlock.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
arch/arm64/kvm/hyp/include/nvhe/spinlock.h +9 -1
@@ -28,9 +28,17 @@ typedef union hyp_spinlock { }; } hyp_spinlock_t; +#define __HYP_SPIN_LOCK_INITIALIZER \ + { .__val = 0 } + +#define __HYP_SPIN_LOCK_UNLOCKED \ + ((hyp_spinlock_t) __HYP_SPIN_LOCK_INITIALIZER) + +#define DEFINE_HYP_SPINLOCK (x) hyp_spinlock_t x = __HYP_SPIN_LOCK_UNLOCKED +#define hyp_spin_lock_init (l) \ do { \ - *(l) = (hyp_spinlock_t){ .__val = 0 }; \ + *(l) = __HYP_SPIN_LOCK_UNLOCKED; \ } while (0)static inline void hyp_spin_lock (hyp_spinlock_t *lock)
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 15/89 KVM: arm64: Introduce shadow VM state at EL2
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Introduce a table of shadow VM structures at EL2 and provide hypercalls to the host for creating and destroying shadow VMs.
--- arch/arm64/include/asm/kvm_asm.h | 2 + arch/arm64/include/asm/kvm_host.h | 6 + arch/arm64/include/asm/kvm_pgtable.h | 8 + arch/arm64/include/asm/kvm_pkvm.h | 8 + arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 3 + arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 61 +++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 21 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 14 + arch/arm64/kvm/hyp/nvhe/pkvm.c | 398 ++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/setup.c | 8 + arch/arm64/kvm/hyp/pgtable.c | 9 + arch/arm64/kvm/pkvm.c | 1 + 12 files changed, 539 insertions(+) create mode 100644 arch/arm64/kvm/hyp/include/nvhe/pkvm.h
arch/arm64/include/asm/kvm_asm.h +2 -0
@@ -76,6 +76,8 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___vgic_v3_save_aprs, __KVM_HOST_SMCCC_FUNC___vgic_v3_restore_aprs, __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_init_traps, + __KVM_HOST_SMCCC_FUNC___pkvm_init_shadow, + __KVM_HOST_SMCCC_FUNC___pkvm_teardown_shadow, };#define DECLARE_KVM_VHE_SYM (sym) extern char sym[]
arch/arm64/include/asm/kvm_host.h +6 -0
@@ -101,6 +101,10 @@ struct kvm_s2_mmu {struct kvm_arch_memory_slot { }; +struct kvm_protected_vm { +unsigned int shadow_handle; +}; +struct kvm_arch {struct kvm_s2_mmu mmu;
@@ -140,6 +144,8 @@ struct kvm_arch {u8 pfr0_csv2;u8 pfr0_csv3; + +struct kvm_protected_vm pkvm; };struct kvm_vcpu_fault_info {
arch/arm64/include/asm/kvm_pgtable.h +8 -0
@@ -288,6 +288,14 @@ u64 kvm_pgtable_hyp_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size);*/ u64 kvm_get_vtcr (u64 mmfr0,u64 mmfr1,u32 phys_shift); +/* +* kvm_pgtable_stage2_pgd_size() - Helper to compute size of a stage-2 PGD +* @vtcr: Content of the VTCR register. +* +* Return: the size (in bytes) of the stage-2 PGD +*/ +size_t kvm_pgtable_stage2_pgd_size (u64 vtcr); +/** * __kvm_pgtable_stage2_init() - Initialise a guest stage-2 page-table. * @pgt: Uninitialised page-table structure to initialise.
arch/arm64/include/asm/kvm_pkvm.h +8 -0
@@ -9,6 +9,9 @@#include <linux/memblock.h> #include <asm/kvm_pgtable.h> +/* Maximum number of protected VMs that can be created. */ +#define KVM_MAX_PVMS 255 +#define HYP_MEMBLOCK_REGIONS 128extern struct memblock_region kvm_nvhe_sym (hyp_memory )[];
@@ -40,6 +43,11 @@ static inline unsigned long hyp_vmemmap_pages(size_t vmemmap_entry_size)return res >>PAGE_SHIFT ; } +static inline unsigned long hyp_shadow_table_pages (void ) +{ +return PAGE_ALIGN (KVM_MAX_PVMS *sizeof (void *)) >>PAGE_SHIFT ; +} +static inline unsigned long __hyp_pgtable_max_pages (unsigned long nr_pages) {unsigned long total =0 , i;
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +3 -0
@@ -11,6 +11,7 @@#include <asm/kvm_mmu.h> #include <asm/kvm_pgtable.h> #include <asm/virt.h> +#include <nvhe/pkvm.h> #include <nvhe/spinlock.h> /*
@@ -68,10 +69,12 @@ bool addr_is_memory(phys_addr_t phys);int host_stage2_idmap_locked (phys_addr_t addr,u64 size,enum kvm_pgtable_prot prot);int host_stage2_set_owner_locked (phys_addr_t addr,u64 size,u8 owner_id);int kvm_host_prepare_stage2 (void *pgt_pool_base); +int kvm_guest_prepare_stage2 (struct kvm_shadow_vm *vm,void *pgd);void handle_host_mem_abort (struct kvm_cpu_context *host_ctxt);int hyp_pin_shared_mem (void *from,void *to);void hyp_unpin_shared_mem (void *from,void *to); +void reclaim_guest_pages (struct kvm_shadow_vm *vm); static __always_inline void __load_host_stage2(void) {
arch/arm64/kvm/hyp/include/nvhe/pkvm.h new file +61 -0
61 lines not on this page · raw message
arch/arm64/kvm/hyp/nvhe/hyp-main.c +21 -0
@@ -15,6 +15,7 @@#include <nvhe/mem_protect.h> #include <nvhe/mm.h> +#include <nvhe/pkvm.h> #include <nvhe/trap_handler.h> DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
@@ -175,6 +176,24 @@ static void handle___pkvm_vcpu_init_traps(struct kvm_cpu_context *host_ctxt)__pkvm_vcpu_init_traps (kern_hyp_va (vcpu)); } +static void handle___pkvm_init_shadow (struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(struct kvm *, host_kvm, host_ctxt, 1); + DECLARE_REG(unsigned long, host_shadow_va, host_ctxt, 2); +DECLARE_REG (size_t, shadow_size, host_ctxt,3 ); + DECLARE_REG(unsigned long, pgd, host_ctxt, 4); + +cpu_reg (host_ctxt,1 ) =__pkvm_init_shadow (host_kvm, host_shadow_va, + shadow_size, pgd); +} + +static void handle___pkvm_teardown_shadow (struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(unsigned int, shadow_handle, host_ctxt, 1); + +cpu_reg (host_ctxt,1 ) =__pkvm_teardown_shadow (shadow_handle); +} +typedef void (*hcall_t )(struct kvm_cpu_context *);#define HANDLE_FUNC (x) [__KVM_HOST_SMCCC_FUNC_##x] = (hcall_t)handle_##x
@@ -204,6 +223,8 @@ static const hcall_t host_hcall[] = {HANDLE_FUNC (__vgic_v3_save_aprs),HANDLE_FUNC (__vgic_v3_restore_aprs),HANDLE_FUNC (__pkvm_vcpu_init_traps), +HANDLE_FUNC (__pkvm_init_shadow), + HANDLE_FUNC(__pkvm_teardown_shadow), };static void handle_host_hcall (struct kvm_cpu_context *host_ctxt)
arch/arm64/kvm/hyp/nvhe/mem_protect.c +14 -0
@@ -141,6 +141,20 @@ int kvm_host_prepare_stage2(void *pgt_pool_base)return 0 ; } +int kvm_guest_prepare_stage2 (struct kvm_shadow_vm *vm,void *pgd) +{ + vm->pgt.pgd = pgd; +return 0 ; +} + +void reclaim_guest_pages (struct kvm_shadow_vm *vm) +{ +unsigned long nr_pages; + + nr_pages =kvm_pgtable_stage2_pgd_size (vm->kvm.arch.vtcr) >>PAGE_SHIFT ; +WARN_ON (__pkvm_hyp_donate_host (hyp_virt_to_pfn (vm->pgt.pgd), nr_pages)); +} +int __pkvm_prot_finalize (void ) {struct kvm_s2_mmu *mmu = &host_kvm.arch.mmu;
arch/arm64/kvm/hyp/nvhe/pkvm.c +398 -0
407 lines not on this page · raw message
arch/arm64/kvm/hyp/nvhe/setup.c +8 -0
@@ -16,6 +16,7 @@#include <nvhe/memory.h> #include <nvhe/mem_protect.h> #include <nvhe/mm.h> +#include <nvhe/pkvm.h> #include <nvhe/trap_handler.h> unsigned long hyp_nr_cpus;
@@ -24,6 +25,7 @@ unsigned long hyp_nr_cpus; (unsigned long)__per_cpu_start)static void *vmemmap_base; +static void *shadow_table_base;static void *hyp_pgt_base;static void *host_s2_pgt_base;static struct kvm_pgtable_mm_ops pkvm_pgtable_mm_ops;
@@ -40,6 +42,11 @@ static int divide_memory_pool(void *virt, unsigned long size)if (!vmemmap_base)return -ENOMEM ; + nr_pages =hyp_shadow_table_pages (); + shadow_table_base =hyp_early_alloc_contig (nr_pages); +if (!shadow_table_base) +return -ENOMEM ; + nr_pages =hyp_s1_pgtable_pages (); hyp_pgt_base =hyp_early_alloc_contig (nr_pages);if (!hyp_pgt_base)
@@ -265,6 +272,7 @@ void __noreturn __pkvm_init_finalise(void)if (ret) goto out; +hyp_shadow_table_init (shadow_table_base); out:/* * We tail-called to here from handle___pkvm_init() and will not return,
arch/arm64/kvm/hyp/pgtable.c +9 -0
@@ -1200,6 +1200,15 @@ int __kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu,return 0 ; } +size_t kvm_pgtable_stage2_pgd_size (u64 vtcr) +{ +u32 ia_bits =VTCR_EL2_IPA (vtcr); +u32 sl0 =FIELD_GET (VTCR_EL2_SL0_MASK , vtcr); +u32 start_level =VTCR_EL2_TGRAN_SL0_BASE - sl0; + +return kvm_pgd_pages (ia_bits, start_level) *PAGE_SIZE ; +} +static int stage2_free_walker (u64 addr,u64 end,u32 level,kvm_pte_t *ptep,enum kvm_pgtable_walk_flags flag,void *const arg)
arch/arm64/kvm/pkvm.c +1 -0
@@ -71,6 +71,7 @@ void __init kvm_hyp_reserve(void) hyp_mem_pages +=hyp_s1_pgtable_pages (); hyp_mem_pages +=host_s2_pgtable_pages (); + hyp_mem_pages +=hyp_shadow_table_pages (); hyp_mem_pages +=hyp_vmemmap_pages (STRUCT_HYP_PAGE_SIZE );/*
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 16/89 KVM: arm64: Instantiate VM shadow data from EL1
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Now that EL2 provides calls to create and destroy shadow VM structures, plumb these into the KVM code at EL1 so that a shadow VM is created on first vCPU run and destroyed later along with the 'struct kvm' at teardown time.
--- arch/arm64/include/asm/kvm_host.h | 6 ++ arch/arm64/include/asm/kvm_pkvm.h | 4 ++ arch/arm64/kvm/arm.c | 14 ++++ arch/arm64/kvm/hyp/hyp-constants.c | 3 + arch/arm64/kvm/pkvm.c | 112 +++++++++++++++++++++++++++++ 5 files changed, 139 insertions(+)
arch/arm64/include/asm/kvm_host.h +6 -0
@@ -103,6 +103,12 @@ struct kvm_arch_memory_slot {struct kvm_protected_vm {unsigned int shadow_handle; +struct mutex shadow_lock; + +struct { +void *pgd; +void *shadow; + } hyp_donations; };struct kvm_arch {
arch/arm64/include/asm/kvm_pkvm.h +4 -0
@@ -14,6 +14,10 @@#define HYP_MEMBLOCK_REGIONS 128 +int kvm_init_pvm (struct kvm *kvm); +int kvm_shadow_create (struct kvm *kvm); +void kvm_shadow_destroy (struct kvm *kvm); +extern struct memblock_region kvm_nvhe_sym (hyp_memory )[];extern unsigned int kvm_nvhe_sym (hyp_memblock_nr );
arch/arm64/kvm/arm.c +14 -0
@@ -37,6 +37,7 @@#include <asm/kvm_arm.h> #include <asm/kvm_asm.h> #include <asm/kvm_mmu.h> +#include <asm/kvm_pkvm.h> #include <asm/kvm_emulate.h> #include <asm/sections.h>
@@ -146,6 +147,10 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)if (ret) goto out_free_stage2_pgd; + ret =kvm_init_pvm (kvm); +if (ret) + goto out_free_stage2_pgd; +if (!zalloc_cpumask_var (&kvm->arch.supported_cpus,GFP_KERNEL )) { ret = -ENOMEM ; goto out_free_stage2_pgd;
@@ -182,6 +187,9 @@ void kvm_arch_destroy_vm(struct kvm *kvm)kvm_vgic_destroy (kvm); +if (is_protected_kvm_enabled ()) +kvm_shadow_destroy (kvm); +kvm_destroy_vcpus (kvm);kvm_unshare_hyp (kvm, kvm +1 );
@@ -545,6 +553,12 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)if (ret)return ret; +if (is_protected_kvm_enabled ()) { + ret =kvm_shadow_create (kvm); +if (ret) +return ret; + } +if (!irqchip_in_kernel (kvm)) {/* * Tell the rest of the code that there are userspace irqchip
arch/arm64/kvm/hyp/hyp-constants.c +3 -0
@@ -2,9 +2,12 @@#include <linux/kbuild.h> #include <nvhe/memory.h> +#include <nvhe/pkvm.h> int main (void ) {DEFINE (STRUCT_HYP_PAGE_SIZE ,sizeof (struct hyp_page )); +DEFINE (KVM_SHADOW_VM_SIZE ,sizeof (struct kvm_shadow_vm )); +DEFINE (KVM_SHADOW_VCPU_STATE_SIZE ,sizeof (struct kvm_shadow_vcpu_state ));return 0 ; }
arch/arm64/kvm/pkvm.c +112 -0
121 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 17/89 KVM: arm64: Make hyp stage-1 refcnt correct on the whole range
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
We currently fixup the hypervisor stage-1 refcount only for specific portions of the hyp stage-1 VA space. In order to allow unmapping pages outside of these ranges, let's fixup the refcount for the entire hyp VA space.
--- arch/arm64/kvm/hyp/nvhe/setup.c | 62 +++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 19 deletions(-)
arch/arm64/kvm/hyp/nvhe/setup.c +43 -19
@@ -167,12 +167,11 @@ static void hpool_put_page(void *addr)hyp_put_page (&hpool, addr); } -static int finalize_host_mappings_walker (u64 addr,u64 end,u32 level, -kvm_pte_t *ptep, -enum kvm_pgtable_walk_flags flag, -void *const arg) +static int fix_host_ownership_walker (u64 addr,u64 end,u32 level, +kvm_pte_t *ptep, +enum kvm_pgtable_walk_flags flag, +void *const arg) { -struct kvm_pgtable_mm_ops *mm_ops = arg;enum kvm_pgtable_prot prot;enum pkvm_page_state state;kvm_pte_t pte = *ptep;
@@ -181,15 +180,6 @@ static int finalize_host_mappings_walker(u64 addr, u64 end, u32 level,if (!kvm_pte_valid (pte))return 0 ; -/* -* Fix-up the refcount for the page-table pages as the early allocator -* was unable to access the hyp_vmemmap and so the buddy allocator has -* initialised the refcount to '1'. -*/ - mm_ops->get_page (ptep); -if (flag !=KVM_PGTABLE_WALK_LEAF ) -return 0 ; -if (level != (KVM_PGTABLE_MAX_LEVELS -1 ))return -EINVAL ;
@@ -218,12 +208,30 @@ static int finalize_host_mappings_walker(u64 addr, u64 end, u32 level,return host_stage2_idmap_locked (phys,PAGE_SIZE , prot); } -static int finalize_host_mappings (void ) +static int fix_hyp_pgtable_refcnt_walker (u64 addr,u64 end,u32 level, +kvm_pte_t *ptep, +enum kvm_pgtable_walk_flags flag, +void *const arg) +{ +struct kvm_pgtable_mm_ops *mm_ops = arg; +kvm_pte_t pte = *ptep; + +/* +* Fix-up the refcount for the page-table pages as the early allocator +* was unable to access the hyp_vmemmap and so the buddy allocator has +* initialised the refcount to '1'. +*/ +if (kvm_pte_valid (pte)) + mm_ops->get_page (ptep); + +return 0 ; +} + +static int fix_host_ownership (void ) {struct kvm_pgtable_walker walker = { - .cb = finalize_host_mappings_walker, - .flags =KVM_PGTABLE_WALK_LEAF |KVM_PGTABLE_WALK_TABLE_POST , - .arg = pkvm_pgtable.mm_ops, + .cb = fix_host_ownership_walker, + .flags =KVM_PGTABLE_WALK_LEAF , };int i, ret;
@@ -239,6 +247,18 @@ static int finalize_host_mappings(void)return 0 ; } +static int fix_hyp_pgtable_refcnt (void ) +{ +struct kvm_pgtable_walker walker = { + .cb = fix_hyp_pgtable_refcnt_walker, + .flags =KVM_PGTABLE_WALK_LEAF |KVM_PGTABLE_WALK_TABLE_POST , + .arg = pkvm_pgtable.mm_ops, + }; + +return kvm_pgtable_walk (&pkvm_pgtable,0 ,BIT (pkvm_pgtable.ia_bits), + &walker); +} +void __noreturn __pkvm_init_finalise (void ) {struct kvm_host_data *host_data =this_cpu_ptr (&kvm_host_data);
@@ -268,7 +288,11 @@ void __noreturn __pkvm_init_finalise(void) }; pkvm_pgtable.mm_ops = &pkvm_pgtable_mm_ops; - ret =finalize_host_mappings (); + ret =fix_host_ownership (); +if (ret) + goto out; + + ret =fix_hyp_pgtable_refcnt ();if (ret) goto out;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 18/89 KVM: arm64: Factor out private range VA allocation
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
__pkvm_create_private_mapping() is currently responsible for allocating VA space in the hypervisor's "private" range and creating stage-1 mappings. In order to allow reusing the VA space allocation logic from other places, let's factor it out in a standalone function.
--- arch/arm64/kvm/hyp/nvhe/mm.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-)
arch/arm64/kvm/hyp/nvhe/mm.c +19 -9
@@ -37,6 +37,22 @@ static int __pkvm_create_mappings(unsigned long start, unsigned long size,return err; } +static unsigned long hyp_alloc_private_va_range (size_t size) +{ +unsigned long addr = __io_map_base; + +hyp_assert_lock_held (&pkvm_pgd_lock); + __io_map_base +=PAGE_ALIGN (size); + +/* Are we overflowing on the vmemmap ? */ +if (__io_map_base > __hyp_vmemmap) { + __io_map_base = addr; + addr = (unsigned long )ERR_PTR (-ENOMEM ); + } + +return addr; +} +unsigned long __pkvm_create_private_mapping (phys_addr_t phys,size_t size,enum kvm_pgtable_prot prot) {
@@ -45,16 +61,10 @@ unsigned long __pkvm_create_private_mapping(phys_addr_t phys, size_t size,hyp_spin_lock (&pkvm_pgd_lock); - size =PAGE_ALIGN (size +offset_in_page (phys)); - addr = __io_map_base; - __io_map_base += size; - -/* Are we overflowing on the vmemmap ? */ -if (__io_map_base > __hyp_vmemmap) { - __io_map_base -= size; - addr = (unsigned long )ERR_PTR (-ENOMEM ); + size = size +offset_in_page (phys); + addr =hyp_alloc_private_va_range (size); +if (IS_ERR ((void *)addr)) goto out; - } err =kvm_pgtable_hyp_map (&pkvm_pgtable, addr, size, phys, prot);if (err) {
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 19/89 KVM: arm64: Add pcpu fixmap infrastructure at EL2
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
We will soon need to temporarily map pages into the hypervisor stage-1 in nVHE protected mode. To do this efficiently, let's introduce a per-cpu fixmap allowing to map a single page without needing to take any lock or to allocate memory.
--- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 2 + arch/arm64/kvm/hyp/include/nvhe/mm.h | 4 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 1 - arch/arm64/kvm/hyp/nvhe/mm.c | 85 +++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/setup.c | 4 + 5 files changed, 95 insertions(+), 1 deletion(-)
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +2 -0
@@ -59,6 +59,8 @@ enum pkvm_component_id { PKVM_ID_HYP, }; +extern unsigned long hyp_nr_cpus; +int __pkvm_prot_finalize (void );int __pkvm_host_share_hyp (u64 pfn);int __pkvm_host_unshare_hyp (u64 pfn);
arch/arm64/kvm/hyp/include/nvhe/mm.h +4 -0
@@ -13,6 +13,10 @@extern struct kvm_pgtable pkvm_pgtable;extern hyp_spinlock_t pkvm_pgd_lock; +int hyp_create_pcpu_fixmap (void ); +void *hyp_fixmap_map (phys_addr_t phys); +int hyp_fixmap_unmap (void ); +int hyp_create_idmap (u32 hyp_va_bits);int hyp_map_vectors (void );int hyp_back_vmemmap (phys_addr_t back);
arch/arm64/kvm/hyp/nvhe/mem_protect.c +0 -1
@@ -21,7 +21,6 @@#define KVM_HOST_S2_FLAGS (KVM_PGTABLE_S2_NOFWB | KVM_PGTABLE_S2_IDMAP) -extern unsigned long hyp_nr_cpus;struct host_kvm host_kvm;static struct hyp_pool host_s2_pool;
arch/arm64/kvm/hyp/nvhe/mm.c +85 -0
103 lines not on this page · raw message
arch/arm64/kvm/hyp/nvhe/setup.c +4 -0
@@ -296,6 +296,10 @@ void __noreturn __pkvm_init_finalise(void)if (ret) goto out; + ret =hyp_create_pcpu_fixmap (); +if (ret) + goto out; +hyp_shadow_table_init (shadow_table_base); out:/*
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 20/89 KVM: arm64: Provide I-cache invalidation by VA at EL2
In reply to Will DeaconIn preparation for handling cache maintenance of guest pages at EL2, introduce an EL2 copy of icache_inval_pou() which will later be plumbed into the stage-2 page-table cache maintenance callbacks.
--- arch/arm64/include/asm/kvm_hyp.h | 1 + arch/arm64/kernel/image-vars.h | 3 --- arch/arm64/kvm/arm.c | 1 + arch/arm64/kvm/hyp/nvhe/cache.S | 11 +++++++++++ arch/arm64/kvm/hyp/nvhe/pkvm.c | 3 +++ 5 files changed, 16 insertions(+), 3 deletions(-)
arch/arm64/include/asm/kvm_hyp.h +1 -0
@@ -123,4 +123,5 @@ extern u64 kvm_nvhe_sym(id_aa64mmfr0_el1_sys_val);extern u64 kvm_nvhe_sym (id_aa64mmfr1_el1_sys_val );extern u64 kvm_nvhe_sym (id_aa64mmfr2_el1_sys_val ); +extern unsigned long kvm_nvhe_sym (__icache_flags );#endif /* __ARM64_KVM_HYP_H__ */
arch/arm64/kernel/image-vars.h +0 -3
@@ -80,9 +80,6 @@ KVM_NVHE_ALIAS(nvhe_hyp_panic_handler);/* Vectors installed by hyp-init on reset HVC. */ KVM_NVHE_ALIAS (__hyp_stub_vectors); -/* Kernel symbol used by icache_is_vpipt(). */ -KVM_NVHE_ALIAS (__icache_flags); -/* VMID bits set by the KVM VMID allocator */ KVM_NVHE_ALIAS (kvm_arm_vmid_bits);
arch/arm64/kvm/arm.c +1 -0
@@ -1832,6 +1832,7 @@ static int kvm_hyp_init_protection(u32 hyp_va_bits)kvm_nvhe_sym (id_aa64mmfr0_el1_sys_val) =read_sanitised_ftr_reg (SYS_ID_AA64MMFR0_EL1 );kvm_nvhe_sym (id_aa64mmfr1_el1_sys_val) =read_sanitised_ftr_reg (SYS_ID_AA64MMFR1_EL1 );kvm_nvhe_sym (id_aa64mmfr2_el1_sys_val) =read_sanitised_ftr_reg (SYS_ID_AA64MMFR2_EL1 ); +kvm_nvhe_sym (__icache_flags) = __icache_flags; ret =create_hyp_mappings (addr, addr + hyp_mem_size,PAGE_HYP );if (ret)
arch/arm64/kvm/hyp/nvhe/cache.S +11 -0
@@ -12,3 +12,14 @@ SYM_FUNC_START(__pi_dcache_clean_inval_poc) ret SYM_FUNC_END(__pi_dcache_clean_inval_poc) SYM_FUNC_ALIAS(dcache_clean_inval_poc, __pi_dcache_clean_inval_poc) + +SYM_FUNC_START(__pi_icache_inval_pou) +alternative_if ARM64_HAS_CACHE_DIC + isb + ret +alternative_else_nop_endif + + invalidate_icache_by_line x0, x1, x2, x3 + ret +SYM_FUNC_END(__pi_icache_inval_pou) +SYM_FUNC_ALIAS(icache_inval_pou, __pi_icache_inval_pou)
arch/arm64/kvm/hyp/nvhe/pkvm.c +3 -0
@@ -12,6 +12,9 @@#include <nvhe/pkvm.h> #include <nvhe/trap_handler.h> +/* Used by icache_is_vpipt(). */ +unsigned long __icache_flags; +/* * Set trap register values based on features in ID_AA64PFR0. */
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 21/89 KVM: arm64: Allow non-coallescable pages in a hyp_pool
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
All the contiguous pages used to initialize a hyp_pool are considered coalesceable, which means that the hyp page allocator will actively try to merge them with their buddies on the hyp_put_page() path. However, using hyp_put_page() on a page that is not part of the inital memory range given to a hyp_pool() is currently unsupported.
In order to allow dynamically extending hyp pools at run-time, add a check to __hyp_attach_page() to allow inserting 'external' pages into the free-list of order 0. This will be necessary to allow lazy donation of pages from the host to the hypervisor when allocating guest stage-2 page-table pages at EL2.
--- arch/arm64/kvm/hyp/nvhe/page_alloc.c | 5 +++++ 1 file changed, 5 insertions(+)
arch/arm64/kvm/hyp/nvhe/page_alloc.c +5 -0
@@ -93,11 +93,15 @@ static inline struct hyp_page *node_to_page(struct list_head *node)static void __hyp_attach_page (struct hyp_pool *pool,struct hyp_page *p) { +phys_addr_t phys =hyp_page_to_phys (p);unsigned short order = p->order;struct hyp_page *buddy;memset (hyp_page_to_virt (p),0 ,PAGE_SIZE << p->order); +if (phys < pool->range_start || phys >= pool->range_end) + goto insert; +/* * Only the first struct hyp_page of a high-order page (otherwise known * as the 'head') should have p->order set. The non-head pages should
@@ -116,6 +120,7 @@ static void __hyp_attach_page(struct hyp_pool *pool, p =min (p, buddy); } +insert:/* Mark the new head, and insert it */ p->order = order;page_add_to_list (p, &pool->free_area[order]);
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 22/89 KVM: arm64: Add generic hyp_memcache helpers
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
The host and hypervisor will need to dynamically exchange memory pages soon. Indeed, the hypervisor will rely on the host to donate memory pages it can use to create guest stage-2 page-table and to store metadata. In order to ease this process, introduce a struct hyp_memcache which is essentially a linked list of available pages, indexed by physical addresses.
--- arch/arm64/include/asm/kvm_host.h | 57 +++++++++++++++++++ arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 2 + arch/arm64/kvm/hyp/nvhe/mm.c | 33 +++++++++++ arch/arm64/kvm/mmu.c | 26 +++++++++ 4 files changed, 118 insertions(+)
arch/arm64/include/asm/kvm_host.h +57 -0
@@ -72,6 +72,63 @@ u32 __attribute_const__ kvm_target_cpu(void);int kvm_reset_vcpu (struct kvm_vcpu *vcpu);void kvm_arm_vcpu_destroy (struct kvm_vcpu *vcpu); +struct kvm_hyp_memcache { +phys_addr_t head; +unsigned long nr_pages; +}; + +static inline void push_hyp_memcache (struct kvm_hyp_memcache *mc, +phys_addr_t *p, +phys_addr_t (*to_pa)(void *virt)) +{ + *p = mc->head; + mc->head =to_pa (p); + mc->nr_pages++; +} + +static inline void *pop_hyp_memcache (struct kvm_hyp_memcache *mc, +void *(*to_va)(phys_addr_t phys)) +{ +phys_addr_t *p =to_va (mc->head); + +if (!mc->nr_pages) +return NULL ; + + mc->head = *p; + mc->nr_pages--; + +return p; +} + +static inline int __topup_hyp_memcache (struct kvm_hyp_memcache *mc, +unsigned long min_pages, +void *(*alloc_fn)(void *arg), +phys_addr_t (*to_pa)(void *virt), +void *arg) +{ +while (mc->nr_pages < min_pages) { +phys_addr_t *p =alloc_fn (arg); + +if (!p) +return -ENOMEM ; +push_hyp_memcache (mc, p, to_pa); + } + +return 0 ; +} + +static inline void __free_hyp_memcache (struct kvm_hyp_memcache *mc, +void (*free_fn)(void *virt,void *arg), +void *(*to_va)(phys_addr_t phys), +void *arg) +{ +while (mc->nr_pages) +free_fn (pop_hyp_memcache (mc, to_va), arg); +} + +void free_hyp_memcache (struct kvm_hyp_memcache *mc); +int topup_hyp_memcache (struct kvm_hyp_memcache *mc,unsigned long min_pages); +struct kvm_vmid {atomic64_t id; };
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +2 -0
@@ -77,6 +77,8 @@ void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt);int hyp_pin_shared_mem (void *from,void *to);void hyp_unpin_shared_mem (void *from,void *to);void reclaim_guest_pages (struct kvm_shadow_vm *vm); +int refill_memcache (struct kvm_hyp_memcache *mc,unsigned long min_pages, +struct kvm_hyp_memcache *host_mc); static __always_inline void __load_host_stage2(void) {
arch/arm64/kvm/hyp/nvhe/mm.c +33 -0
@@ -307,3 +307,36 @@ int hyp_create_idmap(u32 hyp_va_bits)return __pkvm_create_mappings (start, end - start, start,PAGE_HYP_EXEC ); } + +static void *admit_host_page (void *arg) +{ +struct kvm_hyp_memcache *host_mc = arg; + +if (!host_mc->nr_pages) +return NULL ; + +/* +* The host still owns the pages in its memcache, so we need to go +* through a full host-to-hyp donation cycle to change it. Fortunately, +* __pkvm_host_donate_hyp() takes care of races for us, so if it +* succeeds we're good to go. +*/ +if (__pkvm_host_donate_hyp (hyp_phys_to_pfn (host_mc->head),1 )) +return NULL ; + +return pop_hyp_memcache (host_mc, hyp_phys_to_virt); +} + +/* Refill our local memcache by poping pages from the one provided by the host. */ +int refill_memcache (struct kvm_hyp_memcache *mc,unsigned long min_pages, +struct kvm_hyp_memcache *host_mc) +{ +struct kvm_hyp_memcache tmp = *host_mc; +int ret; + + ret =__topup_hyp_memcache (mc, min_pages, admit_host_page, + hyp_virt_to_phys, &tmp); + *host_mc = tmp; + +return ret; +}
arch/arm64/kvm/mmu.c +26 -0
@@ -750,6 +750,32 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) } } +static void hyp_mc_free_fn (void *addr,void *unused) +{ +free_page ((unsigned long )addr); +} + +static void *hyp_mc_alloc_fn (void *unused) +{ +return (void *)__get_free_page (GFP_KERNEL_ACCOUNT ); +} + +void free_hyp_memcache (struct kvm_hyp_memcache *mc) +{ +if (is_protected_kvm_enabled ()) +__free_hyp_memcache (mc, hyp_mc_free_fn, + kvm_host_va,NULL ); +} + +int topup_hyp_memcache (struct kvm_hyp_memcache *mc,unsigned long min_pages) +{ +if (!is_protected_kvm_enabled ()) +return 0 ; + +return __topup_hyp_memcache (mc, min_pages, hyp_mc_alloc_fn, + kvm_host_pa,NULL ); +} +/** * kvm_phys_addr_ioremap - map a device range to guest IPA *
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 23/89 KVM: arm64: Instantiate guest stage-2 page-tables at EL2
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
Extend the shadow initialisation at EL2 so that we instantiate a memory pool and a full 'struct kvm_s2_mmu' structure for each VM, with a stage-2 page-table entirely independent from the one managed by the host at EL1.
For now, the new page-table is unused as there is no way for the host to map anything into it. Yet.
--- arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 6 ++ arch/arm64/kvm/hyp/nvhe/mem_protect.c | 127 ++++++++++++++++++++++++- 2 files changed, 130 insertions(+), 3 deletions(-)
arch/arm64/kvm/hyp/include/nvhe/pkvm.h +6 -0
@@ -9,6 +9,9 @@#include <asm/kvm_pkvm.h> +#include <nvhe/gfp.h> +#include <nvhe/spinlock.h> +/* * Holds the relevant data for maintaining the vcpu state completely at hyp. */
@@ -37,6 +40,9 @@ struct kvm_shadow_vm {size_t shadow_area_size;struct kvm_pgtable pgt; +struct kvm_pgtable_mm_ops mm_ops; +struct hyp_pool pool; +hyp_spinlock_t lock;/* Array of the shadow state per vcpu. */ struct kvm_shadow_vcpu_state shadow_vcpu_states[0 ];
arch/arm64/kvm/hyp/nvhe/mem_protect.c +124 -3
148 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 24/89 KVM: arm64: Return guest memory from EL2 via dedicated teardown memcache
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
Rather than relying on the host to free the shadow VM pages explicitly on teardown, introduce a dedicated teardown memcache which allows the host to reclaim guest memory resources without having to keep track of all of the allocations made by EL2.
--- arch/arm64/include/asm/kvm_host.h | 6 +----- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 2 +- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 17 +++++++++++------ arch/arm64/kvm/hyp/nvhe/pkvm.c | 8 +++++++- arch/arm64/kvm/pkvm.c | 12 +----------- 5 files changed, 21 insertions(+), 24 deletions(-)
arch/arm64/include/asm/kvm_host.h +1 -5
@@ -161,11 +161,7 @@ struct kvm_arch_memory_slot {struct kvm_protected_vm {unsigned int shadow_handle;struct mutex shadow_lock; - -struct { -void *pgd; -void *shadow; - } hyp_donations; +struct kvm_hyp_memcache teardown_mc; };struct kvm_arch {
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +1 -1
@@ -76,7 +76,7 @@ void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt);int hyp_pin_shared_mem (void *from,void *to);void hyp_unpin_shared_mem (void *from,void *to); -void reclaim_guest_pages (struct kvm_shadow_vm *vm); +void reclaim_guest_pages (struct kvm_shadow_vm *vm,struct kvm_hyp_memcache *mc);int refill_memcache (struct kvm_hyp_memcache *mc,unsigned long min_pages,struct kvm_hyp_memcache *host_mc);
arch/arm64/kvm/hyp/nvhe/mem_protect.c +11 -6
@@ -260,19 +260,24 @@ int kvm_guest_prepare_stage2(struct kvm_shadow_vm *vm, void *pgd)return 0 ; } -void reclaim_guest_pages (struct kvm_shadow_vm *vm) +void reclaim_guest_pages (struct kvm_shadow_vm *vm,struct kvm_hyp_memcache *mc) { -unsigned long nr_pages, pfn; - - nr_pages =kvm_pgtable_stage2_pgd_size (vm->kvm.arch.vtcr) >>PAGE_SHIFT ; - pfn =hyp_virt_to_pfn (vm->pgt.pgd); +void *addr; +/* Dump all pgtable pages in the hyp_pool */ guest_lock_component (vm);kvm_pgtable_stage2_destroy (&vm->pgt); vm->kvm.arch.mmu.pgd_phys =0ULL ;guest_unlock_component (vm); -WARN_ON (__pkvm_hyp_donate_host (pfn, nr_pages)); +/* Drain the hyp_pool into the memcache */ + addr =hyp_alloc_pages (&vm->pool,0 ); +while (addr) { +memset (hyp_virt_to_page (addr),0 ,sizeof (struct hyp_page )); +push_hyp_memcache (mc, addr, hyp_virt_to_phys); +WARN_ON (__pkvm_hyp_donate_host (hyp_virt_to_pfn (addr),1 )); + addr =hyp_alloc_pages (&vm->pool,0 ); + } }int __pkvm_prot_finalize (void )
arch/arm64/kvm/hyp/nvhe/pkvm.c +7 -1
@@ -546,8 +546,10 @@ int __pkvm_init_shadow(struct kvm *kvm, unsigned long shadow_hva,int __pkvm_teardown_shadow (unsigned int shadow_handle) { +struct kvm_hyp_memcache *mc;struct kvm_shadow_vm *vm;size_t shadow_size; +void *addr;int err;/* Lookup then remove entry from the shadow table. */
@@ -569,7 +571,8 @@ int __pkvm_teardown_shadow(unsigned int shadow_handle)hyp_spin_unlock (&shadow_lock);/* Reclaim guest pages (including page-table pages) */ -reclaim_guest_pages (vm); + mc = &vm->host_kvm->arch.pkvm.teardown_mc; +reclaim_guest_pages (vm, mc);unpin_host_vcpus (vm->shadow_vcpu_states, vm->kvm.created_vcpus);/* Push the metadata pages to the teardown memcache */
@@ -577,6 +580,9 @@ int __pkvm_teardown_shadow(unsigned int shadow_handle)hyp_unpin_shared_mem (vm->host_kvm, vm->host_kvm +1 );memset (vm,0 , shadow_size); +for (addr = vm; addr < (void *)vm + shadow_size; addr +=PAGE_SIZE ) +push_hyp_memcache (mc, addr, hyp_virt_to_phys); +unmap_donated_memory_noclear (vm, shadow_size);return 0 ;
arch/arm64/kvm/pkvm.c +1 -11
@@ -160,8 +160,6 @@ static int __kvm_shadow_create(struct kvm *kvm)/* Store the shadow handle given by hyp for future call reference. */ kvm->arch.pkvm.shadow_handle = shadow_handle; - kvm->arch.pkvm.hyp_donations.pgd = pgd; - kvm->arch.pkvm.hyp_donations.shadow = shadow_addr;return 0 ; free_shadow:
@@ -185,20 +183,12 @@ int kvm_shadow_create(struct kvm *kvm)void kvm_shadow_destroy (struct kvm *kvm) { -size_t pgd_sz, shadow_sz; -if (kvm->arch.pkvm.shadow_handle)WARN_ON (kvm_call_hyp_nvhe (__pkvm_teardown_shadow, kvm->arch.pkvm.shadow_handle)); kvm->arch.pkvm.shadow_handle =0 ; - - shadow_sz =PAGE_ALIGN (KVM_SHADOW_VM_SIZE + -KVM_SHADOW_VCPU_STATE_SIZE * kvm->created_vcpus); - pgd_sz =kvm_pgtable_stage2_pgd_size (kvm->arch.vtcr); - -free_pages_exact (kvm->arch.pkvm.hyp_donations.shadow, shadow_sz); -free_pages_exact (kvm->arch.pkvm.hyp_donations.pgd, pgd_sz); +free_hyp_memcache (&kvm->arch.pkvm.teardown_mc); }int kvm_init_pvm (struct kvm *kvm)
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 25/89 KVM: arm64: Add flags to struct hyp_page
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
Add a 'flags' field to struct hyp_page, and reduce the size of the order field to u8 to avoid growing the struct size.
--- arch/arm64/kvm/hyp/include/nvhe/gfp.h | 6 +++--- arch/arm64/kvm/hyp/include/nvhe/memory.h | 3 ++- arch/arm64/kvm/hyp/nvhe/page_alloc.c | 14 +++++++------- 3 files changed, 12 insertions(+), 11 deletions(-)
arch/arm64/kvm/hyp/include/nvhe/gfp.h +3 -3
@@ -7,7 +7,7 @@#include <nvhe/memory.h> #include <nvhe/spinlock.h> -#define HYP_NO_ORDER USHRT_MAX +#define HYP_NO_ORDER 0xffstruct hyp_pool {/*
@@ -19,11 +19,11 @@ struct hyp_pool {struct list_head free_area[MAX_ORDER ];phys_addr_t range_start;phys_addr_t range_end; -unsigned short max_order; +u8 max_order; };/* Allocation */ -void *hyp_alloc_pages (struct hyp_pool *pool,unsigned short order); +void *hyp_alloc_pages (struct hyp_pool *pool,u8 order);void hyp_split_page (struct hyp_page *page);void hyp_get_page (struct hyp_pool *pool,void *addr);void hyp_put_page (struct hyp_pool *pool,void *addr);
arch/arm64/kvm/hyp/include/nvhe/memory.h +2 -1
@@ -9,7 +9,8 @@struct hyp_page {unsigned short refcount; -unsigned short order; +u8 order; +u8 flags; };extern u64 __hyp_vmemmap;
arch/arm64/kvm/hyp/nvhe/page_alloc.c +7 -7
@@ -32,7 +32,7 @@ u64 __hyp_vmemmap;*/ static struct hyp_page *__find_buddy_nocheck (struct hyp_pool *pool,struct hyp_page *p, -unsigned short order) +u8 order) {phys_addr_t addr =hyp_page_to_phys (p);
@@ -51,7 +51,7 @@ static struct hyp_page *__find_buddy_nocheck(struct hyp_pool *pool,/* Find a buddy page currently available for allocation */ static struct hyp_page *__find_buddy_avail (struct hyp_pool *pool,struct hyp_page *p, -unsigned short order) +u8 order) {struct hyp_page *buddy =__find_buddy_nocheck (pool, p, order);
@@ -94,8 +94,8 @@ static void __hyp_attach_page(struct hyp_pool *pool, struct hyp_page *p) {phys_addr_t phys =hyp_page_to_phys (p); -unsigned short order = p->order;struct hyp_page *buddy; +u8 order = p->order;memset (hyp_page_to_virt (p),0 ,PAGE_SIZE << p->order);
@@ -128,7 +128,7 @@ static void __hyp_attach_page(struct hyp_pool *pool,static struct hyp_page *__hyp_extract_page (struct hyp_pool *pool,struct hyp_page *p, -unsigned short order) +u8 order) {struct hyp_page *buddy;
@@ -182,7 +182,7 @@ void hyp_get_page(struct hyp_pool *pool, void *addr)void hyp_split_page (struct hyp_page *p) { -unsigned short order = p->order; +u8 order = p->order;unsigned int i; p->order =0 ;
@@ -194,10 +194,10 @@ void hyp_split_page(struct hyp_page *p) } } -void *hyp_alloc_pages (struct hyp_pool *pool,unsigned short order) +void *hyp_alloc_pages (struct hyp_pool *pool,u8 order) { -unsigned short i = order;struct hyp_page *p; +u8 i = order;hyp_spin_lock (&pool->lock);
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 26/89 KVM: arm64: Provide a hypercall for the host to reclaim guest memory
In reply to Will DeaconImplement a new hypercall, __pkvm_host_reclaim_page(), so that the host at EL1 can reclaim pages that were previously donated to EL2. This allows EL2 to defer clearing of guest memory on teardown and allows preemption in the host after reclaiming each page.
--- arch/arm64/include/asm/kvm_asm.h | 1 + arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 1 + arch/arm64/kvm/hyp/include/nvhe/memory.h | 7 ++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 8 ++ arch/arm64/kvm/hyp/nvhe/mem_protect.c | 91 ++++++++++++++++++- 5 files changed, 107 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kvm_asm.h +1 -0
@@ -64,6 +64,7 @@ enum __kvm_host_smccc_func {/* Hypercalls available after pKVM finalisation */ __KVM_HOST_SMCCC_FUNC___pkvm_host_share_hyp, __KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_hyp, + __KVM_HOST_SMCCC_FUNC___pkvm_host_reclaim_page, __KVM_HOST_SMCCC_FUNC___kvm_adjust_pc, __KVM_HOST_SMCCC_FUNC___kvm_vcpu_run, __KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context,
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +1 -0
@@ -64,6 +64,7 @@ extern unsigned long hyp_nr_cpus;int __pkvm_prot_finalize (void );int __pkvm_host_share_hyp (u64 pfn);int __pkvm_host_unshare_hyp (u64 pfn); +int __pkvm_host_reclaim_page (u64 pfn);int __pkvm_host_donate_hyp (u64 pfn,u64 nr_pages);int __pkvm_hyp_donate_host (u64 pfn,u64 nr_pages);
arch/arm64/kvm/hyp/include/nvhe/memory.h +7 -0
@@ -7,6 +7,13 @@#include <linux/types.h> +/* +* Accesses to struct hyp_page flags are serialized by the host stage-2 +* page-table lock. +*/ +#define HOST_PAGE_NEED_POISONING BIT(0) +#define HOST_PAGE_PENDING_RECLAIM BIT(1) +struct hyp_page {unsigned short refcount;u8 order;
arch/arm64/kvm/hyp/nvhe/hyp-main.c +8 -0
@@ -155,6 +155,13 @@ static void handle___pkvm_host_unshare_hyp(struct kvm_cpu_context *host_ctxt)cpu_reg (host_ctxt,1 ) =__pkvm_host_unshare_hyp (pfn); } +static void handle___pkvm_host_reclaim_page (struct kvm_cpu_context *host_ctxt) +{ +DECLARE_REG (u64, pfn, host_ctxt,1 ); + +cpu_reg (host_ctxt,1 ) =__pkvm_host_reclaim_page (pfn); +} +static void handle___pkvm_create_private_mapping (struct kvm_cpu_context *host_ctxt) {DECLARE_REG (phys_addr_t, phys, host_ctxt,1 );
@@ -211,6 +218,7 @@ static const hcall_t host_hcall[] = {HANDLE_FUNC (__pkvm_host_share_hyp),HANDLE_FUNC (__pkvm_host_unshare_hyp), +HANDLE_FUNC (__pkvm_host_reclaim_page),HANDLE_FUNC (__kvm_adjust_pc),HANDLE_FUNC (__kvm_vcpu_run), HANDLE_FUNC(__kvm_flush_vm_context),
arch/arm64/kvm/hyp/nvhe/mem_protect.c +90 -1
108 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 27/89 KVM: arm64: Extend memory sharing to allow host-to-guest transitions
In reply to Will DeaconIn preparation for handling guest stage-2 mappings at EL2, extend our memory protection mechanisms to support sharing of pages from the host to a specific guest.
--- arch/arm64/include/asm/kvm_host.h | 8 +- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 2 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 100 ++++++++++++++++++ 3 files changed, 108 insertions(+), 2 deletions(-)
arch/arm64/include/asm/kvm_host.h +6 -2
@@ -421,8 +421,12 @@ struct kvm_vcpu_arch {/* Don't run the guest (internal implementation need) */ bool pause; -/* Cache some mmu pages needed inside spinlock regions */ -struct kvm_mmu_memory_cache mmu_page_cache; +union { +/* Cache some mmu pages needed inside spinlock regions */ +struct kvm_mmu_memory_cache mmu_page_cache; +/* Pages to be donated to pkvm/EL2 if it runs out */ +struct kvm_hyp_memcache pkvm_memcache; + };/* Target CPU and feature flags */ int target;
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +2 -0
@@ -57,6 +57,7 @@ extern struct host_kvm host_kvm;enum pkvm_component_id {PKVM_ID_HOST ,PKVM_ID_HYP , +PKVM_ID_GUEST , };extern unsigned long hyp_nr_cpus;
@@ -67,6 +68,7 @@ int __pkvm_host_unshare_hyp(u64 pfn);int __pkvm_host_reclaim_page (u64 pfn);int __pkvm_host_donate_hyp (u64 pfn,u64 nr_pages);int __pkvm_hyp_donate_host (u64 pfn,u64 nr_pages); +int __pkvm_host_share_guest (u64 pfn,u64 gfn,struct kvm_vcpu *vcpu);bool addr_is_memory (phys_addr_t phys);int host_stage2_idmap_locked (phys_addr_t addr,u64 size,enum kvm_pgtable_prot prot);
arch/arm64/kvm/hyp/nvhe/mem_protect.c +100 -0
135 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 28/89 KVM: arm64: Consolidate stage-2 init in one function
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
The initialization of stage-2 page-tables of guests is currently split across two functions: kvm_init_stage2_mmu() and kvm_arm_setup_stage2(). That is presumably for historical reasons as kvm_arm_setup_stage2() originates from the (now defunct) KVM port for 32bit Arm.
Simplify this code path by merging both functions into one, and while at it make sure to map the kvm struct into the hypervisor stage-1 early on to simplify the failure path.
--- arch/arm64/include/asm/kvm_arm.h | 2 +- arch/arm64/include/asm/kvm_host.h | 2 -- arch/arm64/include/asm/kvm_mmu.h | 2 +- arch/arm64/kvm/arm.c | 27 +++++++++++++-------------- arch/arm64/kvm/mmu.c | 27 ++++++++++++++++++++++++++- arch/arm64/kvm/reset.c | 29 ----------------------------- 6 files changed, 41 insertions(+), 48 deletions(-)
arch/arm64/include/asm/kvm_arm.h +1 -1
@@ -134,7 +134,7 @@* 40 bits wide (T0SZ = 24). Systems with a PARange smaller than 40 bits are * not known to exist and will break with this configuration. * -* The VTCR_EL2 is configured per VM and is initialised in kvm_arm_setup_stage2(). +* The VTCR_EL2 is configured per VM and is initialised in kvm_init_stage2_mmu. * * Note that when using 4K pages, we concatenate two first level page tables * together. With 16K pages, we concatenate 16 first level page tables.
arch/arm64/include/asm/kvm_host.h +0 -2
@@ -865,8 +865,6 @@ int kvm_set_ipa_limit(void);#define __KVM_HAVE_ARCH_VM_ALLOCstruct kvm *kvm_arch_alloc_vm (void ); -int kvm_arm_setup_stage2 (struct kvm *kvm,unsigned long type); -static inline bool kvm_vm_is_protected (struct kvm *kvm) {return false;
arch/arm64/include/asm/kvm_mmu.h +1 -1
@@ -162,7 +162,7 @@ int create_hyp_exec_mappings(phys_addr_t phys_addr, size_t size,void free_hyp_pgds (void );void stage2_unmap_vm (struct kvm *kvm); -int kvm_init_stage2_mmu (struct kvm *kvm,struct kvm_s2_mmu *mmu); +int kvm_init_stage2_mmu (struct kvm *kvm,struct kvm_s2_mmu *mmu,unsigned long type);void kvm_free_stage2_pgd (struct kvm_s2_mmu *mmu);int kvm_phys_addr_ioremap (struct kvm *kvm,phys_addr_t guest_ipa,phys_addr_t pa,unsigned long size,bool writable);
arch/arm64/kvm/arm.c +13 -14
@@ -135,28 +135,24 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) {int ret; - ret =kvm_arm_setup_stage2 (kvm, type); -if (ret) -return ret; - - ret =kvm_init_stage2_mmu (kvm, &kvm->arch.mmu); -if (ret) -return ret; - ret =kvm_share_hyp (kvm, kvm +1 );if (ret) - goto out_free_stage2_pgd; +return ret; ret =kvm_init_pvm (kvm);if (ret) - goto out_free_stage2_pgd; + goto err_unshare_kvm;if (!zalloc_cpumask_var (&kvm->arch.supported_cpus,GFP_KERNEL )) { ret = -ENOMEM ; - goto out_free_stage2_pgd; + goto err_unshare_kvm; }cpumask_copy (kvm->arch.supported_cpus, cpu_possible_mask); + ret =kvm_init_stage2_mmu (kvm, &kvm->arch.mmu, type); +if (ret) + goto err_free_cpumask; +kvm_vgic_early_init (kvm);/* The maximum number of VCPUs is limited by the host's GIC model */
@@ -164,9 +160,12 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)set_default_spectre (kvm); -return ret; -out_free_stage2_pgd: -kvm_free_stage2_pgd (&kvm->arch.mmu); +return 0 ; + +err_free_cpumask: +free_cpumask_var (kvm->arch.supported_cpus); +err_unshare_kvm: +kvm_unshare_hyp (kvm, kvm +1 );return ret; }
arch/arm64/kvm/mmu.c +26 -1
@@ -618,15 +618,40 @@ static struct kvm_pgtable_mm_ops kvm_s2_mm_ops = {* kvm_init_stage2_mmu - Initialise a S2 MMU structure * @kvm: The pointer to the KVM structure * @mmu: The pointer to the s2 MMU structure +* @type: The machine type of the virtual machine * * Allocates only the stage-2 HW PGD level table(s). * Note we don't need locking here as this is only called when the VM is * created, which can only be done once. */ -int kvm_init_stage2_mmu (struct kvm *kvm,struct kvm_s2_mmu *mmu) +int kvm_init_stage2_mmu (struct kvm *kvm,struct kvm_s2_mmu *mmu,unsigned long type) { +u32 kvm_ipa_limit =get_kvm_ipa_limit ();int cpu, err;struct kvm_pgtable *pgt; +u64 mmfr0, mmfr1; +u32 phys_shift; + +if (type & ~KVM_VM_TYPE_ARM_IPA_SIZE_MASK ) +return -EINVAL ; + + phys_shift =KVM_VM_TYPE_ARM_IPA_SIZE (type); +if (phys_shift) { +if (phys_shift > kvm_ipa_limit || + phys_shift <ARM64_MIN_PARANGE_BITS ) +return -EINVAL ; + }else { + phys_shift =KVM_PHYS_SHIFT ; +if (phys_shift > kvm_ipa_limit) { +pr_warn_once ("%s using unsupported default IPA limit, upgrade your VMM\n" , + current->comm); +return -EINVAL ; + } + } + + mmfr0 =read_sanitised_ftr_reg (SYS_ID_AA64MMFR0_EL1 ); + mmfr1 =read_sanitised_ftr_reg (SYS_ID_AA64MMFR1_EL1 ); + kvm->arch.vtcr =kvm_get_vtcr (mmfr0, mmfr1, phys_shift);if (mmu->pgt !=NULL ) {kvm_err ("kvm_arch already initialized?\n" );
arch/arm64/kvm/reset.c +0 -29
@@ -370,32 +370,3 @@ int kvm_set_ipa_limit(void)return 0 ; } - -int kvm_arm_setup_stage2 (struct kvm *kvm,unsigned long type) -{ -u64 mmfr0, mmfr1; -u32 phys_shift; - -if (type & ~KVM_VM_TYPE_ARM_IPA_SIZE_MASK ) -return -EINVAL ; - - phys_shift =KVM_VM_TYPE_ARM_IPA_SIZE (type); -if (phys_shift) { -if (phys_shift > kvm_ipa_limit || - phys_shift <ARM64_MIN_PARANGE_BITS ) -return -EINVAL ; - }else { - phys_shift =KVM_PHYS_SHIFT ; -if (phys_shift > kvm_ipa_limit) { -pr_warn_once ("%s using unsupported default IPA limit, upgrade your VMM\n" , - current->comm); -return -EINVAL ; - } - } - - mmfr0 =read_sanitised_ftr_reg (SYS_ID_AA64MMFR0_EL1 ); - mmfr1 =read_sanitised_ftr_reg (SYS_ID_AA64MMFR1_EL1 ); - kvm->arch.vtcr =kvm_get_vtcr (mmfr0, mmfr1, phys_shift); - -return 0 ; -}
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 29/89 KVM: arm64: Check for PTE validity when checking for executable/cacheable
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
Don't blindly assume that the PTE is valid when checking whether it describes an executable or cacheable mapping.
This makes sure that we don't issue CMOs for invalid mappings.
--- arch/arm64/kvm/hyp/pgtable.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
arch/arm64/kvm/hyp/pgtable.c +4 -5
@@ -700,12 +700,12 @@ static void stage2_put_pte(kvm_pte_t *ptep, struct kvm_s2_mmu *mmu, u64 addr,static bool stage2_pte_cacheable (struct kvm_pgtable *pgt,kvm_pte_t pte) {u64 memattr = pte &KVM_PTE_LEAF_ATTR_LO_S2_MEMATTR ; -return memattr ==KVM_S2_MEMATTR (pgt,NORMAL ); +return kvm_pte_valid (pte) && memattr ==KVM_S2_MEMATTR (pgt,NORMAL ); }static bool stage2_pte_executable (kvm_pte_t pte) { -return !(pte &KVM_PTE_LEAF_ATTR_HI_S2_XN ); +return kvm_pte_valid (pte) && !(pte &KVM_PTE_LEAF_ATTR_HI_S2_XN ); } static bool stage2_leaf_mapping_allowed(u64 addr, u64 end, u32 level,
@@ -750,8 +750,7 @@ static int stage2_map_walker_try_leaf(u64 addr, u64 end, u32 level,/* Perform CMOs before installation of the guest stage-2 PTE */ if (mm_ops->dcache_clean_inval_poc &&stage2_pte_cacheable (pgt, new)) mm_ops->dcache_clean_inval_poc (kvm_pte_follow (new, mm_ops), - granule); - + granule);if (mm_ops->icache_inval_pou &&stage2_pte_executable (new)) mm_ops->icache_inval_pou (kvm_pte_follow (new, mm_ops), granule);
@@ -1148,7 +1147,7 @@ static int stage2_flush_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,struct kvm_pgtable_mm_ops *mm_ops = pgt->mm_ops;kvm_pte_t pte = *ptep; -if (!kvm_pte_valid (pte) || !stage2_pte_cacheable (pgt, pte)) +if (!stage2_pte_cacheable (pgt, pte))return 0 ;if (mm_ops->dcache_clean_inval_poc)
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 30/89 KVM: arm64: Do not allow memslot changes after first VM run under pKVM
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
As the guest stage-2 page-tables will soon be managed entirely by EL2 when pKVM is enabled, guest memory will be pinned and the MMU notifiers in the host will be unable to reconfigure mappings at EL2 other than destrroying the guest and reclaiming all of the memory.
Forbid memslot move/delete operations for VMs that have run under pKVM, returning -EPERM to userspace if such an operation is requested.
--- arch/arm64/kvm/mmu.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
arch/arm64/kvm/mmu.c +11 -0
@@ -1679,6 +1679,13 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,hva_t hva, reg_end;int ret =0 ; +/* In protected mode, cannot modify memslots once a VM has run. */ +if (is_protected_kvm_enabled () && + (change ==KVM_MR_DELETE || change ==KVM_MR_MOVE ) && + kvm->arch.pkvm.shadow_handle) { +return -EPERM ; + } +if (change !=KVM_MR_CREATE && change !=KVM_MR_MOVE && change !=KVM_MR_FLAGS_ONLY )return 0 ;
@@ -1755,6 +1762,10 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,gpa_t gpa = slot->base_gfn <<PAGE_SHIFT ;phys_addr_t size = slot->npages <<PAGE_SHIFT ; +/* Stage-2 is managed by hyp in protected mode. */ +if (is_protected_kvm_enabled ()) +return ; +write_lock (&kvm->mmu_lock);unmap_stage2_range (&kvm->arch.mmu, gpa, size);write_unlock (&kvm->mmu_lock);
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 31/89 KVM: arm64: Disallow dirty logging and RO memslots with pKVM
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
The current implementation of pKVM doesn't support dirty logging or read-only memslots. Although support for these features is desirable, this will require future work, so let's cleanly report the limitations to userspace by failing the ioctls until then.
--- arch/arm64/kvm/mmu.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-)
arch/arm64/kvm/mmu.c +11 -5
@@ -1679,11 +1679,17 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,hva_t hva, reg_end;int ret =0 ; -/* In protected mode, cannot modify memslots once a VM has run. */ -if (is_protected_kvm_enabled () && - (change ==KVM_MR_DELETE || change ==KVM_MR_MOVE ) && - kvm->arch.pkvm.shadow_handle) { -return -EPERM ; +if (is_protected_kvm_enabled ()) { +/* In protected mode, cannot modify memslots once a VM has run. */ +if ((change ==KVM_MR_DELETE || change ==KVM_MR_MOVE ) && + kvm->arch.pkvm.shadow_handle) { +return -EPERM ; + } + +if (new && + new->flags & (KVM_MEM_LOG_DIRTY_PAGES |KVM_MEM_READONLY )) { +return -EPERM ; + } } if (change != KVM_MR_CREATE && change != KVM_MR_MOVE &&
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 32/89 KVM: arm64: Use the shadow vCPU structure in handle___kvm_vcpu_run()
In reply to Will DeaconAs a stepping stone towards deprivileging the host's access to the guest's vCPU structures, introduce some naive flush/sync routines to copy most of the host vCPU into the shadow vCPU on vCPU run and back again on return to EL1.
This allows us to run using the shadow structure when KVM is initialised in protected mode.
--- arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 4 ++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 82 +++++++++++++++++++++++++- arch/arm64/kvm/hyp/nvhe/pkvm.c | 27 +++++++++ 3 files changed, 111 insertions(+), 2 deletions(-)
arch/arm64/kvm/hyp/include/nvhe/pkvm.h +4 -0
@@ -63,5 +63,9 @@ int __pkvm_init_shadow(struct kvm *kvm, unsigned long shadow_hva, size_t shadow_size, unsigned long pgd_hva);int __pkvm_teardown_shadow (unsigned int shadow_handle); +struct kvm_shadow_vcpu_state * +pkvm_load_shadow_vcpu_state (unsigned int shadow_handle,unsigned int vcpu_idx); +void pkvm_put_shadow_vcpu_state (struct kvm_shadow_vcpu_state *shadow_state); +#endif /* __ARM64_KVM_NVHE_PKVM_H__ */
arch/arm64/kvm/hyp/nvhe/hyp-main.c +80 -2
91 lines not on this page · raw message
arch/arm64/kvm/hyp/nvhe/pkvm.c +27 -0
@@ -244,6 +244,33 @@ static struct kvm_shadow_vm *find_shadow_by_handle(unsigned int shadow_handle)return shadow_table[shadow_idx]; } +struct kvm_shadow_vcpu_state * +pkvm_load_shadow_vcpu_state (unsigned int shadow_handle,unsigned int vcpu_idx) +{ +struct kvm_shadow_vcpu_state *shadow_state =NULL ; +struct kvm_shadow_vm *vm; + +hyp_spin_lock (&shadow_lock); + vm =find_shadow_by_handle (shadow_handle); +if (!vm || vm->kvm.created_vcpus <= vcpu_idx) + goto unlock; + + shadow_state = &vm->shadow_vcpu_states[vcpu_idx]; +hyp_page_ref_inc (hyp_virt_to_page (vm)); +unlock: +hyp_spin_unlock (&shadow_lock); +return shadow_state; +} + +void pkvm_put_shadow_vcpu_state (struct kvm_shadow_vcpu_state *shadow_state) +{ +struct kvm_shadow_vm *vm = shadow_state->shadow_vm; + +hyp_spin_lock (&shadow_lock); +hyp_page_ref_dec (hyp_virt_to_page (vm)); +hyp_spin_unlock (&shadow_lock); +} +static void unpin_host_vcpus (struct kvm_shadow_vcpu_state *shadow_vcpu_states,unsigned int nr_vcpus) {
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 33/89 KVM: arm64: Handle guest stage-2 page-tables entirely at EL2
In reply to Will DeaconNow that EL2 is able to manage guest stage-2 page-tables, avoid allocating a separate MMU structure in the host and instead introduce a new fault handler which responds to guest stage-2 faults by sharing GUP-pinned pages with the guest via a hypercall. These pages are recovered (and unpinned) on guest teardown via the page reclaim hypercall.
--- arch/arm64/include/asm/kvm_asm.h | 1 + arch/arm64/include/asm/kvm_host.h | 6 ++ arch/arm64/kvm/arm.c | 10 ++- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 49 ++++++++++- arch/arm64/kvm/mmu.c | 137 +++++++++++++++++++++++++++-- arch/arm64/kvm/pkvm.c | 17 ++++ 6 files changed, 212 insertions(+), 8 deletions(-)
arch/arm64/include/asm/kvm_asm.h +1 -0
@@ -65,6 +65,7 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___pkvm_host_share_hyp, __KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_hyp, __KVM_HOST_SMCCC_FUNC___pkvm_host_reclaim_page, + __KVM_HOST_SMCCC_FUNC___pkvm_host_map_guest, __KVM_HOST_SMCCC_FUNC___kvm_adjust_pc, __KVM_HOST_SMCCC_FUNC___kvm_vcpu_run, __KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context,
arch/arm64/include/asm/kvm_host.h +6 -0
@@ -158,10 +158,16 @@ struct kvm_s2_mmu {struct kvm_arch_memory_slot { }; +struct kvm_pinned_page { +struct list_head link; +struct page *page; +}; +struct kvm_protected_vm {unsigned int shadow_handle;struct mutex shadow_lock;struct kvm_hyp_memcache teardown_mc; +struct list_head pinned_pages; };struct kvm_arch {
arch/arm64/kvm/arm.c +9 -1
@@ -358,7 +358,11 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)if (vcpu_has_run_once (vcpu) &&unlikely (!irqchip_in_kernel (vcpu->kvm)))static_branch_dec (&userspace_irqchip_in_use); -kvm_mmu_free_memory_cache (&vcpu->arch.mmu_page_cache); +if (is_protected_kvm_enabled ()) +free_hyp_memcache (&vcpu->arch.pkvm_memcache); +else +kvm_mmu_free_memory_cache (&vcpu->arch.mmu_page_cache); +kvm_timer_vcpu_terminate (vcpu);kvm_pmu_vcpu_destroy (vcpu);
@@ -385,6 +389,9 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)struct kvm_s2_mmu *mmu;int *last_ran; +if (is_protected_kvm_enabled ()) + goto nommu; + mmu = vcpu->arch.hw_mmu; last_ran =this_cpu_ptr (mmu->last_vcpu_ran);
@@ -402,6 +409,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) *last_ran = vcpu->vcpu_id; } +nommu: vcpu->cpu = cpu;kvm_vgic_load (vcpu);
arch/arm64/kvm/hyp/nvhe/hyp-main.c +47 -2
67 lines not on this page · raw message
arch/arm64/kvm/mmu.c +132 -5
179 lines not on this page · raw message
arch/arm64/kvm/pkvm.c +17 -0
@@ -6,6 +6,7 @@#include <linux/kvm_host.h> #include <linux/memblock.h> +#include <linux/mm.h> #include <linux/mutex.h> #include <linux/sort.h>
@@ -183,12 +184,28 @@ int kvm_shadow_create(struct kvm *kvm)void kvm_shadow_destroy (struct kvm *kvm) { +struct kvm_pinned_page *ppage, *tmp; +struct mm_struct *mm = current->mm; +struct list_head *ppages; +if (kvm->arch.pkvm.shadow_handle)WARN_ON (kvm_call_hyp_nvhe (__pkvm_teardown_shadow, kvm->arch.pkvm.shadow_handle)); kvm->arch.pkvm.shadow_handle =0 ;free_hyp_memcache (&kvm->arch.pkvm.teardown_mc); + + ppages = &kvm->arch.pkvm.pinned_pages; +list_for_each_entry_safe (ppage, tmp, ppages, link) { +WARN_ON (kvm_call_hyp_nvhe (__pkvm_host_reclaim_page, +page_to_pfn (ppage->page))); +cond_resched (); + +account_locked_vm (mm,1 , false); +unpin_user_pages_dirty_lock (&ppage->page,1 , true); +list_del (&ppage->link); +kfree (ppage); + } }int kvm_init_pvm (struct kvm *kvm)
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Re: [PATCH 33/89] KVM: arm64: Handle guest stage-2 page-tables entirely at EL2
Hi,
Quoted 9 lines · Will Deacon
Now that EL2 is able to manage guest stage-2 page-tables, avoid allocating a separate MMU structure in the host and instead introduce a new fault handler which responds to guest stage-2 faults by sharing GUP-pinned pages with the guest via a hypercall. These pages are recovered (and unpinned) on guest teardown via the page reclaim hypercall.
Signed-off-by Will Deacon <[email protected]>---
[..]
Quoted 16 lines
arch/arm64/kvm/mmu.c +16
-0+static int pkvm_mem_abort (struct kvm_vcpu *vcpu,phys_addr_t fault_ipa, +unsigned long hva) +{ +struct kvm_hyp_memcache *hyp_memcache = &vcpu->arch.pkvm_memcache; +struct mm_struct *mm = current->mm; +unsigned int flags =FOLL_HWPOISON |FOLL_LONGTERM |FOLL_WRITE ; +struct kvm_pinned_page *ppage; +struct kvm *kvm = vcpu->kvm; +struct page *page; +u64 pfn; +int ret; + + ret =topup_hyp_memcache (hyp_memcache,kvm_mmu_cache_min_pages (kvm)); +if (ret) +return -ENOMEM ; +
arch/arm64/kvm/mmu.c static int sanitise_mte_tags(struct kvm *kvm, kvm_pfn_t pfn, · line 1183
Quoted 10 lines
arch/arm64/kvm/mmu.c +10
-0+ ppage =kmalloc (sizeof (*ppage),GFP_KERNEL_ACCOUNT ); +if (!ppage) +return -ENOMEM ; + + ret =account_locked_vm (mm,1 , true); +if (ret) + goto free_ppage; + +mmap_read_lock (mm); + ret =pin_user_pages (hva,1 , flags, &page,NULL );
When I implemented memory pinning via GUP for the KVM SPE series, I discovered that the pages were regularly unmapped at stage 2 because of automatic numa balancing, as change_prot_numa() ends up calling mmu_notifier_invalidate_range_start().
I was curious how you managed to avoid that, I don't know my way around pKVM and can't seem to find where that's implemented.
Thanks,
Alex
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerRe: [PATCH 33/89] KVM: arm64: Handle guest stage-2 page-tables entirely at EL2
Quoted 43 lines · Alexandru Elisei
Quoted 9 lines · Will Deacon
Now that EL2 is able to manage guest stage-2 page-tables, avoid allocating a separate MMU structure in the host and instead introduce a new fault handler which responds to guest stage-2 faults by sharing GUP-pinned pages with the guest via a hypercall. These pages are recovered (and unpinned) on guest teardown via the page reclaim hypercall.
Signed-off-by Will Deacon <[email protected]>---
[..]
Quoted 16 lines
arch/arm64/kvm/mmu.c +16
-0+static int pkvm_mem_abort (struct kvm_vcpu *vcpu,phys_addr_t fault_ipa, +unsigned long hva) +{ +struct kvm_hyp_memcache *hyp_memcache = &vcpu->arch.pkvm_memcache; +struct mm_struct *mm = current->mm; +unsigned int flags =FOLL_HWPOISON |FOLL_LONGTERM |FOLL_WRITE ; +struct kvm_pinned_page *ppage; +struct kvm *kvm = vcpu->kvm; +struct page *page; +u64 pfn; +int ret; + + ret =topup_hyp_memcache (hyp_memcache,kvm_mmu_cache_min_pages (kvm)); +if (ret) +return -ENOMEM ; +arch/arm64/kvm/mmu.c static int sanitise_mte_tags(struct kvm *kvm, kvm_pfn_t pfn, · line 1183
Quoted 10 lines
arch/arm64/kvm/mmu.c +10
-0+ ppage =kmalloc (sizeof (*ppage),GFP_KERNEL_ACCOUNT ); +if (!ppage) +return -ENOMEM ; + + ret =account_locked_vm (mm,1 , true); +if (ret) + goto free_ppage; + +mmap_read_lock (mm); + ret =pin_user_pages (hva,1 , flags, &page,NULL );When I implemented memory pinning via GUP for the KVM SPE series, I discovered that the pages were regularly unmapped at stage 2 because of automatic numa balancing, as
change_prot_numa()ends up callingmmu_notifier_invalidate_range_start().
Quoted 2 lines
I was curious how you managed to avoid that, I don't know my way around pKVM and can't seem to find where that's implemented.
With this series, we don't take any notice of the MMU notifiers at EL2 so the stage-2 remains intact. The GUP pin will prevent the page from being migrated as the rmap walker won't be able to drop the mapcount.
It's functional, but we'd definitely like to do better in the long term. The fd-based approach that I mentioned in the cover letter gets us some of the way there for protected guests ("private memory"), but non-protected guests running under pKVM are proving to be pretty challenging (we need to deal with things like sharing the zero page...).
Will
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Re: [PATCH 33/89] KVM: arm64: Handle guest stage-2 page-tables entirely at EL2
Quoted 51 lines · Will Deacon
Quoted 43 lines · Alexandru Elisei
Quoted 9 lines · Will Deacon
Now that EL2 is able to manage guest stage-2 page-tables, avoid allocating a separate MMU structure in the host and instead introduce a new fault handler which responds to guest stage-2 faults by sharing GUP-pinned pages with the guest via a hypercall. These pages are recovered (and unpinned) on guest teardown via the page reclaim hypercall.
Signed-off-by Will Deacon <[email protected]>---
[..]
Quoted 16 lines
arch/arm64/kvm/mmu.c +16
-0+static int pkvm_mem_abort (struct kvm_vcpu *vcpu,phys_addr_t fault_ipa, +unsigned long hva) +{ +struct kvm_hyp_memcache *hyp_memcache = &vcpu->arch.pkvm_memcache; +struct mm_struct *mm = current->mm; +unsigned int flags =FOLL_HWPOISON |FOLL_LONGTERM |FOLL_WRITE ; +struct kvm_pinned_page *ppage; +struct kvm *kvm = vcpu->kvm; +struct page *page; +u64 pfn; +int ret; + + ret =topup_hyp_memcache (hyp_memcache,kvm_mmu_cache_min_pages (kvm)); +if (ret) +return -ENOMEM ; +arch/arm64/kvm/mmu.c static int sanitise_mte_tags(struct kvm *kvm, kvm_pfn_t pfn, · line 1183
Quoted 10 lines
arch/arm64/kvm/mmu.c +10
-0+ ppage =kmalloc (sizeof (*ppage),GFP_KERNEL_ACCOUNT ); +if (!ppage) +return -ENOMEM ; + + ret =account_locked_vm (mm,1 , true); +if (ret) + goto free_ppage; + +mmap_read_lock (mm); + ret =pin_user_pages (hva,1 , flags, &page,NULL );When I implemented memory pinning via GUP for the KVM SPE series, I discovered that the pages were regularly unmapped at stage 2 because of automatic numa balancing, as
change_prot_numa()ends up callingmmu_notifier_invalidate_range_start().Quoted 2 lines
I was curious how you managed to avoid that, I don't know my way around pKVM and can't seem to find where that's implemented.
With this series, we don't take any notice of the MMU notifiers at EL2 so the stage-2 remains intact. The GUP pin will prevent the page from being migrated as the rmap walker won't be able to drop the mapcount.
Quoted 7 lines
It's functional, but we'd definitely like to do better in the long term. The fd-based approach that I mentioned in the cover letter gets us some of the way there for protected guests ("private memory"), but non-protected guests running under pKVM are proving to be pretty challenging (we need to deal with things like sharing the zero page...).
Will
My understanding is that with the pin_user_pages, the page that used by guests (both protected and non-protected) will stay for a long time, and the page will not be swapped or migrated. So no need to care about the MMU notifiers. Is it right?
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH 33/89] KVM: arm64: Handle guest stage-2 page-tables entirely at EL2
Hi,
Quoted 59 lines · Shaoqin
Quoted 57 lines · Will Deacon
Quoted 43 lines · Alexandru Elisei
Quoted 9 lines · Will Deacon
Now that EL2 is able to manage guest stage-2 page-tables, avoid allocating a separate MMU structure in the host and instead introduce a new fault handler which responds to guest stage-2 faults by sharing GUP-pinned pages with the guest via a hypercall. These pages are recovered (and unpinned) on guest teardown via the page reclaim hypercall.
Signed-off-by Will Deacon <[email protected]>---
[..]
Quoted 16 lines
arch/arm64/kvm/mmu.c +16
-0+static int pkvm_mem_abort (struct kvm_vcpu *vcpu,phys_addr_t fault_ipa, +unsigned long hva) +{ +struct kvm_hyp_memcache *hyp_memcache = &vcpu->arch.pkvm_memcache; +struct mm_struct *mm = current->mm; +unsigned int flags =FOLL_HWPOISON |FOLL_LONGTERM |FOLL_WRITE ; +struct kvm_pinned_page *ppage; +struct kvm *kvm = vcpu->kvm; +struct page *page; +u64 pfn; +int ret; + + ret =topup_hyp_memcache (hyp_memcache,kvm_mmu_cache_min_pages (kvm)); +if (ret) +return -ENOMEM ; +arch/arm64/kvm/mmu.c static int sanitise_mte_tags(struct kvm *kvm, kvm_pfn_t pfn, · line 1183
Quoted 10 lines
arch/arm64/kvm/mmu.c +10
-0+ ppage =kmalloc (sizeof (*ppage),GFP_KERNEL_ACCOUNT ); +if (!ppage) +return -ENOMEM ; + + ret =account_locked_vm (mm,1 , true); +if (ret) + goto free_ppage; + +mmap_read_lock (mm); + ret =pin_user_pages (hva,1 , flags, &page,NULL );When I implemented memory pinning via GUP for the KVM SPE series, I discovered that the pages were regularly unmapped at stage 2 because of automatic numa balancing, as
change_prot_numa()ends up callingmmu_notifier_invalidate_range_start().Quoted 2 lines
I was curious how you managed to avoid that, I don't know my way around pKVM and can't seem to find where that's implemented.
With this series, we don't take any notice of the MMU notifiers at EL2 so the stage-2 remains intact. The GUP pin will prevent the page from being migrated as the rmap walker won't be able to drop the mapcount.
It's functional, but we'd definitely like to do better in the long term. The fd-based approach that I mentioned in the cover letter gets us some of the way there for protected guests ("private memory"), but non-protected guests running under pKVM are proving to be pretty challenging (we need to deal with things like sharing the zero page...).
Quoted 6 lines
Quoted 1 line
Will
My understanding is that with the
pin_user_pages, the page that used by guests (both protected and non-protected) will stay for a long time, and the page will not be swapped or migrated. So no need to care about the MMU notifiers. Is it right?
There are two things here.
First, pinning a page means making the data persistent in memory. From Documentation/core-api/pin_user_pages.rst:
"FOLL_PIN is a *replacement* for FOLL_GET, and is for short term pins on pages whose data *will* get accessed. As such, FOLL_PIN is a "more severe" form of pinning. And finally, FOLL_LONGTERM is an even more restrictive case that has FOLL_PIN as a prerequisite: this is for pages that will be pinned longterm, and whose data will be accessed."
It does not mean that the translation table entry for the page is not modified for as long as the pin exists. In the example I gave, automatic NUMA balancing changes the protection of translation table entries to PAGE_NONE, which will invoke the MMU notifers to unmap the corresponding stage 2 entries, regardless of the fact that the pinned pages will not get migrated the next time they are accessed.
There are other mechanisms in the kernel that do that, for example split_huge_pmd(), which must always succeed, even if the THP is pinned (it transfers the refcounts among the pages): "Note that split_huge_pmd() doesn't have any limitations on refcounting: pmd can be split at any point and never fails" (Documentation/vm/transhuge.rst, also see __split_huge_pmd() from mm/huge_memory.c).
KSM also does that: it invokes the invalidate_range_start MMU notifier before backing out of the merge because of the refcount (see mm/ksm.c::try_to_merge_one_page -> write_protect_page).
This brings me to my second point: one might rightfully ask themselves (I did!), why not invoke the MMU notifiers *after* checking that the page is not pinned? It turns out that that is not reliable, because the refcount is increased by GUP with the page lock held (which is a spinlock), but by their design the invalidate_range_start MMU notifiers must be called from interruptible + preemptible context. The only way to avoid races would be to call the MMU notifier while holding the page table lock, which is impossible.
Hope my explanation has been adequate.
Thanks,
Alex
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Re: [PATCH 33/89] KVM: arm64: Handle guest stage-2 page-tables entirely at EL2
Quoted 107 lines · Alexandru Elisei
Hi,
Quoted 59 lines · Shaoqin
Quoted 57 lines · Will Deacon
Quoted 43 lines · Alexandru Elisei
Quoted 9 lines · Will Deacon
Now that EL2 is able to manage guest stage-2 page-tables, avoid allocating a separate MMU structure in the host and instead introduce a new fault handler which responds to guest stage-2 faults by sharing GUP-pinned pages with the guest via a hypercall. These pages are recovered (and unpinned) on guest teardown via the page reclaim hypercall.
Signed-off-by Will Deacon <[email protected]>---
[..]
Quoted 16 lines
arch/arm64/kvm/mmu.c +16
-0+static int pkvm_mem_abort (struct kvm_vcpu *vcpu,phys_addr_t fault_ipa, +unsigned long hva) +{ +struct kvm_hyp_memcache *hyp_memcache = &vcpu->arch.pkvm_memcache; +struct mm_struct *mm = current->mm; +unsigned int flags =FOLL_HWPOISON |FOLL_LONGTERM |FOLL_WRITE ; +struct kvm_pinned_page *ppage; +struct kvm *kvm = vcpu->kvm; +struct page *page; +u64 pfn; +int ret; + + ret =topup_hyp_memcache (hyp_memcache,kvm_mmu_cache_min_pages (kvm)); +if (ret) +return -ENOMEM ; +arch/arm64/kvm/mmu.c static int sanitise_mte_tags(struct kvm *kvm, kvm_pfn_t pfn, · line 1183
Quoted 10 lines
arch/arm64/kvm/mmu.c +10
-0+ ppage =kmalloc (sizeof (*ppage),GFP_KERNEL_ACCOUNT ); +if (!ppage) +return -ENOMEM ; + + ret =account_locked_vm (mm,1 , true); +if (ret) + goto free_ppage; + +mmap_read_lock (mm); + ret =pin_user_pages (hva,1 , flags, &page,NULL );When I implemented memory pinning via GUP for the KVM SPE series, I discovered that the pages were regularly unmapped at stage 2 because of automatic numa balancing, as
change_prot_numa()ends up callingmmu_notifier_invalidate_range_start().Quoted 2 lines
I was curious how you managed to avoid that, I don't know my way around pKVM and can't seem to find where that's implemented.
With this series, we don't take any notice of the MMU notifiers at EL2 so the stage-2 remains intact. The GUP pin will prevent the page from being migrated as the rmap walker won't be able to drop the mapcount.
It's functional, but we'd definitely like to do better in the long term. The fd-based approach that I mentioned in the cover letter gets us some of the way there for protected guests ("private memory"), but non-protected guests running under pKVM are proving to be pretty challenging (we need to deal with things like sharing the zero page...).
Quoted 6 lines
Quoted 1 line
Will
My understanding is that with the
pin_user_pages, the page that used by guests (both protected and non-protected) will stay for a long time, and the page will not be swapped or migrated. So no need to care about the MMU notifiers. Is it right?There are two things here.
First, pinning a page means making the data persistent in memory. From
Documentation/core-api/pin_user_pages.rst:"
FOLL_PINis a *replacement* forFOLL_GET, and is for short term pins on pages whose data *will* get accessed. As such,FOLL_PINis a "more severe" form of pinning. And finally,FOLL_LONGTERMis an even more restrictive case that hasFOLL_PINas a prerequisite: this is for pages that will be pinned longterm, and whose data will be accessed."It does not mean that the translation table entry for the page is not modified for as long as the pin exists. In the example I gave, automatic NUMA balancing changes the protection of translation table entries to
PAGE_NONE, which will invoke the MMU notifers to unmap the corresponding stage 2 entries, regardless of the fact that the pinned pages will not get migrated the next time they are accessed.There are other mechanisms in the kernel that do that, for example
split_huge_pmd(), which must always succeed, even if the THP is pinned (it transfers the refcounts among the pages): "Note thatsplit_huge_pmd()doesn't have any limitations on refcounting: pmd can be split at any point and never fails" (Documentation/vm/transhuge.rst, also see__split_huge_pmd()frommm/huge_memory.c).KSM also does that: it invokes the
invalidate_range_startMMU notifier before backing out of the merge because of the refcount (seemm/ksm.c::try_to_merge_one_page->write_protect_page).This brings me to my second point: one might rightfully ask themselves (I did!), why not invoke the MMU notifiers *after* checking that the page is not pinned? It turns out that that is not reliable, because the refcount is increased by GUP with the page lock held (which is a spinlock), but by their design the
invalidate_range_startMMU notifiers must be called from interruptible + preemptible context. The only way to avoid races would be to call the MMU notifier while holding the page table lock, which is impossible.
Quoted 4 lines
Hope my explanation has been adequate.
Thanks,
Alex
Thanks for your clear explanation.
Will Deacon
maintainerPATCH 34/89 KVM: arm64: Don't access kvm_arm_hyp_percpu_base at EL1
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
The host KVM PMU code can currently index kvm_arm_hyp_percpu_base[] through this_cpu_ptr_hyp_sym(), but will not actually dereference that pointer when protected KVM is enabled. In preparation for making kvm_arm_hyp_percpu_base[] unaccessible to the host, let's make sure the indexing in hyp per-cpu pages is also done after the static key check to avoid spurious accesses to EL2-private data from EL1.
--- arch/arm64/kvm/pmu.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
arch/arm64/kvm/pmu.c +12 -4
@@ -31,9 +31,13 @@ static bool kvm_pmu_switch_needed(struct perf_event_attr *attr)*/ void kvm_set_pmu_events (u32 set,struct perf_event_attr *attr) { -struct kvm_host_data *ctx =this_cpu_ptr_hyp_sym (kvm_host_data); +struct kvm_host_data *ctx; -if (!kvm_arm_support_pmu_v3 () || !ctx || !kvm_pmu_switch_needed (attr)) +if (!kvm_arm_support_pmu_v3 ()) +return ; + + ctx =this_cpu_ptr_hyp_sym (kvm_host_data); +if (!ctx || !kvm_pmu_switch_needed (attr))return ;if (!attr->exclude_host)
@@ -47,9 +51,13 @@ void kvm_set_pmu_events(u32 set, struct perf_event_attr *attr)*/ void kvm_clr_pmu_events (u32 clr) { -struct kvm_host_data *ctx =this_cpu_ptr_hyp_sym (kvm_host_data); +struct kvm_host_data *ctx; + +if (!kvm_arm_support_pmu_v3 ()) +return ; -if (!kvm_arm_support_pmu_v3 () || !ctx) + ctx =this_cpu_ptr_hyp_sym (kvm_host_data); +if (!ctx)return ; ctx->pmu_events.events_host &= ~clr;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 35/89 KVM: arm64: Unmap kvm_arm_hyp_percpu_base from the host
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
In pKVM mode, we can't trust the host not to mess with the hypervisor per-cpu offsets, so let's move the array containing them to the nVHE code.
--- arch/arm64/include/asm/kvm_asm.h | 4 ++-- arch/arm64/kernel/image-vars.h | 3 --- arch/arm64/kvm/arm.c | 9 ++++----- arch/arm64/kvm/hyp/nvhe/hyp-smp.c | 2 ++ 4 files changed, 8 insertions(+), 10 deletions(-)
arch/arm64/include/asm/kvm_asm.h +2 -2
@@ -110,7 +110,7 @@ enum __kvm_host_smccc_func {#define per_cpu_ptr_nvhe_sym (sym, cpu) \ ({ \ unsigned long base, off; \ - base = kvm_arm_hyp_percpu_base[cpu]; \ + base = kvm_nvhe_sym(kvm_arm_hyp_percpu_base)[cpu]; \ off = (unsigned long)&CHOOSE_NVHE_SYM(sym) - \ (unsigned long)&CHOOSE_NVHE_SYM(__per_cpu_start); \ base ? (typeof(CHOOSE_NVHE_SYM(sym))*)(base + off) : NULL; \
@@ -198,7 +198,7 @@ DECLARE_KVM_HYP_SYM(__kvm_hyp_vector);#define __kvm_hyp_init CHOOSE_NVHE_SYM(__kvm_hyp_init)#define __kvm_hyp_vector CHOOSE_HYP_SYM(__kvm_hyp_vector) -extern unsigned long kvm_arm_hyp_percpu_base[NR_CPUS ]; +extern unsigned long kvm_nvhe_sym (kvm_arm_hyp_percpu_base )[];DECLARE_KVM_NVHE_SYM (__per_cpu_start);DECLARE_KVM_NVHE_SYM (__per_cpu_end);
arch/arm64/kernel/image-vars.h +0 -3
@@ -102,9 +102,6 @@ KVM_NVHE_ALIAS(gic_nonsecure_priorities);KVM_NVHE_ALIAS (__start___kvm_ex_table);KVM_NVHE_ALIAS (__stop___kvm_ex_table); -/* Array containing bases of nVHE per-CPU memory regions. */ -KVM_NVHE_ALIAS (kvm_arm_hyp_percpu_base); -/* PMU available static key */ #ifdef CONFIG_HW_PERF_EVENTS KVM_NVHE_ALIAS (kvm_arm_pmu_available);
arch/arm64/kvm/arm.c +4 -5
@@ -51,7 +51,6 @@ DEFINE_STATIC_KEY_FALSE(kvm_protected_mode_initialized); DECLARE_KVM_HYP_PER_CPU(unsigned long, kvm_hyp_vector); static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page); -unsigned long kvm_arm_hyp_percpu_base[NR_CPUS ];DECLARE_KVM_NVHE_PER_CPU (struct kvm_nvhe_init_params , kvm_init_params);static bool vgic_present;
@@ -1800,13 +1799,13 @@ static void teardown_hyp_mode(void)free_hyp_pgds ();for_each_possible_cpu (cpu) {free_page (per_cpu (kvm_arm_hyp_stack_page, cpu)); -free_pages (kvm_arm_hyp_percpu_base[cpu],nvhe_percpu_order ()); +free_pages (kvm_nvhe_sym (kvm_arm_hyp_percpu_base)[cpu],nvhe_percpu_order ()); } }static int do_pkvm_init (u32 hyp_va_bits) { -void *per_cpu_base =kvm_ksym_ref (kvm_arm_hyp_percpu_base); +void *per_cpu_base =kvm_ksym_ref (kvm_nvhe_sym (kvm_arm_hyp_percpu_base));int ret;preempt_disable ();
@@ -1907,7 +1906,7 @@ static int init_hyp_mode(void) page_addr =page_address (page);memcpy (page_addr,CHOOSE_NVHE_SYM (__per_cpu_start),nvhe_percpu_size ()); - kvm_arm_hyp_percpu_base[cpu] = (unsigned long )page_addr; +kvm_nvhe_sym (kvm_arm_hyp_percpu_base)[cpu] = (unsigned long )page_addr; }/*
@@ -1968,7 +1967,7 @@ static int init_hyp_mode(void) }for_each_possible_cpu (cpu) { -char *percpu_begin = (char *)kvm_arm_hyp_percpu_base[cpu]; +char *percpu_begin = (char *)kvm_nvhe_sym (kvm_arm_hyp_percpu_base)[cpu];char *percpu_end = percpu_begin +nvhe_percpu_size ();/* Map Hyp percpu pages */
arch/arm64/kvm/hyp/nvhe/hyp-smp.c +2 -0
@@ -23,6 +23,8 @@ u64 cpu_logical_map(unsigned int cpu)return hyp_cpu_logical_map[cpu]; } +unsigned long __ro_after_init kvm_arm_hyp_percpu_base[NR_CPUS ]; +unsigned long __hyp_per_cpu_offset (unsigned int cpu) {unsigned long *cpu_base_array;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 36/89 KVM: arm64: Maintain a copy of 'kvm_arm_vmid_bits' at EL2
In reply to Will DeaconSharing 'kvm_arm_vmids_bits' between EL1 and EL2 allows the host to modify the variable arbitrarily, potentially leading to all sorts of shenanians as this is used to configure the VTTBR register for the guest stage-2.
In preparation for unmapping host sections entirely from EL2, maintain a copy of 'kvm_arm_vmid_bits' and initialise it from the host value while it is still trusted.
--- arch/arm64/include/asm/kvm_hyp.h | 2 ++ arch/arm64/kernel/image-vars.h | 3 --- arch/arm64/kvm/arm.c | 1 + arch/arm64/kvm/hyp/nvhe/pkvm.c | 3 +++ 4 files changed, 6 insertions(+), 3 deletions(-)
arch/arm64/include/asm/kvm_hyp.h +2 -0
@@ -124,4 +124,6 @@ extern u64 kvm_nvhe_sym(id_aa64mmfr1_el1_sys_val);extern u64 kvm_nvhe_sym (id_aa64mmfr2_el1_sys_val );extern unsigned long kvm_nvhe_sym (__icache_flags ); +extern unsigned int kvm_nvhe_sym (kvm_arm_vmid_bits ); +#endif /* __ARM64_KVM_HYP_H__ */
arch/arm64/kernel/image-vars.h +0 -3
@@ -80,9 +80,6 @@ KVM_NVHE_ALIAS(nvhe_hyp_panic_handler);/* Vectors installed by hyp-init on reset HVC. */ KVM_NVHE_ALIAS (__hyp_stub_vectors); -/* VMID bits set by the KVM VMID allocator */ -KVM_NVHE_ALIAS (kvm_arm_vmid_bits); -/* Kernel symbols needed for cpus_have_final/const_caps checks. */ KVM_NVHE_ALIAS (arm64_const_caps_ready);KVM_NVHE_ALIAS (cpu_hwcap_keys);
arch/arm64/kvm/arm.c +1 -0
@@ -1839,6 +1839,7 @@ static int kvm_hyp_init_protection(u32 hyp_va_bits)kvm_nvhe_sym (id_aa64mmfr1_el1_sys_val) =read_sanitised_ftr_reg (SYS_ID_AA64MMFR1_EL1 );kvm_nvhe_sym (id_aa64mmfr2_el1_sys_val) =read_sanitised_ftr_reg (SYS_ID_AA64MMFR2_EL1 );kvm_nvhe_sym (__icache_flags) = __icache_flags; +kvm_nvhe_sym (kvm_arm_vmid_bits) = kvm_arm_vmid_bits; ret =create_hyp_mappings (addr, addr + hyp_mem_size,PAGE_HYP );if (ret)
arch/arm64/kvm/hyp/nvhe/pkvm.c +3 -0
@@ -15,6 +15,9 @@/* Used by icache_is_vpipt(). */ unsigned long __icache_flags; +/* Used by kvm_get_vttbr(). */ +unsigned int kvm_arm_vmid_bits; +/* * Set trap register values based on features in ID_AA64PFR0. */
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 37/89 KVM: arm64: Explicitly map kvm_vgic_global_state at EL2
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
The pkvm hypervisor may need to read the kvm_vgic_global_state variable at EL2. Make sure to explicitly map it in its stage-1 page-table rather than relying on mapping all of the host .rodata section.
--- arch/arm64/kvm/hyp/nvhe/setup.c | 5 +++++ 1 file changed, 5 insertions(+)
arch/arm64/kvm/hyp/nvhe/setup.c +5 -0
@@ -136,6 +136,11 @@ static int recreate_hyp_mappings(phys_addr_t phys, unsigned long size,if (ret)return ret; + ret =pkvm_create_mappings (&kvm_vgic_global_state, + &kvm_vgic_global_state +1 , prot); +if (ret) +return ret; +return 0 ; }
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 38/89 KVM: arm64: Don't map host sections in pkvm
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
We no longer need to map the host's .rodata and .bss sections in the pkvm hypervisor, so let's remove those mappings. This will avoid creating dependencies at EL2 on host-controlled data-structures.
--- arch/arm64/kernel/image-vars.h | 6 ------ arch/arm64/kvm/hyp/nvhe/setup.c | 14 +++----------- 2 files changed, 3 insertions(+), 17 deletions(-)
arch/arm64/kernel/image-vars.h +0 -6
@@ -115,12 +115,6 @@ KVM_NVHE_ALIAS_HYP(__memcpy, __pi_memcpy);KVM_NVHE_ALIAS_HYP (__memset, __pi_memset);#endif -/* Kernel memory sections */ -KVM_NVHE_ALIAS (__start_rodata); -KVM_NVHE_ALIAS (__end_rodata); -KVM_NVHE_ALIAS (__bss_start); -KVM_NVHE_ALIAS (__bss_stop); -/* Hyp memory sections */ KVM_NVHE_ALIAS (__hyp_idmap_text_start);KVM_NVHE_ALIAS (__hyp_idmap_text_end);
arch/arm64/kvm/hyp/nvhe/setup.c +3 -11
@@ -119,23 +119,15 @@ static int recreate_hyp_mappings(phys_addr_t phys, unsigned long size, }/* -* Map the host's .bss and .rodata sections RO in the hypervisor, but -* transfer the ownership from the host to the hypervisor itself to -* make sure it can't be donated or shared with another entity. +* Map the host sections RO in the hypervisor, but transfer the +* ownership from the host to the hypervisor itself to make sure they +* can't be donated or shared with another entity. * * The ownership transition requires matching changes in the host * stage-2. This will be done later (see finalize_host_mappings()) once * the hyp_vmemmap is addressable. */ prot =pkvm_mkstate (PAGE_HYP_RO ,PKVM_PAGE_SHARED_OWNED ); - ret =pkvm_create_mappings (__start_rodata, __end_rodata, prot); -if (ret) -return ret; - - ret =pkvm_create_mappings (__hyp_bss_end, __bss_stop, prot); -if (ret) -return ret; - ret =pkvm_create_mappings (&kvm_vgic_global_state, &kvm_vgic_global_state +1 , prot);if (ret)
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 39/89 KVM: arm64: Extend memory donation to allow host-to-guest transitions
In reply to Will DeaconIn preparation for supporting protected guests, where guest memory defaults to being inaccessible to the host, extend our memory protection mechanisms to support donation of pages from the host to a specific guest.
--- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 1 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 62 +++++++++++++++++++ arch/arm64/kvm/hyp/pgtable.c | 2 +- 3 files changed, 64 insertions(+), 1 deletion(-)
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +1 -0
@@ -69,6 +69,7 @@ int __pkvm_host_reclaim_page(u64 pfn);int __pkvm_host_donate_hyp (u64 pfn,u64 nr_pages);int __pkvm_hyp_donate_host (u64 pfn,u64 nr_pages);int __pkvm_host_share_guest (u64 pfn,u64 gfn,struct kvm_vcpu *vcpu); +int __pkvm_host_donate_guest (u64 pfn,u64 gfn,struct kvm_vcpu *vcpu);bool addr_is_memory (phys_addr_t phys);int host_stage2_idmap_locked (phys_addr_t addr,u64 size,enum kvm_pgtable_prot prot);
arch/arm64/kvm/hyp/nvhe/mem_protect.c +62 -0
92 lines not on this page · raw message
arch/arm64/kvm/hyp/pgtable.c +1 -1
@@ -47,7 +47,7 @@ KVM_PTE_LEAF_ATTR_HI_S2_XN)#define KVM_INVALID_PTE_OWNER_MASK GENMASK(9, 2) -#define KVM_MAX_OWNER_ID 1 +#define KVM_MAX_OWNER_ID FIELD_MAX(KVM_INVALID_PTE_OWNER_MASK)struct kvm_pgtable_walk_data {struct kvm_pgtable *pgt;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 40/89 KVM: arm64: Split up nvhe/fixed_config.h
In reply to Will DeaconIn preparation for using some of the pKVM fixed configuration register definitions to filter the available VM CAPs in the host, split the nvhe/fixed_config.h header so that the definitions can be shared with the host, while keeping the hypervisor function prototypes in the nvhe/ namespace.
--- arch/arm64/include/asm/kvm_pkvm.h | 190 ++++++++++++++++ .../arm64/kvm/hyp/include/nvhe/fixed_config.h | 205 ------------------ arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 6 +- arch/arm64/kvm/hyp/nvhe/pkvm.c | 1 - arch/arm64/kvm/hyp/nvhe/setup.c | 1 - arch/arm64/kvm/hyp/nvhe/switch.c | 2 +- arch/arm64/kvm/hyp/nvhe/sys_regs.c | 2 +- 7 files changed, 197 insertions(+), 210 deletions(-) delete mode 100644 arch/arm64/kvm/hyp/include/nvhe/fixed_config.h
arch/arm64/include/asm/kvm_pkvm.h +190 -0
@@ -2,12 +2,14 @@/* * Copyright (C) 2020 - Google LLC * Author: Quentin Perret <[email protected]> +* Author: Fuad Tabba <[email protected]> */ #ifndef __ARM64_KVM_PKVM_H__#define __ARM64_KVM_PKVM_H__#include <linux/memblock.h> #include <asm/kvm_pgtable.h> +#include <asm/sysreg.h> /* Maximum number of protected VMs that can be created. */ #define KVM_MAX_PVMS 255
@@ -18,6 +20,194 @@ int kvm_init_pvm(struct kvm *kvm);int kvm_shadow_create (struct kvm *kvm);void kvm_shadow_destroy (struct kvm *kvm); +/* +* Definitions for features to be allowed or restricted for guest virtual +* machines, depending on the mode KVM is running in and on the type of guest +* that is running. +* +* The ALLOW masks represent a bitmask of feature fields that are allowed +* without any restrictions as long as they are supported by the system. +* +* The RESTRICT_UNSIGNED masks, if present, represent unsigned fields for +* features that are restricted to support at most the specified feature. +* +* If a feature field is not present in either, than it is not supported. +* +* The approach taken for protected VMs is to allow features that are: +* - Needed by common Linux distributions (e.g., floating point) +* - Trivial to support, e.g., supporting the feature does not introduce or +* require tracking of additional state in KVM +* - Cannot be trapped or prevent the guest from using anyway +*/ + +/* +* Allow for protected VMs: +* - Floating-point and Advanced SIMD +* - Data Independent Timing +*/ +#define PVM_ID_AA64PFR0_ALLOW (\ + ARM64_FEATURE_MASK(ID_AA64PFR0_FP) | \ + ARM64_FEATURE_MASK(ID_AA64PFR0_ASIMD) | \ + ARM64_FEATURE_MASK(ID_AA64PFR0_DIT) \ + ) + +/* +* Restrict to the following *unsigned* features for protected VMs: +* - AArch64 guests only (no support for AArch32 guests): +* AArch32 adds complexity in trap handling, emulation, condition codes, +* etc... +* - RAS (v1) +* Supported by KVM +*/ +#define PVM_ID_AA64PFR0_RESTRICT_UNSIGNED (\ + FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL0), ID_AA64PFR0_ELx_64BIT_ONLY) | \ + FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1), ID_AA64PFR0_ELx_64BIT_ONLY) | \ + FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL2), ID_AA64PFR0_ELx_64BIT_ONLY) | \ + FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL3), ID_AA64PFR0_ELx_64BIT_ONLY) | \ + FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_RAS), ID_AA64PFR0_RAS_V1) \ + ) + +/* +* Allow for protected VMs: +* - Branch Target Identification +* - Speculative Store Bypassing +*/ +#define PVM_ID_AA64PFR1_ALLOW (\ + ARM64_FEATURE_MASK(ID_AA64PFR1_BT) | \ + ARM64_FEATURE_MASK(ID_AA64PFR1_SSBS) \ + ) + +/* +* Allow for protected VMs: +* - Mixed-endian +* - Distinction between Secure and Non-secure Memory +* - Mixed-endian at EL0 only +* - Non-context synchronizing exception entry and exit +*/ +#define PVM_ID_AA64MMFR0_ALLOW (\ + ARM64_FEATURE_MASK(ID_AA64MMFR0_BIGENDEL) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR0_SNSMEM) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR0_BIGENDEL0) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR0_EXS) \ + ) + +/* +* Restrict to the following *unsigned* features for protected VMs: +* - 40-bit IPA +* - 16-bit ASID +*/ +#define PVM_ID_AA64MMFR0_RESTRICT_UNSIGNED (\ + FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64MMFR0_PARANGE), ID_AA64MMFR0_PARANGE_40) | \ + FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64MMFR0_ASID), ID_AA64MMFR0_ASID_16) \ + ) + +/* +* Allow for protected VMs: +* - Hardware translation table updates to Access flag and Dirty state +* - Number of VMID bits from CPU +* - Hierarchical Permission Disables +* - Privileged Access Never +* - SError interrupt exceptions from speculative reads +* - Enhanced Translation Synchronization +*/ +#define PVM_ID_AA64MMFR1_ALLOW (\ + ARM64_FEATURE_MASK(ID_AA64MMFR1_HADBS) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR1_VMIDBITS) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR1_HPD) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR1_PAN) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR1_SPECSEI) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR1_ETS) \ + ) + +/* +* Allow for protected VMs: +* - Common not Private translations +* - User Access Override +* - IESB bit in the SCTLR_ELx registers +* - Unaligned single-copy atomicity and atomic functions +* - ESR_ELx.EC value on an exception by read access to feature ID space +* - TTL field in address operations. +* - Break-before-make sequences when changing translation block size +* - E0PDx mechanism +*/ +#define PVM_ID_AA64MMFR2_ALLOW (\ + ARM64_FEATURE_MASK(ID_AA64MMFR2_CNP) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR2_UAO) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR2_IESB) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR2_AT) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR2_IDS) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR2_TTL) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR2_BBM) | \ + ARM64_FEATURE_MASK(ID_AA64MMFR2_E0PD) \ + ) + +/* +* No support for Scalable Vectors for protected VMs: +* Requires additional support from KVM, e.g., context-switching and +* trapping at EL2 +*/ +#define PVM_ID_AA64ZFR0_ALLOW (0ULL) + +/* +* No support for debug, including breakpoints, and watchpoints for protected +* VMs: +* The Arm architecture mandates support for at least the Armv8 debug +* architecture, which would include at least 2 hardware breakpoints and +* watchpoints. Providing that support to protected guests adds +* considerable state and complexity. Therefore, the reserved value of 0 is +* used for debug-related fields. +*/ +#define PVM_ID_AA64DFR0_ALLOW (0ULL) +#define PVM_ID_AA64DFR1_ALLOW (0ULL) + +/* +* No support for implementation defined features. +*/ +#define PVM_ID_AA64AFR0_ALLOW (0ULL) +#define PVM_ID_AA64AFR1_ALLOW (0ULL) + +/* +* No restrictions on instructions implemented in AArch64. +*/ +#define PVM_ID_AA64ISAR0_ALLOW (\ + ARM64_FEATURE_MASK(ID_AA64ISAR0_AES) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR0_SHA1) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR0_SHA2) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR0_CRC32) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR0_ATOMICS) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR0_RDM) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR0_SHA3) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR0_SM3) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR0_SM4) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR0_DP) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR0_FHM) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR0_TS) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR0_TLB) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR0_RNDR) \ + ) + +#define PVM_ID_AA64ISAR1_ALLOW (\ + ARM64_FEATURE_MASK(ID_AA64ISAR1_DPB) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR1_APA) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR1_API) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR1_JSCVT) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR1_FCMA) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR1_LRCPC) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR1_GPA) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR1_GPI) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR1_FRINTTS) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR1_SB) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR1_SPECRES) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR1_BF16) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR1_DGH) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR1_I8MM) \ + ) + +#define PVM_ID_AA64ISAR2_ALLOW (\ + ARM64_FEATURE_MASK(ID_AA64ISAR2_GPA3) | \ + ARM64_FEATURE_MASK(ID_AA64ISAR2_APA3) \ + ) +extern struct memblock_region kvm_nvhe_sym (hyp_memory )[];extern unsigned int kvm_nvhe_sym (hyp_memblock_nr );
arch/arm64/kvm/hyp/include/nvhe/fixed_config.h deleted +0 -205
205 lines not on this page · raw message
arch/arm64/kvm/hyp/include/nvhe/pkvm.h +5 -1
@@ -67,5 +67,9 @@ struct kvm_shadow_vcpu_state * pkvm_load_shadow_vcpu_state(unsigned int shadow_handle, unsigned int vcpu_idx);void pkvm_put_shadow_vcpu_state (struct kvm_shadow_vcpu_state *shadow_state); -#endif /* __ARM64_KVM_NVHE_PKVM_H__ */ +u64 pvm_read_id_reg (const struct kvm_vcpu *vcpu,u32 id); +bool kvm_handle_pvm_sysreg (struct kvm_vcpu *vcpu,u64 *exit_code); +bool kvm_handle_pvm_restricted (struct kvm_vcpu *vcpu,u64 *exit_code); +int kvm_check_pvm_sysreg_table (void ); +#endif /* __ARM64_KVM_NVHE_PKVM_H__ */
arch/arm64/kvm/hyp/nvhe/pkvm.c +0 -1
@@ -6,7 +6,6 @@#include <linux/kvm_host.h> #include <linux/mm.h> -#include <nvhe/fixed_config.h> #include <nvhe/mem_protect.h> #include <nvhe/memory.h> #include <nvhe/pkvm.h>
arch/arm64/kvm/hyp/nvhe/setup.c +0 -1
@@ -11,7 +11,6 @@#include <asm/kvm_pkvm.h> #include <nvhe/early_alloc.h> -#include <nvhe/fixed_config.h> #include <nvhe/gfp.h> #include <nvhe/memory.h> #include <nvhe/mem_protect.h>
arch/arm64/kvm/hyp/nvhe/switch.c +1 -1
@@ -26,8 +26,8 @@#include <asm/debug-monitors.h> #include <asm/processor.h> -#include <nvhe/fixed_config.h> #include <nvhe/mem_protect.h> +#include <nvhe/pkvm.h> /* Non-VHE specific context */ DEFINE_PER_CPU(struct kvm_host_data, kvm_host_data);
arch/arm64/kvm/hyp/nvhe/sys_regs.c +1 -1
@@ -11,7 +11,7 @@#include <hyp/adjust_pc.h> -#include <nvhe/fixed_config.h> +#include <nvhe/pkvm.h> #include "../../sys_regs.h"
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 41/89 KVM: arm64: Make vcpu_{read,write}_sys_reg available to HYP code
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
Allow vcpu_{read,write}_sys_reg() to be called from EL2 so that nVHE hyp code can reuse existing helper functions for operations such as resetting the vCPU state.
--- arch/arm64/include/asm/kvm_host.h | 30 +++++++++++++++++++++++++++--- arch/arm64/kvm/sys_regs.c | 20 -------------------- 2 files changed, 27 insertions(+), 23 deletions(-)
arch/arm64/include/asm/kvm_host.h +27 -3
@@ -553,9 +553,6 @@ struct kvm_vcpu_arch {#define __vcpu_sys_reg (v,r) (ctxt_sys_reg(&(v)->arch.ctxt, (r))) -u64 vcpu_read_sys_reg (const struct kvm_vcpu *vcpu,int reg); -void vcpu_write_sys_reg (struct kvm_vcpu *vcpu,u64 val,int reg); -static inline bool __vcpu_read_sys_reg_from_cpu (int reg,u64 *val) {/*
@@ -647,6 +644,33 @@ static inline bool __vcpu_write_sys_reg_to_cpu(u64 val, int reg)return true; } +static inline u64 vcpu_arch_read_sys_reg (const struct kvm_vcpu_arch *vcpu_arch, +int reg) +{ +u64 val =0x8badf00d8badf00d ; + +/* sysregs_loaded_on_cpu is only used in VHE */ +if (!is_nvhe_hyp_code () && vcpu_arch->sysregs_loaded_on_cpu && +__vcpu_read_sys_reg_from_cpu (reg, &val)) +return val; + +return ctxt_sys_reg (&vcpu_arch->ctxt, reg); +} + +static inline void vcpu_arch_write_sys_reg (struct kvm_vcpu_arch *vcpu_arch, +u64 val,int reg) +{ +/* sysregs_loaded_on_cpu is only used in VHE */ +if (!is_nvhe_hyp_code () && vcpu_arch->sysregs_loaded_on_cpu && +__vcpu_write_sys_reg_to_cpu (val, reg)) +return ; + +ctxt_sys_reg (&vcpu_arch->ctxt, reg) = val; +} + +#define vcpu_read_sys_reg (vcpu, reg) vcpu_arch_read_sys_reg(&((vcpu)->arch), reg) +#define vcpu_write_sys_reg (vcpu, val, reg) vcpu_arch_write_sys_reg(&((vcpu)->arch), val, reg) +struct kvm_vm_stat {struct kvm_vm_stat_generic generic; };
arch/arm64/kvm/sys_regs.c +0 -20
@@ -68,26 +68,6 @@ static bool write_to_read_only(struct kvm_vcpu *vcpu,return false; } -u64 vcpu_read_sys_reg (const struct kvm_vcpu *vcpu,int reg) -{ -u64 val =0x8badf00d8badf00d ; - -if (vcpu->arch.sysregs_loaded_on_cpu && -__vcpu_read_sys_reg_from_cpu (reg, &val)) -return val; - -return __vcpu_sys_reg (vcpu, reg); -} - -void vcpu_write_sys_reg (struct kvm_vcpu *vcpu,u64 val,int reg) -{ -if (vcpu->arch.sysregs_loaded_on_cpu && -__vcpu_write_sys_reg_to_cpu (val, reg)) -return ; - -__vcpu_sys_reg (vcpu, reg) = val; -} -/* 3 bits per cache level, as per CLIDR, but non-existent caches always 0 */ static u32 cache_levels;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 42/89 KVM: arm64: Simplify vgic-v3 hypercalls
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
Consolidate the GICv3 VMCR accessor hypercalls into the APR save/restore hypercalls so that all of the EL2 GICv3 state is covered by a single pair of hypercalls.
--- arch/arm64/include/asm/kvm_asm.h | 8 ++------ arch/arm64/include/asm/kvm_hyp.h | 4 ++-- arch/arm64/kvm/arm.c | 7 +++---- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 24 ++++++------------------ arch/arm64/kvm/hyp/vgic-v3-sr.c | 27 +++++++++++++++++++++++---- arch/arm64/kvm/vgic/vgic-v2.c | 9 +-------- arch/arm64/kvm/vgic/vgic-v3.c | 26 ++++---------------------- arch/arm64/kvm/vgic/vgic.c | 17 +++-------------- arch/arm64/kvm/vgic/vgic.h | 6 ++---- include/kvm/arm_vgic.h | 3 +-- 10 files changed, 47 insertions(+), 84 deletions(-)
arch/arm64/include/asm/kvm_asm.h +2 -6
@@ -73,11 +73,9 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid, __KVM_HOST_SMCCC_FUNC___kvm_flush_cpu_context, __KVM_HOST_SMCCC_FUNC___kvm_timer_set_cntvoff, - __KVM_HOST_SMCCC_FUNC___vgic_v3_read_vmcr, - __KVM_HOST_SMCCC_FUNC___vgic_v3_write_vmcr, - __KVM_HOST_SMCCC_FUNC___vgic_v3_save_aprs, - __KVM_HOST_SMCCC_FUNC___vgic_v3_restore_aprs, __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_init_traps, + __KVM_HOST_SMCCC_FUNC___vgic_v3_save_vmcr_aprs, + __KVM_HOST_SMCCC_FUNC___vgic_v3_restore_vmcr_aprs, __KVM_HOST_SMCCC_FUNC___pkvm_init_shadow, __KVM_HOST_SMCCC_FUNC___pkvm_teardown_shadow, };
@@ -218,8 +216,6 @@ extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);extern void __kvm_adjust_pc (struct kvm_vcpu *vcpu);extern u64 __vgic_v3_get_gic_config (void ); -extern u64 __vgic_v3_read_vmcr (void ); -extern void __vgic_v3_write_vmcr (u32 vmcr);extern void __vgic_v3_init_lrs (void );extern u64 __kvm_get_mdcr_el2 (void );
arch/arm64/include/asm/kvm_hyp.h +2 -2
@@ -61,8 +61,8 @@ void __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if);void __vgic_v3_restore_state (struct vgic_v3_cpu_if *cpu_if);void __vgic_v3_activate_traps (struct vgic_v3_cpu_if *cpu_if);void __vgic_v3_deactivate_traps (struct vgic_v3_cpu_if *cpu_if); -void __vgic_v3_save_aprs (struct vgic_v3_cpu_if *cpu_if); -void __vgic_v3_restore_aprs (struct vgic_v3_cpu_if *cpu_if); +void __vgic_v3_save_vmcr_aprs (struct vgic_v3_cpu_if *cpu_if); +void __vgic_v3_restore_vmcr_aprs (struct vgic_v3_cpu_if *cpu_if);int __vgic_v3_perform_cpuif_access (struct kvm_vcpu *vcpu);#ifdef __KVM_NVHE_HYPERVISOR__
arch/arm64/kvm/arm.c +3 -4
@@ -440,7 +440,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)if (has_vhe ())kvm_vcpu_put_sysregs_vhe (vcpu);kvm_timer_vcpu_put (vcpu); -kvm_vgic_put (vcpu); +kvm_vgic_put (vcpu, false);kvm_vcpu_pmu_restore_host (vcpu);kvm_arm_vmid_clear_active ();
@@ -656,15 +656,14 @@ void kvm_vcpu_wfi(struct kvm_vcpu *vcpu)* doorbells to be signalled, should an interrupt become pending. */ preempt_disable (); -kvm_vgic_vmcr_sync (vcpu); -vgic_v4_put (vcpu, true); +kvm_vgic_put (vcpu, true);preempt_enable ();kvm_vcpu_halt (vcpu);kvm_clear_request (KVM_REQ_UNHALT , vcpu);preempt_disable (); -vgic_v4_load (vcpu); +kvm_vgic_load (vcpu);preempt_enable (); }
arch/arm64/kvm/hyp/nvhe/hyp-main.c +6 -18
@@ -205,16 +205,6 @@ static void handle___vgic_v3_get_gic_config(struct kvm_cpu_context *host_ctxt)cpu_reg (host_ctxt,1 ) =__vgic_v3_get_gic_config (); } -static void handle___vgic_v3_read_vmcr (struct kvm_cpu_context *host_ctxt) -{ -cpu_reg (host_ctxt,1 ) =__vgic_v3_read_vmcr (); -} - -static void handle___vgic_v3_write_vmcr (struct kvm_cpu_context *host_ctxt) -{ -__vgic_v3_write_vmcr (cpu_reg (host_ctxt,1 )); -} -static void handle___vgic_v3_init_lrs (struct kvm_cpu_context *host_ctxt) {__vgic_v3_init_lrs ();
@@ -225,18 +215,18 @@ static void handle___kvm_get_mdcr_el2(struct kvm_cpu_context *host_ctxt)cpu_reg (host_ctxt,1 ) =__kvm_get_mdcr_el2 (); } -static void handle___vgic_v3_save_aprs (struct kvm_cpu_context *host_ctxt) +static void handle___vgic_v3_save_vmcr_aprs (struct kvm_cpu_context *host_ctxt) { DECLARE_REG(struct vgic_v3_cpu_if *, cpu_if, host_ctxt, 1); -__vgic_v3_save_aprs (kern_hyp_va (cpu_if)); +__vgic_v3_save_vmcr_aprs (kern_hyp_va (cpu_if)); } -static void handle___vgic_v3_restore_aprs (struct kvm_cpu_context *host_ctxt) +static void handle___vgic_v3_restore_vmcr_aprs (struct kvm_cpu_context *host_ctxt) { DECLARE_REG(struct vgic_v3_cpu_if *, cpu_if, host_ctxt, 1); -__vgic_v3_restore_aprs (kern_hyp_va (cpu_if)); +__vgic_v3_restore_vmcr_aprs (kern_hyp_va (cpu_if)); }static void handle___pkvm_init (struct kvm_cpu_context *host_ctxt)
@@ -349,11 +339,9 @@ static const hcall_t host_hcall[] = {HANDLE_FUNC (__kvm_tlb_flush_vmid),HANDLE_FUNC (__kvm_flush_cpu_context),HANDLE_FUNC (__kvm_timer_set_cntvoff), -HANDLE_FUNC (__vgic_v3_read_vmcr), -HANDLE_FUNC (__vgic_v3_write_vmcr), -HANDLE_FUNC (__vgic_v3_save_aprs), -HANDLE_FUNC (__vgic_v3_restore_aprs),HANDLE_FUNC (__pkvm_vcpu_init_traps), +HANDLE_FUNC (__vgic_v3_save_vmcr_aprs), +HANDLE_FUNC (__vgic_v3_restore_vmcr_aprs),HANDLE_FUNC (__pkvm_init_shadow), HANDLE_FUNC(__pkvm_teardown_shadow), };
arch/arm64/kvm/hyp/vgic-v3-sr.c +23 -4
@@ -330,7 +330,7 @@ void __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if)write_gicreg (0 ,ICH_HCR_EL2 ); } -void __vgic_v3_save_aprs (struct vgic_v3_cpu_if *cpu_if) +static void __vgic_v3_save_aprs (struct vgic_v3_cpu_if *cpu_if) {u64 val;u32 nr_pre_bits;
@@ -363,7 +363,7 @@ void __vgic_v3_save_aprs(struct vgic_v3_cpu_if *cpu_if) } } -void __vgic_v3_restore_aprs (struct vgic_v3_cpu_if *cpu_if) +static void __vgic_v3_restore_aprs (struct vgic_v3_cpu_if *cpu_if) {u64 val;u32 nr_pre_bits;
@@ -455,16 +455,35 @@ u64 __vgic_v3_get_gic_config(void)return val; } -u64 __vgic_v3_read_vmcr (void ) +static u64 __vgic_v3_read_vmcr (void ) {return read_gicreg (ICH_VMCR_EL2 ); } -void __vgic_v3_write_vmcr (u32 vmcr) +static void __vgic_v3_write_vmcr (u32 vmcr) {write_gicreg (vmcr,ICH_VMCR_EL2 ); } +void __vgic_v3_save_vmcr_aprs (struct vgic_v3_cpu_if *cpu_if) +{ +__vgic_v3_save_aprs (cpu_if); +if (cpu_if->vgic_sre) + cpu_if->vgic_vmcr =__vgic_v3_read_vmcr (); +} + +void __vgic_v3_restore_vmcr_aprs (struct vgic_v3_cpu_if *cpu_if) +{ +/* +* If dealing with a GICv2 emulation on GICv3, VMCR_EL2.VFIQen +* is dependent on ICC_SRE_EL1.SRE, and we have to perform the +* VMCR_EL2 save/restore in the world switch. +*/ +if (cpu_if->vgic_sre) +__vgic_v3_write_vmcr (cpu_if->vgic_vmcr); +__vgic_v3_restore_aprs (cpu_if); +} +static int __vgic_v3_bpr_min (void ) {/* See Pseudocode for VPriorityGroup */
arch/arm64/kvm/vgic/vgic-v2.c +1 -8
@@ -470,17 +470,10 @@ void vgic_v2_load(struct kvm_vcpu *vcpu) kvm_vgic_global_state.vctrl_base + GICH_APR); } -void vgic_v2_vmcr_sync (struct kvm_vcpu *vcpu) +void vgic_v2_put (struct kvm_vcpu *vcpu,bool blocking) {struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2; cpu_if->vgic_vmcr =readl_relaxed (kvm_vgic_global_state.vctrl_base +GICH_VMCR ); -} - -void vgic_v2_put (struct kvm_vcpu *vcpu) -{ -struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2; - -vgic_v2_vmcr_sync (vcpu); cpu_if->vgic_apr =readl_relaxed (kvm_vgic_global_state.vctrl_base +GICH_APR ); }
arch/arm64/kvm/vgic/vgic-v3.c +4 -22
@@ -720,15 +720,7 @@ void vgic_v3_load(struct kvm_vcpu *vcpu) {struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; -/* -* If dealing with a GICv2 emulation on GICv3, VMCR_EL2.VFIQen -* is dependent on ICC_SRE_EL1.SRE, and we have to perform the -* VMCR_EL2 save/restore in the world switch. -*/ -if (likely (cpu_if->vgic_sre)) -kvm_call_hyp (__vgic_v3_write_vmcr, cpu_if->vgic_vmcr); - -kvm_call_hyp (__vgic_v3_restore_aprs, cpu_if); +kvm_call_hyp (__vgic_v3_restore_vmcr_aprs, cpu_if);if (has_vhe ())__vgic_v3_activate_traps (cpu_if);
@@ -736,23 +728,13 @@ void vgic_v3_load(struct kvm_vcpu *vcpu)WARN_ON (vgic_v4_load (vcpu)); } -void vgic_v3_vmcr_sync (struct kvm_vcpu *vcpu) +void vgic_v3_put (struct kvm_vcpu *vcpu,bool blocking) {struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; -if (likely (cpu_if->vgic_sre)) - cpu_if->vgic_vmcr =kvm_call_hyp_ret (__vgic_v3_read_vmcr); -} - -void vgic_v3_put (struct kvm_vcpu *vcpu) -{ -struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; - -WARN_ON (vgic_v4_put (vcpu, false)); - -vgic_v3_vmcr_sync (vcpu); +WARN_ON (vgic_v4_put (vcpu, blocking)); -kvm_call_hyp (__vgic_v3_save_aprs, cpu_if); +kvm_call_hyp (__vgic_v3_save_vmcr_aprs, cpu_if);if (has_vhe ())__vgic_v3_deactivate_traps (cpu_if);
arch/arm64/kvm/vgic/vgic.c +3 -14
@@ -931,26 +931,15 @@ void kvm_vgic_load(struct kvm_vcpu *vcpu)vgic_v3_load (vcpu); } -void kvm_vgic_put (struct kvm_vcpu *vcpu) +void kvm_vgic_put (struct kvm_vcpu *vcpu,bool blocking) {if (unlikely (!vgic_initialized (vcpu->kvm)))return ;if (kvm_vgic_global_state.type ==VGIC_V2 ) -vgic_v2_put (vcpu); +vgic_v2_put (vcpu, blocking);else -vgic_v3_put (vcpu); -} - -void kvm_vgic_vmcr_sync (struct kvm_vcpu *vcpu) -{ -if (unlikely (!irqchip_in_kernel (vcpu->kvm))) -return ; - -if (kvm_vgic_global_state.type ==VGIC_V2 ) -vgic_v2_vmcr_sync (vcpu); -else -vgic_v3_vmcr_sync (vcpu); +vgic_v3_put (vcpu, blocking); }int kvm_vgic_vcpu_pending_irq (struct kvm_vcpu *vcpu)
arch/arm64/kvm/vgic/vgic.h +2 -4
@@ -196,8 +196,7 @@ int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address,void vgic_v2_init_lrs (void );void vgic_v2_load (struct kvm_vcpu *vcpu); -void vgic_v2_put (struct kvm_vcpu *vcpu); -void vgic_v2_vmcr_sync (struct kvm_vcpu *vcpu); +void vgic_v2_put (struct kvm_vcpu *vcpu,bool blocking);void vgic_v2_save_state (struct kvm_vcpu *vcpu);void vgic_v2_restore_state (struct kvm_vcpu *vcpu);
@@ -227,8 +226,7 @@ int vgic_register_redist_iodev(struct kvm_vcpu *vcpu);bool vgic_v3_check_base (struct kvm *kvm);void vgic_v3_load (struct kvm_vcpu *vcpu); -void vgic_v3_put (struct kvm_vcpu *vcpu); -void vgic_v3_vmcr_sync (struct kvm_vcpu *vcpu); +void vgic_v3_put (struct kvm_vcpu *vcpu,bool blocking);bool vgic_has_its (struct kvm *kvm);int kvm_vgic_register_its_device (void );
include/kvm/arm_vgic.h +1 -2
@@ -380,8 +380,7 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int vintid);int kvm_vgic_vcpu_pending_irq (struct kvm_vcpu *vcpu);void kvm_vgic_load (struct kvm_vcpu *vcpu); -void kvm_vgic_put (struct kvm_vcpu *vcpu); -void kvm_vgic_vmcr_sync (struct kvm_vcpu *vcpu); +void kvm_vgic_put (struct kvm_vcpu *vcpu,bool blocking);#define irqchip_in_kernel (k) (!!((k)->arch.vgic.in_kernel))#define vgic_initialized (k) ((k)->arch.vgic.initialized)
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 43/89 KVM: arm64: Add the {flush,sync}_vgic_state() primitives
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
Rather than blindly copying the vGIC state to/from the host at EL2, introduce a couple of helpers to copy only what is needed and to sanitise untrusted data passed by the host kernel.
--- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 50 +++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 7 deletions(-)
arch/arm64/kvm/hyp/nvhe/hyp-main.c +43 -7
@@ -18,10 +18,51 @@#include <nvhe/pkvm.h> #include <nvhe/trap_handler.h> +#include <linux/irqchip/arm-gic-v3.h> + DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);void __kvm_hyp_host_forward_smc (struct kvm_cpu_context *host_ctxt); +static void flush_vgic_state (struct kvm_vcpu *host_vcpu, +struct kvm_vcpu *shadow_vcpu) +{ +struct vgic_v3_cpu_if *host_cpu_if, *shadow_cpu_if; +unsigned int used_lrs, max_lrs, i; + + host_cpu_if = &host_vcpu->arch.vgic_cpu.vgic_v3; + shadow_cpu_if = &shadow_vcpu->arch.vgic_cpu.vgic_v3; + + max_lrs = (read_gicreg (ICH_VTR_EL2 ) &0xf ) +1 ; + used_lrs =READ_ONCE (host_cpu_if->used_lrs); + used_lrs =min (used_lrs, max_lrs); + + shadow_cpu_if->vgic_hcr =READ_ONCE (host_cpu_if->vgic_hcr); +/* Should be a one-off */ + shadow_cpu_if->vgic_sre = (ICC_SRE_EL1_DIB | +ICC_SRE_EL1_DFB | +ICC_SRE_EL1_SRE ); + shadow_cpu_if->used_lrs = used_lrs; + +for (i =0 ; i < used_lrs; i++) + shadow_cpu_if->vgic_lr[i] =READ_ONCE (host_cpu_if->vgic_lr[i]); +} + +static void sync_vgic_state (struct kvm_vcpu *host_vcpu, +struct kvm_vcpu *shadow_vcpu) +{ +struct vgic_v3_cpu_if *host_cpu_if, *shadow_cpu_if; +unsigned int i; + + host_cpu_if = &host_vcpu->arch.vgic_cpu.vgic_v3; + shadow_cpu_if = &shadow_vcpu->arch.vgic_cpu.vgic_v3; + +WRITE_ONCE (host_cpu_if->vgic_hcr, shadow_cpu_if->vgic_hcr); + +for (i =0 ; i < shadow_cpu_if->used_lrs; i++) +WRITE_ONCE (host_cpu_if->vgic_lr[i], shadow_cpu_if->vgic_lr[i]); +} +static void flush_shadow_state (struct kvm_shadow_vcpu_state *shadow_state) {struct kvm_vcpu *shadow_vcpu = &shadow_state->shadow_vcpu;
@@ -43,16 +84,13 @@ static void flush_shadow_state(struct kvm_shadow_vcpu_state *shadow_state) shadow_vcpu->arch.vsesr_el2 = host_vcpu->arch.vsesr_el2; - shadow_vcpu->arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3; +flush_vgic_state (host_vcpu, shadow_vcpu); }static void sync_shadow_state (struct kvm_shadow_vcpu_state *shadow_state) {struct kvm_vcpu *shadow_vcpu = &shadow_state->shadow_vcpu;struct kvm_vcpu *host_vcpu = shadow_state->host_vcpu; -struct vgic_v3_cpu_if *shadow_cpu_if = &shadow_vcpu->arch.vgic_cpu.vgic_v3; -struct vgic_v3_cpu_if *host_cpu_if = &host_vcpu->arch.vgic_cpu.vgic_v3; -unsigned int i; host_vcpu->arch.ctxt = shadow_vcpu->arch.ctxt;
@@ -63,9 +101,7 @@ static void sync_shadow_state(struct kvm_shadow_vcpu_state *shadow_state) host_vcpu->arch.flags = shadow_vcpu->arch.flags; - host_cpu_if->vgic_hcr = shadow_cpu_if->vgic_hcr; -for (i =0 ; i < shadow_cpu_if->used_lrs; ++i) - host_cpu_if->vgic_lr[i] = shadow_cpu_if->vgic_lr[i]; +sync_vgic_state (host_vcpu, shadow_vcpu); }static void handle___kvm_vcpu_run (struct kvm_cpu_context *host_ctxt)
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 44/89 KVM: arm64: Introduce predicates to check for protected state
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
In order to determine whether or not a VM or (shadow) vCPU are protected, introduce a helper function to query this state. For now, these will always return 'false' as the underlying field is never configured.
--- arch/arm64/include/asm/kvm_host.h | 6 ++---- arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 13 +++++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-)
arch/arm64/include/asm/kvm_host.h +2 -4
@@ -164,6 +164,7 @@ struct kvm_pinned_page { };struct kvm_protected_vm { +bool enabled;unsigned int shadow_handle;struct mutex shadow_lock;struct kvm_hyp_memcache teardown_mc;
@@ -895,10 +896,7 @@ int kvm_set_ipa_limit(void);#define __KVM_HAVE_ARCH_VM_ALLOCstruct kvm *kvm_arch_alloc_vm (void ); -static inline bool kvm_vm_is_protected (struct kvm *kvm) -{ -return false; -} +#define kvm_vm_is_protected (kvm) ((kvm)->arch.pkvm.enabled)void kvm_init_protected_traps (struct kvm_vcpu *vcpu);
arch/arm64/kvm/hyp/include/nvhe/pkvm.h +13 -0
@@ -58,6 +58,19 @@ static inline struct kvm_shadow_vm *get_shadow_vm(struct kvm_vcpu *shadow_vcpu)return get_shadow_state (shadow_vcpu)->shadow_vm; } +static inline bool shadow_state_is_protected (struct kvm_shadow_vcpu_state *shadow_state) +{ +return shadow_state->shadow_vm->kvm.arch.pkvm.enabled; +} + +static inline bool vcpu_is_protected (struct kvm_vcpu *vcpu) +{ +if (!is_protected_kvm_enabled ()) +return false; + +return shadow_state_is_protected (get_shadow_state (vcpu)); +} +void hyp_shadow_table_init (void *tbl);int __pkvm_init_shadow (struct kvm *kvm,unsigned long shadow_hva,size_t shadow_size,unsigned long pgd_hva);
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 45/89 KVM: arm64: Add the {flush,sync}_timer_state() primitives
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
In preparation for save/restore of the timer state at EL2 for protected VMs, introduce a couple of sync/flush primitives for the architected timer, in much the same way as we have for the GIC.
--- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+)
arch/arm64/kvm/hyp/nvhe/hyp-main.c +34 -0
@@ -63,6 +63,38 @@ static void sync_vgic_state(struct kvm_vcpu *host_vcpu,WRITE_ONCE (host_cpu_if->vgic_lr[i], shadow_cpu_if->vgic_lr[i]); } +static void flush_timer_state (struct kvm_shadow_vcpu_state *shadow_state) +{ +struct kvm_vcpu *shadow_vcpu = &shadow_state->shadow_vcpu; + +if (!shadow_state_is_protected (shadow_state)) +return ; + +/* +* A shadow vcpu has no offset, and sees vtime == ptime. The +* ptimer is fully emulated by EL1 and cannot be trusted. +*/ +write_sysreg (0 , cntvoff_el2); +isb (); +write_sysreg_el0 (__vcpu_sys_reg (shadow_vcpu,CNTV_CVAL_EL0 ),SYS_CNTV_CVAL ); +write_sysreg_el0 (__vcpu_sys_reg (shadow_vcpu,CNTV_CTL_EL0 ),SYS_CNTV_CTL ); +} + +static void sync_timer_state (struct kvm_shadow_vcpu_state *shadow_state) +{ +struct kvm_vcpu *shadow_vcpu = &shadow_state->shadow_vcpu; + +if (!shadow_state_is_protected (shadow_state)) +return ; + +/* +* Preserve the vtimer state so that it is always correct, +* even if the host tries to make a mess. +*/ +__vcpu_sys_reg (shadow_vcpu,CNTV_CVAL_EL0 ) =read_sysreg_el0 (SYS_CNTV_CVAL ); +__vcpu_sys_reg (shadow_vcpu,CNTV_CTL_EL0 ) =read_sysreg_el0 (SYS_CNTV_CTL ); +} +static void flush_shadow_state (struct kvm_shadow_vcpu_state *shadow_state) {struct kvm_vcpu *shadow_vcpu = &shadow_state->shadow_vcpu;
@@ -85,6 +117,7 @@ static void flush_shadow_state(struct kvm_shadow_vcpu_state *shadow_state) shadow_vcpu->arch.vsesr_el2 = host_vcpu->arch.vsesr_el2;flush_vgic_state (host_vcpu, shadow_vcpu); +flush_timer_state (shadow_state); }static void sync_shadow_state (struct kvm_shadow_vcpu_state *shadow_state)
@@ -102,6 +135,7 @@ static void sync_shadow_state(struct kvm_shadow_vcpu_state *shadow_state) host_vcpu->arch.flags = shadow_vcpu->arch.flags;sync_vgic_state (host_vcpu, shadow_vcpu); +sync_timer_state (shadow_state); }static void handle___kvm_vcpu_run (struct kvm_cpu_context *host_ctxt)
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 46/89 KVM: arm64: Introduce the pkvm_vcpu_{load,put} hypercalls
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
Rather than look-up the shadow vCPU on every run hypercall at EL2, introduce a per-CPU loaded_shadow_state' which is updated by a pair of load/put hypercalls that are called directly from kvm_arch_vcpu_{load,put}() when pKVM is enabled.
--- arch/arm64/include/asm/kvm_asm.h | 2 + arch/arm64/kvm/arm.c | 14 ++++++ arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 7 +++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 65 ++++++++++++++++++-------- arch/arm64/kvm/hyp/nvhe/pkvm.c | 28 +++++++++++ arch/arm64/kvm/vgic/vgic-v3.c | 6 ++- 6 files changed, 100 insertions(+), 22 deletions(-)
arch/arm64/include/asm/kvm_asm.h +2 -0
@@ -78,6 +78,8 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___vgic_v3_restore_vmcr_aprs, __KVM_HOST_SMCCC_FUNC___pkvm_init_shadow, __KVM_HOST_SMCCC_FUNC___pkvm_teardown_shadow, + __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_load, + __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_put, };#define DECLARE_KVM_VHE_SYM (sym) extern char sym[]
arch/arm64/kvm/arm.c +14 -0
@@ -429,12 +429,26 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)vcpu_ptrauth_disable (vcpu);kvm_arch_vcpu_load_debug_state_flags (vcpu); +if (is_protected_kvm_enabled ()) { +kvm_call_hyp_nvhe (__pkvm_vcpu_load, + vcpu->kvm->arch.pkvm.shadow_handle, + vcpu->vcpu_idx, vcpu->arch.hcr_el2); +kvm_call_hyp (__vgic_v3_restore_vmcr_aprs, + &vcpu->arch.vgic_cpu.vgic_v3); + } +if (!cpumask_test_cpu (smp_processor_id (), vcpu->kvm->arch.supported_cpus))vcpu_set_on_unsupported_cpu (vcpu); }void kvm_arch_vcpu_put (struct kvm_vcpu *vcpu) { +if (is_protected_kvm_enabled ()) { +kvm_call_hyp (__vgic_v3_save_vmcr_aprs, + &vcpu->arch.vgic_cpu.vgic_v3); +kvm_call_hyp_nvhe (__pkvm_vcpu_put); + } +kvm_arch_vcpu_put_debug_state_flags (vcpu);kvm_arch_vcpu_put_fp (vcpu);if (has_vhe ())
arch/arm64/kvm/hyp/include/nvhe/pkvm.h +7 -0
@@ -24,6 +24,12 @@ struct kvm_shadow_vcpu_state {/* A pointer to the shadow vm. */ struct kvm_shadow_vm *shadow_vm; + +/* +* Points to the per-cpu pointer of the cpu where it's loaded, or NULL +* if not loaded. +*/ +struct kvm_shadow_vcpu_state **loaded_shadow_state; };/*
@@ -79,6 +85,7 @@ int __pkvm_teardown_shadow(unsigned int shadow_handle);struct kvm_shadow_vcpu_state *pkvm_load_shadow_vcpu_state (unsigned int shadow_handle,unsigned int vcpu_idx);void pkvm_put_shadow_vcpu_state (struct kvm_shadow_vcpu_state *shadow_state); +struct kvm_shadow_vcpu_state *pkvm_loaded_shadow_vcpu_state (void );u64 pvm_read_id_reg (const struct kvm_vcpu *vcpu,u32 id);bool kvm_handle_pvm_sysreg (struct kvm_vcpu *vcpu,u64 *exit_code);
arch/arm64/kvm/hyp/nvhe/hyp-main.c +45 -20
91 lines not on this page · raw message
arch/arm64/kvm/hyp/nvhe/pkvm.c +28 -0
@@ -17,6 +17,12 @@ unsigned long __icache_flags;/* Used by kvm_get_vttbr(). */ unsigned int kvm_arm_vmid_bits; +/* +* The shadow state for the currently loaded vcpu. Used only when protected KVM +* is enabled for both protected and non-protected VMs. +*/ +static DEFINE_PER_CPU(struct kvm_shadow_vcpu_state *, loaded_shadow_state); +/* * Set trap register values based on features in ID_AA64PFR0. */
@@ -252,15 +258,30 @@ pkvm_load_shadow_vcpu_state(unsigned int shadow_handle, unsigned int vcpu_idx)struct kvm_shadow_vcpu_state *shadow_state =NULL ;struct kvm_shadow_vm *vm; +/* Cannot load a new vcpu without putting the old one first. */ +if (__this_cpu_read (loaded_shadow_state)) +return NULL ; +hyp_spin_lock (&shadow_lock); vm =find_shadow_by_handle (shadow_handle);if (!vm || vm->kvm.created_vcpus <= vcpu_idx) goto unlock; shadow_state = &vm->shadow_vcpu_states[vcpu_idx]; + +/* Ensure vcpu isn't loaded on more than one cpu simultaneously. */ +if (unlikely (shadow_state->loaded_shadow_state)) { + shadow_state =NULL ; + goto unlock; + } + shadow_state->loaded_shadow_state =this_cpu_ptr (&loaded_shadow_state); +hyp_page_ref_inc (hyp_virt_to_page (vm)); unlock:hyp_spin_unlock (&shadow_lock); + +__this_cpu_write (loaded_shadow_state, shadow_state); +return shadow_state; }
@@ -269,10 +290,17 @@ void pkvm_put_shadow_vcpu_state(struct kvm_shadow_vcpu_state *shadow_state)struct kvm_shadow_vm *vm = shadow_state->shadow_vm;hyp_spin_lock (&shadow_lock); + shadow_state->loaded_shadow_state =NULL ; +__this_cpu_write (loaded_shadow_state,NULL );hyp_page_ref_dec (hyp_virt_to_page (vm));hyp_spin_unlock (&shadow_lock); } +struct kvm_shadow_vcpu_state *pkvm_loaded_shadow_vcpu_state (void ) +{ +return __this_cpu_read (loaded_shadow_state); +} +static void unpin_host_vcpus (struct kvm_shadow_vcpu_state *shadow_vcpu_states,unsigned int nr_vcpus) {
arch/arm64/kvm/vgic/vgic-v3.c +4 -2
@@ -720,7 +720,8 @@ void vgic_v3_load(struct kvm_vcpu *vcpu) {struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; -kvm_call_hyp (__vgic_v3_restore_vmcr_aprs, cpu_if); +if (likely (!is_protected_kvm_enabled ())) +kvm_call_hyp (__vgic_v3_restore_vmcr_aprs, cpu_if);if (has_vhe ())__vgic_v3_activate_traps (cpu_if);
@@ -734,7 +735,8 @@ void vgic_v3_put(struct kvm_vcpu *vcpu, bool blocking)WARN_ON (vgic_v4_put (vcpu, blocking)); -kvm_call_hyp (__vgic_v3_save_vmcr_aprs, cpu_if); +if (likely (!is_protected_kvm_enabled ())) +kvm_call_hyp (__vgic_v3_save_vmcr_aprs, cpu_if);if (has_vhe ())__vgic_v3_deactivate_traps (cpu_if);
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 47/89 KVM: arm64: Add current vcpu and shadow_state lookup primitive
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
In order to be able to safely manipulate the loaded state, add a helper that always return the vcpu as mapped in the EL2 S1 address space as well as the pointer to the shadow state.
In case of failure, both pointers are returned as NULL values.
For non-protected setups, state is always NULL, and vcpu the EL2 mapping of the input value.
handle___kvm_vcpu_run() is converted to this helper.
--- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 41 +++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 6 deletions(-)
arch/arm64/kvm/hyp/nvhe/hyp-main.c +35 -6
@@ -177,22 +177,51 @@ static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt) } } +static struct kvm_vcpu *__get_current_vcpu (struct kvm_vcpu *vcpu, +struct kvm_shadow_vcpu_state **state) +{ +struct kvm_shadow_vcpu_state *sstate =NULL ; + + vcpu =kern_hyp_va (vcpu); + +if (unlikely (is_protected_kvm_enabled ())) { + sstate =pkvm_loaded_shadow_vcpu_state (); +if (!sstate || vcpu != sstate->host_vcpu) { + sstate =NULL ; + vcpu =NULL ; + } + } + + *state = sstate; +return vcpu; +} + +#define get_current_vcpu (ctxt, regnr, statepp) \ + ({ \ + DECLARE_REG(struct kvm_vcpu *, __vcpu, ctxt, regnr); \ + __get_current_vcpu(__vcpu, statepp); \ + }) +static void handle___kvm_vcpu_run (struct kvm_cpu_context *host_ctxt) { - DECLARE_REG(struct kvm_vcpu *, host_vcpu, host_ctxt, 1); +struct kvm_shadow_vcpu_state *shadow_state; +struct kvm_vcpu *vcpu;int ret; -if (unlikely (is_protected_kvm_enabled ())) { -struct kvm_shadow_vcpu_state *shadow_state =pkvm_loaded_shadow_vcpu_state (); -struct kvm_vcpu *shadow_vcpu = &shadow_state->shadow_vcpu; + vcpu =get_current_vcpu (host_ctxt,1 , &shadow_state); +if (!vcpu) { +cpu_reg (host_ctxt,1 ) = -EINVAL ; +return ; + } +if (unlikely (shadow_state)) {flush_shadow_state (shadow_state); - ret =__kvm_vcpu_run (shadow_vcpu); + ret =__kvm_vcpu_run (&shadow_state->shadow_vcpu);sync_shadow_state (shadow_state); }else { - ret =__kvm_vcpu_run (kern_hyp_va (host_vcpu)); + ret =__kvm_vcpu_run (vcpu); }cpu_reg (host_ctxt,1 ) = ret;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 48/89 KVM: arm64: Skip __kvm_adjust_pc() for protected vcpus
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
Prevent the host from issuing arbitrary PC adjustments for protected vCPUs.
--- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
arch/arm64/kvm/hyp/nvhe/hyp-main.c +15 -2
@@ -275,9 +275,22 @@ static void handle___pkvm_host_map_guest(struct kvm_cpu_context *host_ctxt)static void handle___kvm_adjust_pc (struct kvm_cpu_context *host_ctxt) { - DECLARE_REG(struct kvm_vcpu *, vcpu, host_ctxt, 1); +struct kvm_shadow_vcpu_state *shadow_state; +struct kvm_vcpu *vcpu; + + vcpu =get_current_vcpu (host_ctxt,1 , &shadow_state); +if (!vcpu) +return ; + +if (shadow_state) { +/* This only applies to non-protected VMs */ +if (shadow_state_is_protected (shadow_state)) +return ; + + vcpu = &shadow_state->shadow_vcpu; + } -__kvm_adjust_pc (kern_hyp_va (vcpu)); +__kvm_adjust_pc (vcpu); }static void handle___kvm_flush_vm_context (struct kvm_cpu_context *host_ctxt)
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 49/89 KVM: arm64: Add hyp per_cpu variable to track current physical cpu number
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Hyp cannot trust the equivalent variable at the host.
--- arch/arm64/include/asm/kvm_hyp.h | 3 +++ arch/arm64/kvm/arm.c | 4 ++++ arch/arm64/kvm/hyp/nvhe/hyp-smp.c | 2 ++ 3 files changed, 9 insertions(+)
arch/arm64/include/asm/kvm_hyp.h +3 -0
@@ -15,6 +15,9 @@ DECLARE_PER_CPU(struct kvm_cpu_context, kvm_hyp_ctxt); DECLARE_PER_CPU(unsigned long, kvm_hyp_vector); DECLARE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params); +DECLARE_PER_CPU (int, hyp_cpu_number); + +#define hyp_smp_processor_id () (__this_cpu_read(hyp_cpu_number))#define read_sysreg_elx (r,nvh,vh) \ ({ \
arch/arm64/kvm/arm.c +4 -0
@@ -52,6 +52,7 @@ DECLARE_KVM_HYP_PER_CPU(unsigned long, kvm_hyp_vector); static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);DECLARE_KVM_NVHE_PER_CPU (struct kvm_nvhe_init_params , kvm_init_params); +DECLARE_KVM_NVHE_PER_CPU (int, hyp_cpu_number);static bool vgic_present;
@@ -1487,6 +1488,9 @@ static void cpu_prepare_hyp_mode(int cpu) {struct kvm_nvhe_init_params *params =per_cpu_ptr_nvhe_sym (kvm_init_params, cpu);unsigned long tcr; +int *hyp_cpu_number_ptr =per_cpu_ptr_nvhe_sym (hyp_cpu_number, cpu); + + *hyp_cpu_number_ptr = cpu;/* * Calculate the raw per-cpu offset without a translation from the
arch/arm64/kvm/hyp/nvhe/hyp-smp.c +2 -0
@@ -8,6 +8,8 @@#include <asm/kvm_hyp.h> #include <asm/kvm_mmu.h> +DEFINE_PER_CPU (int, hyp_cpu_number); +/* * nVHE copy of data structures tracking available CPU cores. * Only entries for CPUs that were online at KVM init are populated.
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 50/89 KVM: arm64: Ensure that TLBs and I-cache are private to each vcpu
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Guarantee that both TLBs and I-cache are private to each vcpu. Flush the CPU context if a different vcpu from the same vm is loaded on the same physical CPU.
--- arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 9 +++++-- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 17 ++++++++++++- arch/arm64/kvm/hyp/nvhe/pkvm.c | 34 ++++++++++++++++++++++++-- arch/arm64/kvm/pkvm.c | 20 +++++++++++---- 4 files changed, 70 insertions(+), 10 deletions(-)
arch/arm64/kvm/hyp/include/nvhe/pkvm.h +7 -2
@@ -45,6 +45,9 @@ struct kvm_shadow_vm {/* The total size of the donated shadow area. */ size_t shadow_area_size; +/* The total size of the donated area for last_ran. */ +size_t last_ran_size; +struct kvm_pgtable pgt;struct kvm_pgtable_mm_ops mm_ops;struct hyp_pool pool;
@@ -78,8 +81,10 @@ static inline bool vcpu_is_protected(struct kvm_vcpu *vcpu) }void hyp_shadow_table_init (void *tbl); -int __pkvm_init_shadow (struct kvm *kvm,unsigned long shadow_hva, -size_t shadow_size,unsigned long pgd_hva); +int __pkvm_init_shadow (struct kvm *kvm, +unsigned long shadow_hva,size_t shadow_size, +unsigned long pgd_hva, +unsigned long last_ran_hva,size_t last_ran_size);int __pkvm_teardown_shadow (unsigned int shadow_handle); struct kvm_shadow_vcpu_state *
arch/arm64/kvm/hyp/nvhe/hyp-main.c +16 -1
@@ -145,6 +145,7 @@ static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt)DECLARE_REG (u64, hcr_el2, host_ctxt,3 );struct kvm_shadow_vcpu_state *shadow_state;struct kvm_vcpu *shadow_vcpu; +int *last_ran;if (!is_protected_kvm_enabled ())return ;
@@ -155,6 +156,17 @@ static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt) shadow_vcpu = &shadow_state->shadow_vcpu; +/* +* Guarantee that both TLBs and I-cache are private to each vcpu. If a +* vcpu from the same VM has previously run on the same physical CPU, +* nuke the relevant contexts. +*/ + last_ran = &shadow_vcpu->arch.hw_mmu->last_vcpu_ran[hyp_smp_processor_id ()]; +if (*last_ran != shadow_vcpu->vcpu_id) { +__kvm_flush_cpu_context (shadow_vcpu->arch.hw_mmu); + *last_ran = shadow_vcpu->vcpu_id; + } +if (shadow_state_is_protected (shadow_state)) {/* Propagate WFx trapping flags, trap ptrauth */ shadow_vcpu->arch.hcr_el2 &= ~(HCR_TWE | HCR_TWI |
@@ -436,9 +448,12 @@ static void handle___pkvm_init_shadow(struct kvm_cpu_context *host_ctxt) DECLARE_REG(unsigned long, host_shadow_va, host_ctxt, 2);DECLARE_REG (size_t, shadow_size, host_ctxt,3 ); DECLARE_REG(unsigned long, pgd, host_ctxt, 4); + DECLARE_REG(unsigned long, last_ran, host_ctxt, 5); +DECLARE_REG (size_t, last_ran_size, host_ctxt,6 );cpu_reg (host_ctxt,1 ) =__pkvm_init_shadow (host_kvm, host_shadow_va, - shadow_size, pgd); + shadow_size, pgd, + last_ran, last_ran_size); }static void handle___pkvm_teardown_shadow (struct kvm_cpu_context *host_ctxt)
arch/arm64/kvm/hyp/nvhe/pkvm.c +32 -2
@@ -338,6 +338,8 @@ static int set_host_vcpus(struct kvm_shadow_vcpu_state *shadow_vcpu_states,static int init_shadow_structs (struct kvm *kvm,struct kvm_shadow_vm *vm,struct kvm_vcpu **vcpu_array, +int *last_ran, +size_t last_ran_size,unsigned int nr_vcpus) {int i;
@@ -345,6 +347,9 @@ static int init_shadow_structs(struct kvm *kvm, struct kvm_shadow_vm *vm, vm->host_kvm = kvm; vm->kvm.created_vcpus = nr_vcpus; vm->kvm.arch.vtcr = host_kvm.arch.vtcr; + vm->kvm.arch.mmu.last_vcpu_ran = last_ran; + vm->last_ran_size = last_ran_size; +memset (vm->kvm.arch.mmu.last_vcpu_ran,-1 ,sizeof (int ) * hyp_nr_cpus);for (i =0 ; i < nr_vcpus; i++) {struct kvm_shadow_vcpu_state *shadow_vcpu_state = &vm->shadow_vcpu_states[i];
@@ -471,6 +476,15 @@ static int check_shadow_size(unsigned int nr_vcpus, size_t shadow_size)return 0 ; } +/* +* Check whether the size of the area donated by the host is sufficient for +* tracking the last vcpu that has run a physical cpu on this vm. +*/ +static int check_last_ran_size (size_t size) +{ +return size >= (hyp_nr_cpus *sizeof (int )) ?0 : -ENOMEM ; +} +static void *map_donated_memory_noclear (unsigned long host_va,size_t size) {void *va = (void *)kern_hyp_va (host_va);
@@ -530,6 +544,10 @@ static void unmap_donated_memory_noclear(void *va, size_t size)* pgd_hva: The host va of the area being donated for the stage-2 PGD for * the VM. Must be page aligned. Its size is implied by the VM's * VTCR. +* last_ran_hva: The host va of the area being donated for hyp to use to track +* the most recent physical cpu on which each vcpu has run. +* last_ran_size: The size of the area being donated at last_ran_hva. +* Must be a multiple of the page size. * Note: An array to the host KVM VCPUs (host VA) is passed via the pgd, as to * not to be dependent on how the VCPU's are layed out in struct kvm. *
@@ -537,9 +555,11 @@ static void unmap_donated_memory_noclear(void *va, size_t size)* negative error code on failure. */ int __pkvm_init_shadow (struct kvm *kvm,unsigned long shadow_hva, -size_t shadow_size,unsigned long pgd_hva) +size_t shadow_size,unsigned long pgd_hva, +unsigned long last_ran_hva,size_t last_ran_size) {struct kvm_shadow_vm *vm =NULL ; +void *last_ran =NULL ;unsigned int nr_vcpus;size_t pgd_size =0 ;void *pgd =NULL ;
@@ -555,12 +575,20 @@ int __pkvm_init_shadow(struct kvm *kvm, unsigned long shadow_hva,if (ret) goto err_unpin_kvm; + ret =check_last_ran_size (last_ran_size); +if (ret) + goto err_unpin_kvm; + ret = -ENOMEM ; vm =map_donated_memory (shadow_hva, shadow_size);if (!vm) goto err_remove_mappings; + last_ran =map_donated_memory (last_ran_hva, last_ran_size); +if (!last_ran) + goto err_remove_mappings; + pgd_size =kvm_pgtable_stage2_pgd_size (host_kvm.arch.vtcr); pgd =map_donated_memory_noclear (pgd_hva, pgd_size);if (!pgd)
@@ -570,7 +598,7 @@ int __pkvm_init_shadow(struct kvm *kvm, unsigned long shadow_hva,if (ret) goto err_remove_mappings; - ret =init_shadow_structs (kvm, vm, pgd, nr_vcpus); + ret =init_shadow_structs (kvm, vm, pgd, last_ran, last_ran_size, nr_vcpus);if (ret <0 ) goto err_unpin_host_vcpus;
@@ -595,6 +623,7 @@ int __pkvm_init_shadow(struct kvm *kvm, unsigned long shadow_hva,unpin_host_vcpus (vm->shadow_vcpu_states, nr_vcpus); err_remove_mappings:unmap_donated_memory (vm, shadow_size); +unmap_donated_memory (last_ran, last_ran_size);unmap_donated_memory_noclear (pgd, pgd_size); err_unpin_kvm:hyp_unpin_shared_mem (kvm, kvm +1 );
@@ -636,6 +665,7 @@ int __pkvm_teardown_shadow(unsigned int shadow_handle) shadow_size = vm->shadow_area_size;hyp_unpin_shared_mem (vm->host_kvm, vm->host_kvm +1 ); +unmap_donated_memory (vm->kvm.arch.mmu.last_vcpu_ran, vm->last_ran_size);memset (vm,0 , shadow_size);for (addr = vm; addr < (void *)vm + shadow_size; addr +=PAGE_SIZE )push_hyp_memcache (mc, addr, hyp_virt_to_phys);
arch/arm64/kvm/pkvm.c +15 -5
@@ -111,8 +111,8 @@ static int __kvm_shadow_create(struct kvm *kvm) {struct kvm_vcpu *vcpu, **vcpu_array;unsigned int shadow_handle; -size_t pgd_sz, shadow_sz; -void *pgd, *shadow_addr; +size_t pgd_sz, shadow_sz, last_ran_sz; +void *pgd, *shadow_addr, *last_ran;unsigned long idx;int ret;
@@ -138,6 +138,14 @@ static int __kvm_shadow_create(struct kvm *kvm) goto free_pgd; } +/* Allocate memory to donate to hyp for tracking mmu->last_vcpu_ran. */ + last_ran_sz =PAGE_ALIGN (num_possible_cpus () *sizeof (int )); + last_ran =alloc_pages_exact (last_ran_sz,GFP_KERNEL_ACCOUNT ); +if (!last_ran) { + ret = -ENOMEM ; + goto free_shadow; + } +/* Stash the vcpu pointers into the PGD */ BUILD_BUG_ON (KVM_MAX_VCPUS > (PAGE_SIZE /sizeof (u64))); vcpu_array = pgd;
@@ -145,7 +153,7 @@ static int __kvm_shadow_create(struct kvm *kvm)/* Indexing of the vcpus to be sequential starting at 0. */ if (WARN_ON (vcpu->vcpu_idx != idx)) { ret = -EINVAL ; - goto free_shadow; + goto free_last_ran; } vcpu_array[idx] = vcpu;
@@ -153,9 +161,9 @@ static int __kvm_shadow_create(struct kvm *kvm)/* Donate the shadow memory to hyp and let hyp initialize it. */ ret =kvm_call_hyp_nvhe (__pkvm_init_shadow, kvm, shadow_addr, shadow_sz, - pgd); + pgd, last_ran, last_ran_sz);if (ret <0 ) - goto free_shadow; + goto free_last_ran; shadow_handle = ret;
@@ -163,6 +171,8 @@ static int __kvm_shadow_create(struct kvm *kvm) kvm->arch.pkvm.shadow_handle = shadow_handle;return 0 ; +free_last_ran: +free_pages_exact (last_ran, last_ran_sz); free_shadow:free_pages_exact (shadow_addr, shadow_sz); free_pgd:
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 51/89 KVM: arm64: Introduce per-EC entry/exit handlers
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
Introduce per-EC entry/exit handlers at EL2 and provide initial implementations to manage the 'flags' and fault information registers.
--- arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 3 + arch/arm64/kvm/hyp/nvhe/hyp-main.c | 99 +++++++++++++++++++++++--- 2 files changed, 94 insertions(+), 8 deletions(-)
arch/arm64/kvm/hyp/include/nvhe/pkvm.h +3 -0
@@ -25,6 +25,9 @@ struct kvm_shadow_vcpu_state {/* A pointer to the shadow vm. */ struct kvm_shadow_vm *shadow_vm; +/* Tracks exit code for the protected guest. */ +u32 exit_code; +/* * Points to the per-cpu pointer of the cpu where it's loaded, or NULL * if not loaded.
arch/arm64/kvm/hyp/nvhe/hyp-main.c +91 -8
142 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 52/89 KVM: arm64: Introduce lazy-ish state sync for non-protected VMs
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
Rather than blindly copying the register state between the shadow and host vCPU structures, abstract this code into some helpers which are called only for non-protected VMs running under pKVM. To faciliate host accesses to guest registers within a get/put sequence, introduce a new 'sync_state' hypercall to provide access to the registers of a non-protected VM when handling traps.
--- arch/arm64/include/asm/kvm_asm.h | 1 + arch/arm64/include/asm/kvm_host.h | 1 + arch/arm64/kvm/arm.c | 7 ++++ arch/arm64/kvm/handle_exit.c | 22 ++++++++++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 65 +++++++++++++++++++++++++++++- 5 files changed, 95 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kvm_asm.h +1 -0
@@ -80,6 +80,7 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___pkvm_teardown_shadow, __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_load, __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_put, + __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_sync_state, };#define DECLARE_KVM_VHE_SYM (sym) extern char sym[]
arch/arm64/include/asm/kvm_host.h +1 -0
@@ -512,6 +512,7 @@ struct kvm_vcpu_arch {#define KVM_ARM64_DEBUG_STATE_SAVE_TRBE (1 << 13)/* Save TRBE context if active */ #define KVM_ARM64_FP_FOREIGN_FPSTATE (1 << 14)#define KVM_ARM64_ON_UNSUPPORTED_CPU (1 << 15)/* Physical CPU not in supported_cpus */ +#define KVM_ARM64_PKVM_STATE_DIRTY (1 << 16)#define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \ KVM_GUESTDBG_USE_SW_BP | \
arch/arm64/kvm/arm.c +7 -0
@@ -448,6 +448,10 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)kvm_call_hyp (__vgic_v3_save_vmcr_aprs, &vcpu->arch.vgic_cpu.vgic_v3);kvm_call_hyp_nvhe (__pkvm_vcpu_put); + +/* __pkvm_vcpu_put implies a sync of the state */ +if (!kvm_vm_is_protected (vcpu->kvm)) + vcpu->arch.flags |=KVM_ARM64_PKVM_STATE_DIRTY ; }kvm_arch_vcpu_put_debug_state_flags (vcpu);
@@ -575,6 +579,9 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)return ret;if (is_protected_kvm_enabled ()) { +/* Start with the vcpu in a dirty state */ +if (!kvm_vm_is_protected (vcpu->kvm)) + vcpu->arch.flags |=KVM_ARM64_PKVM_STATE_DIRTY ; ret =kvm_shadow_create (kvm);if (ret)return ret;
arch/arm64/kvm/handle_exit.c +22 -0
@@ -203,6 +203,21 @@ static int handle_trap_exceptions(struct kvm_vcpu *vcpu) {int handled; +/* +* If we run a non-protected VM when protection is enabled +* system-wide, resync the state from the hypervisor and mark +* it as dirty on the host side if it wasn't dirty already +* (which could happen if preemption has taken place). +*/ +if (is_protected_kvm_enabled () && !kvm_vm_is_protected (vcpu->kvm)) { +preempt_disable (); +if (!(vcpu->arch.flags &KVM_ARM64_PKVM_STATE_DIRTY )) { +kvm_call_hyp_nvhe (__pkvm_vcpu_sync_state); + vcpu->arch.flags |=KVM_ARM64_PKVM_STATE_DIRTY ; + } +preempt_enable (); + } +/* * See ARM ARM B1.14.1: "Hyp traps on instructions * that fail their condition code check"
@@ -270,6 +285,13 @@ int handle_exit(struct kvm_vcpu *vcpu, int exception_index)/* For exit types that need handling before we can be preempted */ void handle_exit_early (struct kvm_vcpu *vcpu,int exception_index) { +/* +* We just exited, so the state is clean from a hypervisor +* perspective. +*/ +if (is_protected_kvm_enabled ()) + vcpu->arch.flags &= ~KVM_ARM64_PKVM_STATE_DIRTY ; +if (ARM_SERROR_PENDING (exception_index)) {if (this_cpu_has_cap (ARM64_HAS_RAS_EXTN )) {u64 disr =kvm_vcpu_get_disr (vcpu);
arch/arm64/kvm/hyp/nvhe/hyp-main.c +64 -1
93 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 53/89 KVM: arm64: Lazy host FP save/restore
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
Implement lazy save/restore of the host FPSIMD register state at EL2. This allows us to save/restore guest FPSIMD registers without involving the host and means that we can avoid having to repopulate the shadow register state on every flush.
--- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 57 ++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 6 deletions(-)
arch/arm64/kvm/hyp/nvhe/hyp-main.c +51 -6
@@ -20,6 +20,14 @@#include <linux/irqchip/arm-gic-v3.h> +/* +* Host FPSIMD state. Written to when the guest accesses its own FPSIMD state, +* and read when the guest state is live and we need to switch back to the host. +* +* Only valid when the KVM_ARM64_FP_ENABLED flag is set in the shadow structure. +*/ +static DEFINE_PER_CPU(struct user_fpsimd_state, loaded_host_fpsimd_state); +DEFINE_PER_CPU (struct kvm_nvhe_init_params , kvm_init_params);void __kvm_hyp_host_forward_smc (struct kvm_cpu_context *host_ctxt);
@@ -195,10 +203,8 @@ static void flush_shadow_state(struct kvm_shadow_vcpu_state *shadow_state) shadow_vcpu->arch.hcr_el2 = host_vcpu->arch.hcr_el2; shadow_vcpu->arch.mdcr_el2 = host_vcpu->arch.mdcr_el2; - shadow_vcpu->arch.cptr_el2 = host_vcpu->arch.cptr_el2; shadow_vcpu->arch.debug_ptr =kern_hyp_va (host_vcpu->arch.debug_ptr); - shadow_vcpu->arch.host_fpsimd_state = host_vcpu->arch.host_fpsimd_state; shadow_vcpu->arch.vsesr_el2 = host_vcpu->arch.vsesr_el2;
@@ -235,7 +241,6 @@ static void sync_shadow_state(struct kvm_shadow_vcpu_state *shadow_state, host_vcpu->arch.ctxt = shadow_vcpu->arch.ctxt; host_vcpu->arch.hcr_el2 = shadow_vcpu->arch.hcr_el2; - host_vcpu->arch.cptr_el2 = shadow_vcpu->arch.cptr_el2;sync_vgic_state (host_vcpu, shadow_vcpu);sync_timer_state (shadow_state);
@@ -262,6 +267,27 @@ static void sync_shadow_state(struct kvm_shadow_vcpu_state *shadow_state, shadow_state->exit_code = exit_reason; } +static void fpsimd_host_restore (void ) +{ +sysreg_clear_set (cptr_el2,CPTR_EL2_TZ |CPTR_EL2_TFP ,0 ); +isb (); + +if (unlikely (is_protected_kvm_enabled ())) { +struct kvm_shadow_vcpu_state *shadow_state =pkvm_loaded_shadow_vcpu_state (); +struct kvm_vcpu *shadow_vcpu = &shadow_state->shadow_vcpu; +struct user_fpsimd_state *host_fpsimd_state =this_cpu_ptr (&loaded_host_fpsimd_state); + +__fpsimd_save_state (&shadow_vcpu->arch.ctxt.fp_regs); +__fpsimd_restore_state (host_fpsimd_state); + + shadow_vcpu->arch.flags &= ~KVM_ARM64_FP_ENABLED ; + shadow_vcpu->arch.flags |=KVM_ARM64_FP_HOST ; + } + +if (system_supports_sve ()) +sve_cond_update_zcr_vq (ZCR_ELx_LEN_MASK,SYS_ZCR_EL2 ); +} +static void handle___pkvm_vcpu_load (struct kvm_cpu_context *host_ctxt) { DECLARE_REG(unsigned int, shadow_handle, host_ctxt, 1);
@@ -291,6 +317,9 @@ static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt) *last_ran = shadow_vcpu->vcpu_id; } + shadow_vcpu->arch.host_fpsimd_state =this_cpu_ptr (&loaded_host_fpsimd_state); + shadow_vcpu->arch.flags |=KVM_ARM64_FP_HOST ; +if (shadow_state_is_protected (shadow_state)) {/* Propagate WFx trapping flags, trap ptrauth */ shadow_vcpu->arch.hcr_el2 &= ~(HCR_TWE | HCR_TWI |
@@ -310,6 +339,10 @@ static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt)if (shadow_state) {struct kvm_vcpu *host_vcpu = shadow_state->host_vcpu; +struct kvm_vcpu *shadow_vcpu = &shadow_state->shadow_vcpu; + +if (shadow_vcpu->arch.flags &KVM_ARM64_FP_ENABLED ) +fpsimd_host_restore ();if (!shadow_state_is_protected (shadow_state) && !(READ_ONCE (host_vcpu->arch.flags) &KVM_ARM64_PKVM_STATE_DIRTY ))
@@ -377,6 +410,19 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt) ret =__kvm_vcpu_run (&shadow_state->shadow_vcpu);sync_shadow_state (shadow_state, ret); + +if (shadow_state->shadow_vcpu.arch.flags &KVM_ARM64_FP_ENABLED ) { +/* +* The guest has used the FP, trap all accesses +* from the host (both FP and SVE). +*/ +u64 reg =CPTR_EL2_TFP ; + +if (system_supports_sve ()) + reg |=CPTR_EL2_TZ ; + +sysreg_clear_set (cptr_el2,0 , reg); + } }else { ret =__kvm_vcpu_run (vcpu); }
@@ -707,10 +753,9 @@ void handle_trap(struct kvm_cpu_context *host_ctxt)case ESR_ELx_EC_SMC64:handle_host_smc (host_ctxt);break ; +case ESR_ELx_EC_FP_ASIMD:case ESR_ELx_EC_SVE: -sysreg_clear_set (cptr_el2,CPTR_EL2_TZ ,0 ); -isb (); -sve_cond_update_zcr_vq (ZCR_ELx_LEN_MASK,SYS_ZCR_EL2 ); +fpsimd_host_restore ();break ;case ESR_ELx_EC_IABT_LOW:case ESR_ELx_EC_DABT_LOW:
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 54/89 KVM: arm64: Reduce host/shadow vcpu state copying
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
When running with pKVM enabled, protected guests run with a fixed CPU configuration and therefore features such as hardware debug and SVE are unavailable and their state does not need to be copied from the host structures on each flush operation. Although non-protected guests do require the host and shadow structures to be kept in-sync with each other, we can defer writing back to the host to an explicit sync hypercall, rather than doing it after every vCPU run.
--- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-)
arch/arm64/kvm/hyp/nvhe/hyp-main.c +12 -11
@@ -196,17 +196,18 @@ static void flush_shadow_state(struct kvm_shadow_vcpu_state *shadow_state)if (host_flags &KVM_ARM64_PKVM_STATE_DIRTY )__flush_vcpu_state (shadow_state); - } - shadow_vcpu->arch.sve_state =kern_hyp_va (host_vcpu->arch.sve_state); - shadow_vcpu->arch.sve_max_vl = host_vcpu->arch.sve_max_vl; + shadow_vcpu->arch.sve_state =kern_hyp_va (host_vcpu->arch.sve_state); + shadow_vcpu->arch.sve_max_vl = host_vcpu->arch.sve_max_vl; - shadow_vcpu->arch.hcr_el2 = host_vcpu->arch.hcr_el2; - shadow_vcpu->arch.mdcr_el2 = host_vcpu->arch.mdcr_el2; + shadow_vcpu->arch.hcr_el2 =HCR_GUEST_FLAGS & ~(HCR_RW |HCR_TWI |HCR_TWE ); + shadow_vcpu->arch.hcr_el2 |=READ_ONCE (host_vcpu->arch.hcr_el2); - shadow_vcpu->arch.debug_ptr =kern_hyp_va (host_vcpu->arch.debug_ptr); + shadow_vcpu->arch.mdcr_el2 = host_vcpu->arch.mdcr_el2; + shadow_vcpu->arch.debug_ptr =kern_hyp_va (host_vcpu->arch.debug_ptr); + } - shadow_vcpu->arch.vsesr_el2 = host_vcpu->arch.vsesr_el2; + shadow_vcpu->arch.vsesr_el2 = host_vcpu->arch.vsesr_el2;flush_vgic_state (host_vcpu, shadow_vcpu);flush_timer_state (shadow_state);
@@ -238,10 +239,10 @@ static void sync_shadow_state(struct kvm_shadow_vcpu_state *shadow_state,unsigned long host_flags;u8 esr_ec; - host_vcpu->arch.ctxt = shadow_vcpu->arch.ctxt; - - host_vcpu->arch.hcr_el2 = shadow_vcpu->arch.hcr_el2; - +/* +* Don't sync the vcpu GPR/sysreg state after a run. Instead, +* leave it in the shadow until someone actually requires it. +*/ sync_vgic_state (host_vcpu, shadow_vcpu);sync_timer_state (shadow_state);
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 55/89 KVM: arm64: Do not pass the vcpu to __pkvm_host_map_guest()
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
__pkvm_host_map_guest() always applies to the loaded vcpu in hyp, and should not trust the host to provide the vcpu.
--- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 15 ++++----------- arch/arm64/kvm/mmu.c | 6 +++--- 2 files changed, 7 insertions(+), 14 deletions(-)
arch/arm64/kvm/hyp/nvhe/hyp-main.c +4 -11
@@ -445,20 +445,15 @@ static void handle___pkvm_host_map_guest(struct kvm_cpu_context *host_ctxt) {DECLARE_REG (u64, pfn, host_ctxt,1 );DECLARE_REG (u64, gfn, host_ctxt,2 ); - DECLARE_REG(struct kvm_vcpu *, host_vcpu, host_ctxt, 3); -struct kvm_shadow_vcpu_state *shadow_state; +struct kvm_vcpu *host_vcpu;struct kvm_vcpu *shadow_vcpu; -struct kvm *host_kvm; -unsigned int handle; +struct kvm_shadow_vcpu_state *shadow_state;int ret = -EINVAL ;if (!is_protected_kvm_enabled ()) goto out; - host_vcpu =kern_hyp_va (host_vcpu); - host_kvm =kern_hyp_va (host_vcpu->kvm); - handle = host_kvm->arch.pkvm.shadow_handle; - shadow_state =pkvm_load_shadow_vcpu_state (handle, host_vcpu->vcpu_idx); + shadow_state =pkvm_loaded_shadow_vcpu_state ();if (!shadow_state) goto out;
@@ -468,11 +463,9 @@ static void handle___pkvm_host_map_guest(struct kvm_cpu_context *host_ctxt)/* Topup shadow memcache with the host's */ ret =pkvm_refill_memcache (shadow_vcpu, host_vcpu);if (ret) - goto out_put_state; + goto out; ret =__pkvm_host_share_guest (pfn, gfn, shadow_vcpu); -out_put_state: -pkvm_put_shadow_vcpu_state (shadow_state); out:cpu_reg (host_ctxt,1 ) = ret; }
arch/arm64/kvm/mmu.c +3 -3
@@ -1143,9 +1143,9 @@ static int sanitise_mte_tags(struct kvm *kvm, kvm_pfn_t pfn,return 0 ; } -static int pkvm_host_map_guest (u64 pfn,u64 gfn,struct kvm_vcpu *vcpu) +static int pkvm_host_map_guest (u64 pfn,u64 gfn) { -int ret =kvm_call_hyp_nvhe (__pkvm_host_map_guest, pfn, gfn, vcpu); +int ret =kvm_call_hyp_nvhe (__pkvm_host_map_guest, pfn, gfn);/* * Getting -EPERM at this point implies that the pfn has already been
@@ -1211,7 +1211,7 @@ static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,write_lock (&kvm->mmu_lock); pfn =page_to_pfn (page); - ret =pkvm_host_map_guest (pfn, fault_ipa >>PAGE_SHIFT , vcpu); + ret =pkvm_host_map_guest (pfn, fault_ipa >>PAGE_SHIFT );if (ret) {if (ret == -EAGAIN ) ret =0 ;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 56/89 KVM: arm64: Check directly whether the vcpu is protected
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Simpler code and ensures we're always looking at hyp state.
--- arch/arm64/kvm/hyp/nvhe/switch.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
arch/arm64/kvm/hyp/nvhe/switch.c +2 -4
@@ -215,7 +215,7 @@ static const exit_handler_fn pvm_exit_handlers[] = {static const exit_handler_fn *kvm_get_exit_handler_array (struct kvm_vcpu *vcpu) { -if (unlikely (kvm_vm_is_protected (kern_hyp_va (vcpu->kvm)))) +if (unlikely (vcpu_is_protected (vcpu)))return pvm_exit_handlers;return hyp_exit_handlers;
@@ -234,9 +234,7 @@ static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu)*/ static void early_exit_filter (struct kvm_vcpu *vcpu,u64 *exit_code) { -struct kvm *kvm =kern_hyp_va (vcpu->kvm); - -if (kvm_vm_is_protected (kvm) &&vcpu_mode_is_32bit (vcpu)) { +if (unlikely (vcpu_is_protected (vcpu) &&vcpu_mode_is_32bit (vcpu))) {/* * As we have caught the guest red-handed, decide that it isn't * fit for purpose anymore by making the vcpu invalid. The VMM
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 57/89 KVM: arm64: Trap debug break and watch from guest
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Debug and trace are not currently supported for protected guests, so trap accesses to the related registers and emulate them as RAZ/WI.
--- arch/arm64/kvm/hyp/nvhe/pkvm.c | 2 +- arch/arm64/kvm/hyp/nvhe/sys_regs.c | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-)
arch/arm64/kvm/hyp/nvhe/pkvm.c +1 -1
@@ -108,7 +108,7 @@ static void pvm_init_traps_aa64dfr0(struct kvm_vcpu *vcpu)/* Trap Debug */ if (!FIELD_GET (ARM64_FEATURE_MASK (ID_AA64DFR0_DEBUGVER ), feature_ids)) - mdcr_set |=MDCR_EL2_TDRA |MDCR_EL2_TDA |MDCR_EL2_TDE ; + mdcr_set |=MDCR_EL2_TDRA |MDCR_EL2_TDA ;/* Trap OS Double Lock */ if (!FIELD_GET (ARM64_FEATURE_MASK (ID_AA64DFR0_DOUBLELOCK ), feature_ids))
arch/arm64/kvm/hyp/nvhe/sys_regs.c +11 -0
@@ -356,6 +356,17 @@ static const struct sys_reg_desc pvm_sys_reg_descs[] = {/* Cache maintenance by set/way operations are restricted. */ /* Debug and Trace Registers are restricted. */ +RAZ_WI (SYS_DBGBVRn_EL1 (0 )), +RAZ_WI (SYS_DBGBCRn_EL1 (0 )), +RAZ_WI (SYS_DBGWVRn_EL1 (0 )), +RAZ_WI (SYS_DBGWCRn_EL1 (0 )), +RAZ_WI (SYS_MDSCR_EL1 ), +RAZ_WI (SYS_OSLAR_EL1 ), +RAZ_WI (SYS_OSLSR_EL1 ), +RAZ_WI (SYS_OSDLR_EL1 ), + +/* Group 1 ID registers */ +RAZ_WI (SYS_REVIDR_EL1 ),/* AArch64 mappings of the AArch32 ID registers */ /* CRm=1 */
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 58/89 KVM: arm64: Restrict protected VM capabilities
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Restrict protected VM capabilities based on the
fixed-configuration for protected VMs.
No functional change intended in current KVM-supported modes (nVHE, VHE).
--- arch/arm64/include/asm/kvm_pkvm.h | 27 ++++++++++++ arch/arm64/kvm/arm.c | 69 ++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kvm_pkvm.h +27 -0
@@ -208,6 +208,33 @@ void kvm_shadow_destroy(struct kvm *kvm); ARM64_FEATURE_MASK(ID_AA64ISAR2_APA3) \ ) +/* +* Returns the maximum number of breakpoints supported for protected VMs. +*/ +static inline int pkvm_get_max_brps (void ) +{ +int num =FIELD_GET (ARM64_FEATURE_MASK (ID_AA64DFR0_BRPS ), +PVM_ID_AA64DFR0_ALLOW ); + +/* +* If breakpoints are supported, the maximum number is 1 + the field. +* Otherwise, return 0, which is not compliant with the architecture, +* but is reserved and is used here to indicate no debug support. +*/ +return num ? num +1 :0 ; +} + +/* +* Returns the maximum number of watchpoints supported for protected VMs. +*/ +static inline int pkvm_get_max_wrps (void ) +{ +int num =FIELD_GET (ARM64_FEATURE_MASK (ID_AA64DFR0_WRPS ), +PVM_ID_AA64DFR0_ALLOW ); + +return num ? num +1 :0 ; +} +extern struct memblock_region kvm_nvhe_sym (hyp_memory )[];extern unsigned int kvm_nvhe_sym (hyp_memblock_nr );
arch/arm64/kvm/arm.c +68 -1
83 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 59/89 KVM: arm64: Do not support MTE for protected VMs
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Return an error (-EINVAL) if trying to enable MTE on a protected vm.
--- arch/arm64/kvm/arm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
arch/arm64/kvm/arm.c +3 -1
@@ -90,7 +90,9 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,break ;case KVM_CAP_ARM_MTE :mutex_lock (&kvm->lock); -if (!system_supports_mte () || kvm->created_vcpus) { +if (!system_supports_mte () || +kvm_vm_is_protected (kvm) || + kvm->created_vcpus) { r = -EINVAL ; }else { r =0 ;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Re: [PATCH 59/89] KVM: arm64: Do not support MTE for protected VMs
Quoted 5 lines · Will Deacon
From: Fuad Tabba <[email protected]>
Return an error (
-EINVAL) if trying to enable MTE on a protected vm.
I think this commit message needs more explanation as to why MTE is not currently supported in protected VMs.
Peter
Re: [PATCH 59/89] KVM: arm64: Do not support MTE for protected VMs
Hi Peter,
Quoted 10 lines · Peter Collingbourne
Quoted 5 lines · Will Deacon
From: Fuad Tabba <[email protected]>
Return an error (
-EINVAL) if trying to enable MTE on a protected vm.I think this commit message needs more explanation as to why MTE is not currently supported in protected VMs.
Yes, we need to explain this more. Basically this is an extension of restricting features for protected VMs done earlier [*].
Various VM feature configurations are allowed in KVM/arm64, each requiring specific handling logic to deal with traps, context-switching and potentially emulation. Achieving feature parity in pKVM therefore requires either elevating this logic to EL2 (and substantially increasing the TCB) or continuing to trust the host handlers at EL1. Since neither of these options are especially appealing, pKVM instead limits the CPU features exposed to a guest to a fixed configuration based on the underlying hardware and which can mostly be provided straightforwardly by EL2.
This of course can change in the future and we can support more features for protected VMs as needed. We'll expand on this commit message when we respin.
Also note that this only applies to protected VMs. Non-protected VMs in protected mode support MTE.
Cheers,
/fuad
[*] https://lore.kernel.org/kvmarm/[email protected]/
Quoted 2 lines
Peter
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Re: [PATCH 59/89] KVM: arm64: Do not support MTE for protected VMs
Hi Fuad,
Quoted 26 lines · Fuad Tabba
Hi Peter,
Quoted 10 lines · Peter Collingbourne
Quoted 5 lines · Will Deacon
From: Fuad Tabba <[email protected]>
Return an error (
-EINVAL) if trying to enable MTE on a protected vm.I think this commit message needs more explanation as to why MTE is not currently supported in protected VMs.
Yes, we need to explain this more. Basically this is an extension of restricting features for protected VMs done earlier [*].
Various VM feature configurations are allowed in KVM/arm64, each requiring specific handling logic to deal with traps, context-switching and potentially emulation. Achieving feature parity in pKVM therefore requires either elevating this logic to EL2 (and substantially increasing the TCB) or continuing to trust the host handlers at EL1. Since neither of these options are especially appealing, pKVM instead limits the CPU features exposed to a guest to a fixed configuration based on the underlying hardware and which can mostly be provided straightforwardly by EL2.
Quoted 6 lines
This of course can change in the future and we can support more features for protected VMs as needed. We'll expand on this commit message when we respin.
Also note that this only applies to protected VMs. Non-protected VMs in protected mode support MTE.
I see. In this case unless I'm missing something the EL2 side seems quite trivial though (flipping some bits in HCR_EL2). The patch below (in place of this one) seems to make MTE work in my test environment (patched [1] crosvm on Android in MTE-enabled QEMU).
[1] https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3689015
From c87965cd14515586d487872486e7670874209113 Mon Sep 17 00:00:00 2001
From: Peter Collingbourne <[email protected]>
Date: Thu, 2 Jun 2022 19:16:02 -0700
Subject: [PATCH] arm64: support MTE in protected VMs
Enable HCR_EL2.ATA while running a vCPU with MTE enabled.
To avoid exposing MTE tags from the host to protected VMs, sanitize tags before donating pages.
--- arch/arm64/include/asm/kvm_pkvm.h | 4 +++- arch/arm64/kvm/hyp/nvhe/pkvm.c | 6 +++--- arch/arm64/kvm/mmu.c | 4 +++- 3 files changed, 9 insertions(+), 5 deletions(-)
arch/arm64/include/asm/kvm_pkvm.h +3 -1
@@ -73,10 +73,12 @@ void kvm_shadow_destroy(struct kvm *kvm);* Allow for protected VMs: * - Branch Target Identification * - Speculative Store Bypassing +* - Memory Tagging Extension */ #define PVM_ID_AA64PFR1_ALLOW (\ ARM64_FEATURE_MASK(ID_AA64PFR1_BT) | \ - ARM64_FEATURE_MASK(ID_AA64PFR1_SSBS) \ + ARM64_FEATURE_MASK(ID_AA64PFR1_SSBS) | \ + ARM64_FEATURE_MASK(ID_AA64PFR1_MTE) \ )/*
arch/arm64/kvm/hyp/nvhe/pkvm.c +3 -3
@@ -88,7 +88,7 @@ static void pvm_init_traps_aa64pfr1(struct kvm_vcpu *vcpu)/* Memory Tagging: Trap and Treat as Untagged if not supported. */ if (!FIELD_GET (ARM64_FEATURE_MASK (ID_AA64PFR1_MTE ), feature_ids)) { hcr_set |=HCR_TID5 ; - hcr_clear |=HCR_DCT |HCR_ATA ; + hcr_clear |=HCR_ATA ; } vcpu->arch.hcr_el2 |= hcr_set;
@@ -179,8 +179,8 @@ static void pvm_init_trap_regs(struct kvm_vcpu *vcpu)* - Feature id registers: to control features exposed to guests * - Implementation-defined features */ - vcpu->arch.hcr_el2 =HCR_GUEST_FLAGS | -HCR_TID3 |HCR_TACR |HCR_TIDCP |HCR_TID1 ; + vcpu->arch.hcr_el2 =HCR_GUEST_FLAGS |HCR_TID3 |HCR_TACR |HCR_TIDCP | +HCR_TID1 |HCR_ATA ;if (cpus_have_const_cap (ARM64_HAS_RAS_EXTN )) {/* route synchronous external abort exceptions to EL2 */
arch/arm64/kvm/mmu.c +3 -1
@@ -1206,8 +1206,10 @@ static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, goto dec_account; } -write_lock (&kvm->mmu_lock); pfn =page_to_pfn (page); +sanitise_mte_tags (kvm, pfn,PAGE_SIZE ); + +write_lock (&kvm->mmu_lock); ret =pkvm_host_map_guest (pfn, fault_ipa >>PAGE_SHIFT );if (ret) {if (ret == -EAGAIN )
Signature · 1 line
2.36.1.255.ge46751e96f-goog
Marc Zyngier
maintainerRe: [PATCH 59/89] KVM: arm64: Do not support MTE for protected VMs
Quoted 108 lines · Peter Collingbourne
Hi Fuad,
Quoted 26 lines · Fuad Tabba
Hi Peter,
Quoted 10 lines · Peter Collingbourne
Quoted 5 lines · Will Deacon
From: Fuad Tabba <[email protected]>
Return an error (
-EINVAL) if trying to enable MTE on a protected vm.I think this commit message needs more explanation as to why MTE is not currently supported in protected VMs.
Yes, we need to explain this more. Basically this is an extension of restricting features for protected VMs done earlier [*].
Various VM feature configurations are allowed in KVM/arm64, each requiring specific handling logic to deal with traps, context-switching and potentially emulation. Achieving feature parity in pKVM therefore requires either elevating this logic to EL2 (and substantially increasing the TCB) or continuing to trust the host handlers at EL1. Since neither of these options are especially appealing, pKVM instead limits the CPU features exposed to a guest to a fixed configuration based on the underlying hardware and which can mostly be provided straightforwardly by EL2.
Quoted 6 lines
This of course can change in the future and we can support more features for protected VMs as needed. We'll expand on this commit message when we respin.
Also note that this only applies to protected VMs. Non-protected VMs in protected mode support MTE.
I see. In this case unless I'm missing something the EL2 side seems quite trivial though (flipping some bits in
HCR_EL2). The patch below (in place of this one) seems to make MTE work in my test environment (patched [1] crosvm on Android in MTE-enabled QEMU).[1] https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3689015
From
c87965cd14515586d487872486e7670874209113Mon Sep 17 00:00:00 2001
From: Peter Collingbourne <[email protected]>
Date: Thu, 2 Jun 2022 19:16:02 -0700
Subject: [PATCH] arm64: support MTE in protected VMsEnable HCR_EL2.ATA while running a vCPU with MTE enabled.
To avoid exposing MTE tags from the host to protected VMs, sanitize tags before donating pages.
Signed-off-by Peter Collingbourne <[email protected]>--- arch/arm64/include/asm/kvm_pkvm.h | 4 +++- arch/arm64/kvm/hyp/nvhe/pkvm.c | 6 +++--- arch/arm64/kvm/mmu.c | 4 +++- 3 files changed, 9 insertions(+), 5 deletions(-)arch/arm64/include/asm/kvm_pkvm.h +3
-1@@ -73,10 +73,12 @@ void kvm_shadow_destroy(struct kvm *kvm);* Allow for protected VMs: * - Branch Target Identification * - Speculative Store Bypassing +* - Memory Tagging Extension */ #define PVM_ID_AA64PFR1_ALLOW (\ ARM64_FEATURE_MASK(ID_AA64PFR1_BT) | \ - ARM64_FEATURE_MASK(ID_AA64PFR1_SSBS) \ + ARM64_FEATURE_MASK(ID_AA64PFR1_SSBS) | \ + ARM64_FEATURE_MASK(ID_AA64PFR1_MTE) \ )/* arch/arm64/kvm/hyp/nvhe/pkvm.c +3
-3@@ -88,7 +88,7 @@ static void pvm_init_traps_aa64pfr1(struct kvm_vcpu *vcpu)/* Memory Tagging: Trap and Treat as Untagged if not supported. */ if (!FIELD_GET (ARM64_FEATURE_MASK (ID_AA64PFR1_MTE ), feature_ids)) { hcr_set |=HCR_TID5 ; - hcr_clear |=HCR_DCT |HCR_ATA ; + hcr_clear |=HCR_ATA ; } vcpu->arch.hcr_el2 |= hcr_set;@@ -179,8 +179,8 @@ static void pvm_init_trap_regs(struct kvm_vcpu *vcpu)* - Feature id registers: to control features exposed to guests * - Implementation-defined features */ - vcpu->arch.hcr_el2 =HCR_GUEST_FLAGS | -HCR_TID3 |HCR_TACR |HCR_TIDCP |HCR_TID1 ; + vcpu->arch.hcr_el2 =HCR_GUEST_FLAGS |HCR_TID3 |HCR_TACR |HCR_TIDCP | +HCR_TID1 |HCR_ATA ;if (cpus_have_const_cap (ARM64_HAS_RAS_EXTN )) {/* route synchronous external abort exceptions to EL2 */ arch/arm64/kvm/mmu.c +0
-0
arch/arm64/kvm/mmu.c static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, · line 1212
Quoted 8 lines
arch/arm64/kvm/mmu.c +3
-1@@ -1206,8 +1206,10 @@ static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, goto dec_account; } -write_lock (&kvm->mmu_lock); pfn =page_to_pfn (page); +sanitise_mte_tags (kvm, pfn,PAGE_SIZE ); + +write_lock (&kvm->mmu_lock);
Is it really safe to rely on the host to clear the tags? My guts feeling says that it isn't. If it is required, we cannot leave this responsibility to the host, and this logic must be moved to EL2. And if it isn't, then we should drop it.
arch/arm64/kvm/mmu.c static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, · line 1215
Quoted 3 lines
arch/arm64/kvm/mmu.c +0
-0ret =pkvm_host_map_guest (pfn, fault_ipa >>PAGE_SHIFT );if (ret) {if (ret == -EAGAIN )
But the bigger picture here is what ensures that the host cannot mess with the guest tags? I don't think we have a any mechanism to guarantee that, specially on systems where the tags are only a memory carve-out, which the host could map and change at will.
In any case, this isn't the time to pile new features on top of pKVM. The current plan is to not support MTE at all, and only do it once we have a definitive story on page donation (which as you may have noticed, is pretty hacky). I don't see any compelling reason to add MTE to the mix until this is solved.
Thanks,
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Re: [PATCH 59/89] KVM: arm64: Do not support MTE for protected VMs
Quoted 114 lines · Marc Zyngier
Quoted 111 lines · Peter Collingbourne
Hi Fuad,
Quoted 26 lines · Fuad Tabba
Hi Peter,
Quoted 10 lines · Peter Collingbourne
Quoted 5 lines · Will Deacon
From: Fuad Tabba <[email protected]>
Return an error (
-EINVAL) if trying to enable MTE on a protected vm.I think this commit message needs more explanation as to why MTE is not currently supported in protected VMs.
Yes, we need to explain this more. Basically this is an extension of restricting features for protected VMs done earlier [*].
Various VM feature configurations are allowed in KVM/arm64, each requiring specific handling logic to deal with traps, context-switching and potentially emulation. Achieving feature parity in pKVM therefore requires either elevating this logic to EL2 (and substantially increasing the TCB) or continuing to trust the host handlers at EL1. Since neither of these options are especially appealing, pKVM instead limits the CPU features exposed to a guest to a fixed configuration based on the underlying hardware and which can mostly be provided straightforwardly by EL2.
Quoted 6 lines
This of course can change in the future and we can support more features for protected VMs as needed. We'll expand on this commit message when we respin.
Also note that this only applies to protected VMs. Non-protected VMs in protected mode support MTE.
I see. In this case unless I'm missing something the EL2 side seems quite trivial though (flipping some bits in
HCR_EL2). The patch below (in place of this one) seems to make MTE work in my test environment (patched [1] crosvm on Android in MTE-enabled QEMU).[1] https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3689015
From
c87965cd14515586d487872486e7670874209113Mon Sep 17 00:00:00 2001
From: Peter Collingbourne <[email protected]>
Date: Thu, 2 Jun 2022 19:16:02 -0700
Subject: [PATCH] arm64: support MTE in protected VMsEnable HCR_EL2.ATA while running a vCPU with MTE enabled.
To avoid exposing MTE tags from the host to protected VMs, sanitize tags before donating pages.
Signed-off-by Peter Collingbourne <[email protected]>--- arch/arm64/include/asm/kvm_pkvm.h | 4 +++- arch/arm64/kvm/hyp/nvhe/pkvm.c | 6 +++--- arch/arm64/kvm/mmu.c | 4 +++- 3 files changed, 9 insertions(+), 5 deletions(-)arch/arm64/include/asm/kvm_pkvm.h +3
-1@@ -73,10 +73,12 @@ void kvm_shadow_destroy(struct kvm *kvm);* Allow for protected VMs: * - Branch Target Identification * - Speculative Store Bypassing +* - Memory Tagging Extension */ #define PVM_ID_AA64PFR1_ALLOW (\ ARM64_FEATURE_MASK(ID_AA64PFR1_BT) | \ - ARM64_FEATURE_MASK(ID_AA64PFR1_SSBS) \ + ARM64_FEATURE_MASK(ID_AA64PFR1_SSBS) | \ + ARM64_FEATURE_MASK(ID_AA64PFR1_MTE) \ )/* arch/arm64/kvm/hyp/nvhe/pkvm.c +3
-3@@ -88,7 +88,7 @@ static void pvm_init_traps_aa64pfr1(struct kvm_vcpu *vcpu)/* Memory Tagging: Trap and Treat as Untagged if not supported. */ if (!FIELD_GET (ARM64_FEATURE_MASK (ID_AA64PFR1_MTE ), feature_ids)) { hcr_set |=HCR_TID5 ; - hcr_clear |=HCR_DCT |HCR_ATA ; + hcr_clear |=HCR_ATA ; } vcpu->arch.hcr_el2 |= hcr_set;@@ -179,8 +179,8 @@ static void pvm_init_trap_regs(struct kvm_vcpu *vcpu)* - Feature id registers: to control features exposed to guests * - Implementation-defined features */ - vcpu->arch.hcr_el2 =HCR_GUEST_FLAGS | -HCR_TID3 |HCR_TACR |HCR_TIDCP |HCR_TID1 ; + vcpu->arch.hcr_el2 =HCR_GUEST_FLAGS |HCR_TID3 |HCR_TACR |HCR_TIDCP | +HCR_TID1 |HCR_ATA ;if (cpus_have_const_cap (ARM64_HAS_RAS_EXTN )) {/* route synchronous external abort exceptions to EL2 */ arch/arm64/kvm/mmu.c +0
-0@@ -1206,8 +1206,10 @@ static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, goto dec_account; }
Quoted 10 lines
arch/arm64/kvm/mmu.c static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, · line 1212
Quoted 5 lines
arch/arm64/kvm/mmu.c +3
-1-write_lock (&kvm->mmu_lock); pfn =page_to_pfn (page); +sanitise_mte_tags (kvm, pfn,PAGE_SIZE ); + +write_lock (&kvm->mmu_lock);Is it really safe to rely on the host to clear the tags? My guts feeling says that it isn't. If it is required, we cannot leave this responsibility to the host, and this logic must be moved to EL2. And if it isn't, then we should drop it.
The goal here isn't to protect the guest. It's already the case that whatever the page contents are when the page is donated (from the perspective of the KVM client), that's what the guest sees. That applies to both data and (in non-protected VMs) tags.
The code that I added here is for solving a different problem, which is to avoid exposing stale host state to the guest, which the KVM client may not even be aware of. We sanitize pages before exposing them in non-protected VMs for the same reason.
Quoted 8 lines
arch/arm64/kvm/mmu.c static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, · line 1215
Quoted 3 lines
arch/arm64/kvm/mmu.c +0
-0ret =pkvm_host_map_guest (pfn, fault_ipa >>PAGE_SHIFT );if (ret) {if (ret == -EAGAIN )But the bigger picture here is what ensures that the host cannot mess with the guest tags? I don't think we have a any mechanism to guarantee that, specially on systems where the tags are only a memory carve-out, which the host could map and change at will.
Right, I forgot about that. We probably only want to expose MTE to guests if we have some indication (through the device tree or ACPI) of how to protect the guest tag storage.
Quoted 5 lines
In any case, this isn't the time to pile new features on top of pKVM. The current plan is to not support MTE at all, and only do it once we have a definitive story on page donation (which as you may have noticed, is pretty hacky). I don't see any compelling reason to add MTE to the mix until this is solved.
It sounds reasonable to land a basic set of features to begin with and add MTE later. I'll develop my MTE-in-pKVM patch series as a followup on top of this series.
Peter
Catalin Marinas
maintainerRe: [PATCH 59/89] KVM: arm64: Do not support MTE for protected VMs
Quoted 9 lines · Peter Collingbourne
Quoted 4 lines · Marc Zyngier
But the bigger picture here is what ensures that the host cannot mess with the guest tags? I don't think we have a any mechanism to guarantee that, specially on systems where the tags are only a memory carve-out, which the host could map and change at will.
Right, I forgot about that. We probably only want to expose MTE to guests if we have some indication (through the device tree or ACPI) of how to protect the guest tag storage.
I think this would be useful irrespective of MTE. Some SoCs (though I hope very rare these days) may allow for physical aliasing of RAM but if the host stage 2 only protects one of the aliases, it's not of much use.
I am yet to fully understand how pKVM works but with the separation of the hyp from the host kernel, it may have to actually parse the DT/ACPI/EFI tables itself if it cannot rely on what the host kernel told it. IIUC currently it creates an idmap at stage 2 for the host kernel, only unmapped if the memory was assigned to a guest. But not sure what happens with the rest of the host physical address space (devices etc.), I presume they are fully accessible by the host kernel in stage 2.
Signature · 1 line
Catalin
Re: [PATCH 59/89] KVM: arm64: Do not support MTE for protected VMs
In reply to Will DeaconQuoted 16 lines · Will Deacon
From: Fuad Tabba <[email protected]>
Return an error (
-EINVAL) if trying to enable MTE on a protected vm.Signed-off-by Fuad Tabba <[email protected]>--- arch/arm64/kvm/arm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)arch/arm64/kvm/arm.c +0
-0
arch/arm64/kvm/arm.c int kvm_vm_ioctl_enable_cap(struct kvm *kvm, · line 94
Quoted 6 lines
arch/arm64/kvm/arm.c +2
-1@@ -90,7 +90,9 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,break ;case KVM_CAP_ARM_MTE :mutex_lock (&kvm->lock); -if (!system_supports_mte () || kvm->created_vcpus) { + if (!system_supports_mte() || + kvm_vm_is_protected(kvm) ||
Should this check be added to kvm_vm_ioctl_check_extension() as well?
Peter
Re: [PATCH 59/89] KVM: arm64: Do not support MTE for protected VMs
Hi Peter,
Quoted 13 lines · Peter Collingbourne
Quoted 11 lines · Will Deacon
From: Fuad Tabba <[email protected]>
Return an error (
-EINVAL) if trying to enable MTE on a protected vm.Signed-off-by Fuad Tabba <[email protected]>--- arch/arm64/kvm/arm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
Quoted 13 lines
arch/arm64/kvm/arm.c int kvm_vm_ioctl_enable_cap(struct kvm *kvm, · line 94
Quoted 11 lines
arch/arm64/kvm/arm.c +2
-1@@ -90,7 +90,9 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,break ;case KVM_CAP_ARM_MTE :mutex_lock (&kvm->lock); -if (!system_supports_mte () || kvm->created_vcpus) { + if (!system_supports_mte() || + kvm_vm_is_protected(kvm) ||Should this check be added to
kvm_vm_ioctl_check_extension()as well?
No need. kvm_vm_ioctl_check_extension() calls pkvm_check_extension() for protected vms, which functions as an allow list rather than a block list.
Cheers,
/fuad
Quoted 1 line
Peter
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Re: [PATCH 59/89] KVM: arm64: Do not support MTE for protected VMs
Quoted 31 lines · Fuad Tabba
Hi Peter,
Quoted 13 lines · Peter Collingbourne
Quoted 11 lines · Will Deacon
From: Fuad Tabba <[email protected]>
Return an error (
-EINVAL) if trying to enable MTE on a protected vm.Signed-off-by Fuad Tabba <[email protected]>--- arch/arm64/kvm/arm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)Quoted 13 lines
arch/arm64/kvm/arm.c int kvm_vm_ioctl_enable_cap(struct kvm *kvm, · line 94
Quoted 11 lines
arch/arm64/kvm/arm.c +2
-1@@ -90,7 +90,9 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,break ;case KVM_CAP_ARM_MTE :mutex_lock (&kvm->lock); -if (!system_supports_mte () || kvm->created_vcpus) { + if (!system_supports_mte() || + kvm_vm_is_protected(kvm) ||Should this check be added to
kvm_vm_ioctl_check_extension()as well?
Quoted 3 lines
No need.
kvm_vm_ioctl_check_extension()callspkvm_check_extension()for protected vms, which functions as an allow list rather than a block list.
I see. I guess I got confused when reading the code because I saw this in kvm_check_extension():
case KVM_CAP_ARM_NISV_TO_USER:
r = !kvm || !kvm_vm_is_protected(kvm);
break;This can probably be simplified to "r = 1;".
Peter
Will Deacon
maintainerPATCH 60/89 KVM: arm64: Refactor reset_mpidr to extract its computation
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Move the computation of the mpidr to its own function in a shared header, as the computation will be used by hyp in protected mode.
No functional change intended.
--- arch/arm64/kvm/sys_regs.c | 14 +------------- arch/arm64/kvm/sys_regs.h | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 13 deletions(-)
arch/arm64/kvm/sys_regs.c +1 -13
@@ -598,19 +598,7 @@ static void reset_actlr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)static void reset_mpidr (struct kvm_vcpu *vcpu,const struct sys_reg_desc *r) { -u64 mpidr; - -/* -* Map the vcpu_id into the first three affinity level fields of -* the MPIDR. We limit the number of VCPUs in level 0 due to a -* limitation to 16 CPUs in that level in the ICC_SGIxR registers -* of the GICv3 to be able to address each CPU directly when -* sending IPIs. -*/ - mpidr = (vcpu->vcpu_id &0x0f ) <<MPIDR_LEVEL_SHIFT (0 ); - mpidr |= ((vcpu->vcpu_id >>4 ) &0xff ) <<MPIDR_LEVEL_SHIFT (1 ); - mpidr |= ((vcpu->vcpu_id >>12 ) &0xff ) <<MPIDR_LEVEL_SHIFT (2 ); -vcpu_write_sys_reg (vcpu, (1ULL <<31 ) | mpidr,MPIDR_EL1 ); +vcpu_write_sys_reg (vcpu,calculate_mpidr (vcpu),MPIDR_EL1 ); } static unsigned int pmu_visibility(const struct kvm_vcpu *vcpu,
arch/arm64/kvm/sys_regs.h +19 -0
@@ -183,6 +183,25 @@ find_reg(const struct sys_reg_params *params, const struct sys_reg_desc table[],return __inline_bsearch ((void *)pval, table, num,sizeof (table[0 ]), match_sys_reg); } +static inline u64 calculate_mpidr (const struct kvm_vcpu *vcpu) +{ +u64 mpidr; + +/* +* Map the vcpu_id into the first three affinity level fields of +* the MPIDR. We limit the number of VCPUs in level 0 due to a +* limitation to 16 CPUs in that level in the ICC_SGIxR registers +* of the GICv3 to be able to address each CPU directly when +* sending IPIs. +*/ + mpidr = (vcpu->vcpu_id &0x0f ) <<MPIDR_LEVEL_SHIFT (0 ); + mpidr |= ((vcpu->vcpu_id >>4 ) &0xff ) <<MPIDR_LEVEL_SHIFT (1 ); + mpidr |= ((vcpu->vcpu_id >>12 ) &0xff ) <<MPIDR_LEVEL_SHIFT (2 ); + mpidr |= (1ULL <<31 ); + +return mpidr; +} + const struct sys_reg_desc *find_reg_by_id(u64 id, struct sys_reg_params *params, const struct sys_reg_desc table[],
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 61/89 KVM: arm64: Reset sysregs for protected VMs
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Create a framework for resetting protected VM system registers to their architecturally defined reset values.
No functional change intended as these are not hooked in yet.
--- arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 1 + arch/arm64/kvm/hyp/nvhe/sys_regs.c | 84 +++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 1 deletion(-)
arch/arm64/kvm/hyp/include/nvhe/pkvm.h +1 -0
@@ -98,6 +98,7 @@ struct kvm_shadow_vcpu_state *pkvm_loaded_shadow_vcpu_state(void);u64 pvm_read_id_reg (const struct kvm_vcpu *vcpu,u32 id);bool kvm_handle_pvm_sysreg (struct kvm_vcpu *vcpu,u64 *exit_code);bool kvm_handle_pvm_restricted (struct kvm_vcpu *vcpu,u64 *exit_code); +void kvm_reset_pvm_sys_regs (struct kvm_vcpu *vcpu);int kvm_check_pvm_sysreg_table (void );#endif /* __ARM64_KVM_NVHE_PKVM_H__ */
arch/arm64/kvm/hyp/nvhe/sys_regs.c +83 -1
97 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 62/89 KVM: arm64: Move pkvm_vcpu_init_traps to shadow vcpu init
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Move the initialization of traps to the initialization of the shadow vcpu, and remove the associated hypercall.
No functional change intended.
--- arch/arm64/include/asm/kvm_asm.h | 1 - arch/arm64/kvm/arm.c | 8 -------- arch/arm64/kvm/hyp/include/nvhe/trap_handler.h | 2 -- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 8 -------- arch/arm64/kvm/hyp/nvhe/pkvm.c | 4 +++- 5 files changed, 3 insertions(+), 20 deletions(-)
arch/arm64/include/asm/kvm_asm.h +0 -1
@@ -73,7 +73,6 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid, __KVM_HOST_SMCCC_FUNC___kvm_flush_cpu_context, __KVM_HOST_SMCCC_FUNC___kvm_timer_set_cntvoff, - __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_init_traps, __KVM_HOST_SMCCC_FUNC___vgic_v3_save_vmcr_aprs, __KVM_HOST_SMCCC_FUNC___vgic_v3_restore_vmcr_aprs, __KVM_HOST_SMCCC_FUNC___pkvm_init_shadow,
arch/arm64/kvm/arm.c +0 -8
@@ -664,14 +664,6 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)static_branch_inc (&userspace_irqchip_in_use); } -/* -* Initialize traps for protected VMs. -* NOTE: Move to run in EL2 directly, rather than via a hypercall, once -* the code is in place for first run initialization at EL2. -*/ -if (kvm_vm_is_protected (kvm)) -kvm_call_hyp_nvhe (__pkvm_vcpu_init_traps, vcpu); -mutex_lock (&kvm->lock);set_bit (KVM_ARCH_FLAG_HAS_RAN_ONCE , &kvm->arch.flags);mutex_unlock (&kvm->lock);
arch/arm64/kvm/hyp/include/nvhe/trap_handler.h +0 -2
@@ -15,6 +15,4 @@#define DECLARE_REG (type, name, ctxt, reg) \ type name = (type)cpu_reg(ctxt, (reg)) -void __pkvm_vcpu_init_traps (struct kvm_vcpu *vcpu); -#endif /* __ARM64_KVM_NVHE_TRAP_HANDLER_H__ */
arch/arm64/kvm/hyp/nvhe/hyp-main.c +0 -8
@@ -620,13 +620,6 @@ static void handle___pkvm_prot_finalize(struct kvm_cpu_context *host_ctxt)cpu_reg (host_ctxt,1 ) =__pkvm_prot_finalize (); } -static void handle___pkvm_vcpu_init_traps (struct kvm_cpu_context *host_ctxt) -{ - DECLARE_REG(struct kvm_vcpu *, vcpu, host_ctxt, 1); - -__pkvm_vcpu_init_traps (kern_hyp_va (vcpu)); -} -static void handle___pkvm_init_shadow (struct kvm_cpu_context *host_ctxt) { DECLARE_REG(struct kvm *, host_kvm, host_ctxt, 1);
@@ -674,7 +667,6 @@ static const hcall_t host_hcall[] = {HANDLE_FUNC (__kvm_tlb_flush_vmid),HANDLE_FUNC (__kvm_flush_cpu_context),HANDLE_FUNC (__kvm_timer_set_cntvoff), -HANDLE_FUNC (__pkvm_vcpu_init_traps),HANDLE_FUNC (__vgic_v3_save_vmcr_aprs),HANDLE_FUNC (__vgic_v3_restore_vmcr_aprs), HANDLE_FUNC(__pkvm_init_shadow),
arch/arm64/kvm/hyp/nvhe/pkvm.c +3 -1
@@ -188,7 +188,7 @@ static void pvm_init_trap_regs(struct kvm_vcpu *vcpu)/* * Initialize trap register values for protected VMs. */ -void __pkvm_vcpu_init_traps (struct kvm_vcpu *vcpu) +static void pkvm_vcpu_init_traps (struct kvm_vcpu *vcpu) {pvm_init_trap_regs (vcpu);pvm_init_traps_aa64pfr0 (vcpu);
@@ -363,6 +363,8 @@ static int init_shadow_structs(struct kvm *kvm, struct kvm_shadow_vm *vm, shadow_vcpu->vcpu_idx = i; shadow_vcpu->arch.hw_mmu = &vm->kvm.arch.mmu; + +pkvm_vcpu_init_traps (shadow_vcpu); }return 0 ;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 63/89 KVM: arm64: Fix initializing traps in protected mode
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
The values of the trapping registers for protected VMs should be computed from the ground up, and not depend on potentially preexisting values.
Moreover, non-protected VMs should not be restricted in protected mode in the same manner as protected VMs.
--- arch/arm64/kvm/hyp/nvhe/pkvm.c | 48 ++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 17 deletions(-)
arch/arm64/kvm/hyp/nvhe/pkvm.c +31 -17
@@ -168,34 +168,48 @@ static void pvm_init_traps_aa64mmfr1(struct kvm_vcpu *vcpu)*/ static void pvm_init_trap_regs (struct kvm_vcpu *vcpu) { -const u64 hcr_trap_feat_regs =HCR_TID3 ; -const u64 hcr_trap_impdef =HCR_TACR |HCR_TIDCP |HCR_TID1 ; -/* * Always trap: * - Feature id registers: to control features exposed to guests * - Implementation-defined features */ - vcpu->arch.hcr_el2 |= hcr_trap_feat_regs | hcr_trap_impdef; + vcpu->arch.hcr_el2 =HCR_GUEST_FLAGS | +HCR_TID3 |HCR_TACR |HCR_TIDCP |HCR_TID1 ; + +if (cpus_have_const_cap (ARM64_HAS_RAS_EXTN )) { +/* route synchronous external abort exceptions to EL2 */ + vcpu->arch.hcr_el2 |=HCR_TEA ; +/* trap error record accesses */ + vcpu->arch.hcr_el2 |=HCR_TERR ; + } -/* Clear res0 and set res1 bits to trap potential new features. */ - vcpu->arch.hcr_el2 &= ~(HCR_RES0 ); - vcpu->arch.mdcr_el2 &= ~(MDCR_EL2_RES0 ); - vcpu->arch.cptr_el2 |=CPTR_NVHE_EL2_RES1 ; - vcpu->arch.cptr_el2 &= ~(CPTR_NVHE_EL2_RES0 ); +if (cpus_have_const_cap (ARM64_HAS_STAGE2_FWB )) + vcpu->arch.hcr_el2 |=HCR_FWB ; + +if (cpus_have_const_cap (ARM64_MISMATCHED_CACHE_TYPE )) + vcpu->arch.hcr_el2 |=HCR_TID2 ; }/* * Initialize trap register values for protected VMs. */ -static void pkvm_vcpu_init_traps (struct kvm_vcpu *vcpu) +static void pkvm_vcpu_init_traps (struct kvm_vcpu *shadow_vcpu,struct kvm_vcpu *host_vcpu) { -pvm_init_trap_regs (vcpu); -pvm_init_traps_aa64pfr0 (vcpu); -pvm_init_traps_aa64pfr1 (vcpu); -pvm_init_traps_aa64dfr0 (vcpu); -pvm_init_traps_aa64mmfr0 (vcpu); -pvm_init_traps_aa64mmfr1 (vcpu); + shadow_vcpu->arch.cptr_el2 =CPTR_EL2_DEFAULT ; + shadow_vcpu->arch.mdcr_el2 =0 ; + +if (!vcpu_is_protected (shadow_vcpu)) { + shadow_vcpu->arch.hcr_el2 =HCR_GUEST_FLAGS | +READ_ONCE (host_vcpu->arch.hcr_el2); +return ; + } + +pvm_init_trap_regs (shadow_vcpu); +pvm_init_traps_aa64pfr0 (shadow_vcpu); +pvm_init_traps_aa64pfr1 (shadow_vcpu); +pvm_init_traps_aa64dfr0 (shadow_vcpu); +pvm_init_traps_aa64mmfr0 (shadow_vcpu); +pvm_init_traps_aa64mmfr1 (shadow_vcpu); }/*
@@ -364,7 +378,7 @@ static int init_shadow_structs(struct kvm *kvm, struct kvm_shadow_vm *vm, shadow_vcpu->arch.hw_mmu = &vm->kvm.arch.mmu; -pkvm_vcpu_init_traps (shadow_vcpu); +pkvm_vcpu_init_traps (shadow_vcpu, host_vcpu); }return 0 ;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 64/89 KVM: arm64: Advertise GICv3 sysreg interface to protected guests
In reply to Will DeaconAdvertise the system register GICv3 CPU interface to protected guests as that is the only supported configuration under pKVM.
--- arch/arm64/include/asm/kvm_pkvm.h | 2 ++ 1 file changed, 2 insertions(+)
arch/arm64/include/asm/kvm_pkvm.h +2 -0
@@ -43,11 +43,13 @@ void kvm_shadow_destroy(struct kvm *kvm);/* * Allow for protected VMs: * - Floating-point and Advanced SIMD +* - GICv3(+) system register interface * - Data Independent Timing */ #define PVM_ID_AA64PFR0_ALLOW (\ ARM64_FEATURE_MASK(ID_AA64PFR0_FP) | \ ARM64_FEATURE_MASK(ID_AA64PFR0_ASIMD) | \ + ARM64_FEATURE_MASK(ID_AA64PFR0_GIC) | \ ARM64_FEATURE_MASK(ID_AA64PFR0_DIT) \ )
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 65/89 KVM: arm64: Force injection of a data abort on NISV MMIO exit
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
If a vcpu exits for a data abort with an invalid syndrome, the expectations are that userspace has a chance to save the day if it has requested to see such exits.
However, this is completely futile in the case of a protected VM, as none of the state is available. In this particular case, inject a data abort directly into the vcpu, consistent with what userspace could do.
This also helps with pKVM, which discards all syndrome information when forwarding data aborts that are not known to be MMIO.
Finally, hide the RETURN_NISV_IO_ABORT_TO_USER cap from userspace on protected VMs, and document this tweak to the API.
--- Documentation/virt/kvm/api.rst | 7 +++++++ arch/arm64/kvm/arm.c | 14 ++++++++++---- arch/arm64/kvm/mmio.c | 9 +++++++++ 3 files changed, 26 insertions(+), 4 deletions(-)
Documentation/virt/kvm/api.rst +7 -0
@@ -6115,6 +6115,13 @@ Note that KVM does not skip the faulting instruction as it does for KVM_EXIT_MMIO, but userspace has to emulate any change to the processing state if it decides to decode and emulate the instruction. +This feature isn't available to protected VMs, as userspace does not +have access to the state that is required to perform the emulation. +Instead, a data abort exception is directly injected in the guest. +Note that although KVM_CAP_ARM_NISV_TO_USER will be reported if +queried outside of a protected VM context, the feature will not be +exposed if queried on a protected VM file descriptor. + :: /* KVM_EXIT_X86_RDMSR / KVM_EXIT_X86_WRMSR */
arch/arm64/kvm/arm.c +10 -4
@@ -84,9 +84,13 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,switch (cap->cap) {case KVM_CAP_ARM_NISV_TO_USER : - r =0 ; -set_bit (KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER , - &kvm->arch.flags); +if (kvm_vm_is_protected (kvm)) { + r = -EINVAL ; + }else { + r =0 ; +set_bit (KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER , + &kvm->arch.flags); + }break ;case KVM_CAP_ARM_MTE :mutex_lock (&kvm->lock);
@@ -217,13 +221,15 @@ static int kvm_check_extension(struct kvm *kvm, long ext)case KVM_CAP_IMMEDIATE_EXIT :case KVM_CAP_VCPU_EVENTS :case KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 : -case KVM_CAP_ARM_NISV_TO_USER :case KVM_CAP_ARM_INJECT_EXT_DABT :case KVM_CAP_SET_GUEST_DEBUG :case KVM_CAP_VCPU_ATTRIBUTES :case KVM_CAP_PTP_KVM : r =1 ;break ; +case KVM_CAP_ARM_NISV_TO_USER : + r = !kvm || !kvm_vm_is_protected (kvm); +break ;case KVM_CAP_SET_GUEST_DEBUG2 :return KVM_GUESTDBG_VALID_MASK ;case KVM_CAP_ARM_SET_DEVICE_ADDR :
arch/arm64/kvm/mmio.c +9 -0
@@ -133,8 +133,17 @@ int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)/* * No valid syndrome? Ask userspace for help if it has * volunteered to do so, and bail out otherwise. +* +* In the protected VM case, there isn't much userspace can do +* though, so directly deliver an exception to the guest. */ if (!kvm_vcpu_dabt_isvalid (vcpu)) { +if (is_protected_kvm_enabled () && +kvm_vm_is_protected (vcpu->kvm)) { +kvm_inject_dabt (vcpu,kvm_vcpu_get_hfar (vcpu)); +return 1 ; + } +if (test_bit (KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER , &vcpu->kvm->arch.flags)) { run->exit_reason =KVM_EXIT_ARM_NISV ;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 66/89 KVM: arm64: Donate memory to protected guests
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
Instead of sharing memory with protected guests, which still leaves the host with r/w access, donate the underlying pages so that they are unmapped from the host stage-2.
--- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
arch/arm64/kvm/hyp/nvhe/hyp-main.c +4 -1
@@ -465,7 +465,10 @@ static void handle___pkvm_host_map_guest(struct kvm_cpu_context *host_ctxt)if (ret) goto out; - ret =__pkvm_host_share_guest (pfn, gfn, shadow_vcpu); +if (shadow_state_is_protected (shadow_state)) + ret =__pkvm_host_donate_guest (pfn, gfn, shadow_vcpu); +else + ret =__pkvm_host_share_guest (pfn, gfn, shadow_vcpu); out:cpu_reg (host_ctxt,1 ) = ret; }
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 67/89 KVM: arm64: Add EL2 entry/exit handlers for pKVM guests
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Introduce separate El2 entry/exit handlers for protected and non-protected guests under pKVM and hook up the protected handlers to expose the minimum amount of data to the host required for EL1 handling.
--- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 230 ++++++++++++++++++++++++++++- 1 file changed, 228 insertions(+), 2 deletions(-)
arch/arm64/kvm/hyp/nvhe/hyp-main.c +228 -2
@@ -20,6 +20,8 @@#include <linux/irqchip/arm-gic-v3.h> +#include "../../sys_regs.h" +/* * Host FPSIMD state. Written to when the guest accesses its own FPSIMD state, * and read when the guest state is live and we need to switch back to the host.
@@ -34,6 +36,207 @@ void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt);typedef void (*shadow_entry_exit_handler_fn )(struct kvm_vcpu *,struct kvm_vcpu *); +static void handle_pvm_entry_wfx (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) +{ + shadow_vcpu->arch.flags |=READ_ONCE (host_vcpu->arch.flags) & +KVM_ARM64_INCREMENT_PC ; +} + +static void handle_pvm_entry_sys64 (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) +{ +unsigned long host_flags; + + host_flags =READ_ONCE (host_vcpu->arch.flags); + +/* Exceptions have priority on anything else */ +if (host_flags &KVM_ARM64_PENDING_EXCEPTION ) { +/* Exceptions caused by this should be undef exceptions. */ +u32 esr = (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT); + +__vcpu_sys_reg (shadow_vcpu,ESR_EL1 ) = esr; + shadow_vcpu->arch.flags &= ~(KVM_ARM64_PENDING_EXCEPTION | +KVM_ARM64_EXCEPT_MASK ); + shadow_vcpu->arch.flags |= (KVM_ARM64_PENDING_EXCEPTION | + KVM_ARM64_EXCEPT_AA64_ELx_SYNC | +KVM_ARM64_EXCEPT_AA64_EL1 ); + +return ; + } + +if (host_flags &KVM_ARM64_INCREMENT_PC ) { + shadow_vcpu->arch.flags &= ~(KVM_ARM64_PENDING_EXCEPTION | +KVM_ARM64_EXCEPT_MASK ); + shadow_vcpu->arch.flags |=KVM_ARM64_INCREMENT_PC ; + } + +if (!esr_sys64_to_params (shadow_vcpu->arch.fault.esr_el2).is_write) { +/* r0 as transfer register between the guest and the host. */ +u64 rt_val =READ_ONCE (host_vcpu->arch.ctxt.regs.regs[0 ]); +int rt =kvm_vcpu_sys_get_rt (shadow_vcpu); + +vcpu_set_reg (shadow_vcpu, rt, rt_val); + } +} + +static void handle_pvm_entry_iabt (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) +{ +unsigned long cpsr = *vcpu_cpsr (shadow_vcpu); +unsigned long host_flags; +u32 esr = ESR_ELx_IL; + + host_flags =READ_ONCE (host_vcpu->arch.flags); + +if (!(host_flags &KVM_ARM64_PENDING_EXCEPTION )) +return ; + +/* +* If the host wants to inject an exception, get syndrom and +* fault address. +*/ +if ((cpsr &PSR_MODE_MASK ) == PSR_MODE_EL0t) + esr |= (ESR_ELx_EC_IABT_LOW << ESR_ELx_EC_SHIFT); +else + esr |= (ESR_ELx_EC_IABT_CUR << ESR_ELx_EC_SHIFT); + + esr |= ESR_ELx_FSC_EXTABT; + +__vcpu_sys_reg (shadow_vcpu,ESR_EL1 ) = esr; +__vcpu_sys_reg (shadow_vcpu,FAR_EL1 ) =kvm_vcpu_get_hfar (shadow_vcpu); + +/* Tell the run loop that we want to inject something */ + shadow_vcpu->arch.flags &= ~(KVM_ARM64_PENDING_EXCEPTION | +KVM_ARM64_EXCEPT_MASK ); + shadow_vcpu->arch.flags |= (KVM_ARM64_PENDING_EXCEPTION | + KVM_ARM64_EXCEPT_AA64_ELx_SYNC | +KVM_ARM64_EXCEPT_AA64_EL1 ); +} + +static void handle_pvm_entry_dabt (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) +{ +unsigned long host_flags; +bool rd_update; + + host_flags =READ_ONCE (host_vcpu->arch.flags); + +/* Exceptions have priority over anything else */ +if (host_flags &KVM_ARM64_PENDING_EXCEPTION ) { +unsigned long cpsr = *vcpu_cpsr (shadow_vcpu); +u32 esr = ESR_ELx_IL; + +if ((cpsr &PSR_MODE_MASK ) == PSR_MODE_EL0t) + esr |= (ESR_ELx_EC_DABT_LOW << ESR_ELx_EC_SHIFT); +else + esr |= (ESR_ELx_EC_DABT_CUR << ESR_ELx_EC_SHIFT); + + esr |= ESR_ELx_FSC_EXTABT; + +__vcpu_sys_reg (shadow_vcpu,ESR_EL1 ) = esr; +__vcpu_sys_reg (shadow_vcpu,FAR_EL1 ) =kvm_vcpu_get_hfar (shadow_vcpu); +/* Tell the run loop that we want to inject something */ + shadow_vcpu->arch.flags &= ~(KVM_ARM64_PENDING_EXCEPTION | +KVM_ARM64_EXCEPT_MASK ); + shadow_vcpu->arch.flags |= (KVM_ARM64_PENDING_EXCEPTION | + KVM_ARM64_EXCEPT_AA64_ELx_SYNC | +KVM_ARM64_EXCEPT_AA64_EL1 ); + +/* Cancel potential in-flight MMIO */ + shadow_vcpu->mmio_needed = false; +return ; + } + +/* Handle PC increment on MMIO */ +if ((host_flags &KVM_ARM64_INCREMENT_PC ) && shadow_vcpu->mmio_needed) { + shadow_vcpu->arch.flags &= ~(KVM_ARM64_PENDING_EXCEPTION | +KVM_ARM64_EXCEPT_MASK ); + shadow_vcpu->arch.flags |=KVM_ARM64_INCREMENT_PC ; + } + +/* If we were doing an MMIO read access, update the register*/ + rd_update = (shadow_vcpu->mmio_needed && + (host_flags &KVM_ARM64_INCREMENT_PC )); + rd_update &= !kvm_vcpu_dabt_iswrite (shadow_vcpu); + +if (rd_update) { +/* r0 as transfer register between the guest and the host. */ +u64 rd_val =READ_ONCE (host_vcpu->arch.ctxt.regs.regs[0 ]); +int rd =kvm_vcpu_dabt_get_rd (shadow_vcpu); + +vcpu_set_reg (shadow_vcpu, rd, rd_val); + } + + shadow_vcpu->mmio_needed = false; +} + +static void handle_pvm_exit_wfx (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) +{ +WRITE_ONCE (host_vcpu->arch.ctxt.regs.pstate, + shadow_vcpu->arch.ctxt.regs.pstate &PSR_MODE_MASK ); +WRITE_ONCE (host_vcpu->arch.fault.esr_el2, + shadow_vcpu->arch.fault.esr_el2); +} + +static void handle_pvm_exit_sys64 (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) +{ +u32 esr_el2 = shadow_vcpu->arch.fault.esr_el2; + +/* r0 as transfer register between the guest and the host. */ +WRITE_ONCE (host_vcpu->arch.fault.esr_el2, + esr_el2 & ~ESR_ELx_SYS64_ISS_RT_MASK); + +/* The mode is required for the host to emulate some sysregs */ +WRITE_ONCE (host_vcpu->arch.ctxt.regs.pstate, + shadow_vcpu->arch.ctxt.regs.pstate &PSR_MODE_MASK ); + +if (esr_sys64_to_params (esr_el2).is_write) { +int rt =kvm_vcpu_sys_get_rt (shadow_vcpu); +u64 rt_val =vcpu_get_reg (shadow_vcpu, rt); + +WRITE_ONCE (host_vcpu->arch.ctxt.regs.regs[0 ], rt_val); + } +} + +static void handle_pvm_exit_iabt (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) +{ +WRITE_ONCE (host_vcpu->arch.fault.esr_el2, + shadow_vcpu->arch.fault.esr_el2); +WRITE_ONCE (host_vcpu->arch.fault.hpfar_el2, + shadow_vcpu->arch.fault.hpfar_el2); +} + +static void handle_pvm_exit_dabt (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) +{ +/* +* For now, we treat all data aborts as MMIO since we have no knowledge +* of the memslot configuration at EL2. +*/ + shadow_vcpu->mmio_needed = true; + +if (shadow_vcpu->mmio_needed) { +/* r0 as transfer register between the guest and the host. */ +WRITE_ONCE (host_vcpu->arch.fault.esr_el2, + shadow_vcpu->arch.fault.esr_el2 & ~ESR_ELx_SRT_MASK); + +if (kvm_vcpu_dabt_iswrite (shadow_vcpu)) { +int rt =kvm_vcpu_dabt_get_rd (shadow_vcpu); +u64 rt_val =vcpu_get_reg (shadow_vcpu, rt); + +WRITE_ONCE (host_vcpu->arch.ctxt.regs.regs[0 ], rt_val); + } + }else { +WRITE_ONCE (host_vcpu->arch.fault.esr_el2, + shadow_vcpu->arch.fault.esr_el2 & ~ESR_ELx_ISV); + } + +WRITE_ONCE (host_vcpu->arch.ctxt.regs.pstate, + shadow_vcpu->arch.ctxt.regs.pstate &PSR_MODE_MASK ); +WRITE_ONCE (host_vcpu->arch.fault.far_el2, + shadow_vcpu->arch.fault.far_el2 &GENMASK (11 ,0 )); +WRITE_ONCE (host_vcpu->arch.fault.hpfar_el2, + shadow_vcpu->arch.fault.hpfar_el2); +WRITE_ONCE (__vcpu_sys_reg (host_vcpu,SCTLR_EL1 ), +__vcpu_sys_reg (shadow_vcpu,SCTLR_EL1 ) & (SCTLR_ELx_EE |SCTLR_EL1_E0E )); +} +static void handle_vm_entry_generic (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) {unsigned long host_flags =READ_ONCE (host_vcpu->arch.flags);
@@ -67,6 +270,22 @@ static void handle_vm_exit_abt(struct kvm_vcpu *host_vcpu, struct kvm_vcpu *shad shadow_vcpu->arch.fault.disr_el1); } +static const shadow_entry_exit_handler_fn entry_pvm_shadow_handlers[] = { + [0 ... ESR_ELx_EC_MAX] =NULL , + [ESR_ELx_EC_WFx] = handle_pvm_entry_wfx, + [ESR_ELx_EC_SYS64] = handle_pvm_entry_sys64, + [ESR_ELx_EC_IABT_LOW] = handle_pvm_entry_iabt, + [ESR_ELx_EC_DABT_LOW] = handle_pvm_entry_dabt, +}; + +static const shadow_entry_exit_handler_fn exit_pvm_shadow_handlers[] = { + [0 ... ESR_ELx_EC_MAX] =NULL , + [ESR_ELx_EC_WFx] = handle_pvm_exit_wfx, + [ESR_ELx_EC_SYS64] = handle_pvm_exit_sys64, + [ESR_ELx_EC_IABT_LOW] = handle_pvm_exit_iabt, + [ESR_ELx_EC_DABT_LOW] = handle_pvm_exit_dabt, +}; +static const shadow_entry_exit_handler_fn entry_vm_shadow_handlers[] = { [0 ... ESR_ELx_EC_MAX] = handle_vm_entry_generic, };
@@ -219,9 +438,13 @@ static void flush_shadow_state(struct kvm_shadow_vcpu_state *shadow_state)break ;case ARM_EXCEPTION_TRAP : esr_ec =ESR_ELx_EC (kvm_vcpu_get_esr (shadow_vcpu)); - ec_handler = entry_vm_shadow_handlers[esr_ec]; +if (shadow_state_is_protected (shadow_state)) + ec_handler = entry_pvm_shadow_handlers[esr_ec]; +else + ec_handler = entry_vm_shadow_handlers[esr_ec];if (ec_handler)ec_handler (host_vcpu, shadow_vcpu); +break ;default :BUG ();
@@ -251,7 +474,10 @@ static void sync_shadow_state(struct kvm_shadow_vcpu_state *shadow_state,break ;case ARM_EXCEPTION_TRAP : esr_ec =ESR_ELx_EC (kvm_vcpu_get_esr (shadow_vcpu)); - ec_handler = exit_vm_shadow_handlers[esr_ec]; +if (shadow_state_is_protected (shadow_state)) + ec_handler = exit_pvm_shadow_handlers[esr_ec]; +else + ec_handler = exit_vm_shadow_handlers[esr_ec];if (ec_handler)ec_handler (host_vcpu, shadow_vcpu);break ;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 68/89 KVM: arm64: Move vgic state between host and shadow vcpu structures
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
Since the world switch vgic code operates on the shadow data structure, move the state back and forth between the host and shadow vcpu.
This is currently limited to the VMCR and APR registers, but further patches will deal with the rest of the state.
Note that some of the scontrol settings (such as SRE) are always set to the same value. This will eventually be moved to the shadow initialisation.
--- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 65 ++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 4 deletions(-)
arch/arm64/kvm/hyp/nvhe/hyp-main.c +61 -4
@@ -619,6 +619,17 @@ static struct kvm_vcpu *__get_current_vcpu(struct kvm_vcpu *vcpu,__get_current_vcpu (__vcpu, statepp); \ }) +#define get_current_vcpu_from_cpu_if (ctxt, regnr, statepp) \ + ({ \ + DECLARE_REG(struct vgic_v3_cpu_if *, cif, ctxt, regnr); \ + struct kvm_vcpu *__vcpu; \ + __vcpu = container_of(cif, \ + struct kvm_vcpu, \ + arch.vgic_cpu.vgic_v3); \ + \ + __get_current_vcpu(__vcpu, statepp); \ + }) +static void handle___kvm_vcpu_run (struct kvm_cpu_context *host_ctxt) {struct kvm_shadow_vcpu_state *shadow_state;
@@ -778,16 +789,62 @@ static void handle___kvm_get_mdcr_el2(struct kvm_cpu_context *host_ctxt)static void handle___vgic_v3_save_vmcr_aprs (struct kvm_cpu_context *host_ctxt) { - DECLARE_REG(struct vgic_v3_cpu_if *, cpu_if, host_ctxt, 1); +struct kvm_shadow_vcpu_state *shadow_state; +struct kvm_vcpu *vcpu; + + vcpu =get_current_vcpu_from_cpu_if (host_ctxt,1 , &shadow_state); +if (!vcpu) +return ; + +if (shadow_state) { +struct vgic_v3_cpu_if *shadow_cpu_if, *cpu_if; +int i; + + shadow_cpu_if = &shadow_state->shadow_vcpu.arch.vgic_cpu.vgic_v3; +__vgic_v3_save_vmcr_aprs (shadow_cpu_if); + + cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; -__vgic_v3_save_vmcr_aprs (kern_hyp_va (cpu_if)); + cpu_if->vgic_vmcr = shadow_cpu_if->vgic_vmcr; +for (i =0 ; i <ARRAY_SIZE (cpu_if->vgic_ap0r); i++) { + cpu_if->vgic_ap0r[i] = shadow_cpu_if->vgic_ap0r[i]; + cpu_if->vgic_ap1r[i] = shadow_cpu_if->vgic_ap1r[i]; + } + }else { +__vgic_v3_save_vmcr_aprs (&vcpu->arch.vgic_cpu.vgic_v3); + } }static void handle___vgic_v3_restore_vmcr_aprs (struct kvm_cpu_context *host_ctxt) { - DECLARE_REG(struct vgic_v3_cpu_if *, cpu_if, host_ctxt, 1); +struct kvm_shadow_vcpu_state *shadow_state; +struct kvm_vcpu *vcpu; -__vgic_v3_restore_vmcr_aprs (kern_hyp_va (cpu_if)); + vcpu =get_current_vcpu_from_cpu_if (host_ctxt,1 , &shadow_state); +if (!vcpu) +return ; + +if (shadow_state) { +struct vgic_v3_cpu_if *shadow_cpu_if, *cpu_if; +int i; + + shadow_cpu_if = &shadow_state->shadow_vcpu.arch.vgic_cpu.vgic_v3; + cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; + + shadow_cpu_if->vgic_vmcr = cpu_if->vgic_vmcr; +/* Should be a one-off */ + shadow_cpu_if->vgic_sre = (ICC_SRE_EL1_DIB | +ICC_SRE_EL1_DFB | +ICC_SRE_EL1_SRE ); +for (i =0 ; i <ARRAY_SIZE (cpu_if->vgic_ap0r); i++) { + shadow_cpu_if->vgic_ap0r[i] = cpu_if->vgic_ap0r[i]; + shadow_cpu_if->vgic_ap1r[i] = cpu_if->vgic_ap1r[i]; + } + +__vgic_v3_restore_vmcr_aprs (shadow_cpu_if); + }else { +__vgic_v3_restore_vmcr_aprs (&vcpu->arch.vgic_cpu.vgic_v3); + } }static void handle___pkvm_init (struct kvm_cpu_context *host_ctxt)
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 69/89 KVM: arm64: Do not update virtual timer state for protected VMs
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
Protected vCPUs always run with a virtual counter offset of 0, so don't bother trying to update it from the host.
--- arch/arm64/kvm/arch_timer.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
arch/arm64/kvm/arch_timer.c +6 -1
@@ -88,7 +88,9 @@ static u64 timer_get_offset(struct arch_timer_context *ctxt)switch (arch_timer_ctx_index (ctxt)) {case TIMER_VTIMER : -return __vcpu_sys_reg (vcpu,CNTVOFF_EL2 ); +if (likely (!kvm_vm_is_protected (vcpu->kvm))) +return __vcpu_sys_reg (vcpu,CNTVOFF_EL2 ); + fallthrough;default :return 0 ; }
@@ -753,6 +755,9 @@ static void update_vtimer_cntvoff(struct kvm_vcpu *vcpu, u64 cntvoff)struct kvm *kvm = vcpu->kvm;struct kvm_vcpu *tmp; +if (unlikely (kvm_vm_is_protected (vcpu->kvm))) + cntvoff =0 ; +mutex_lock (&kvm->lock);kvm_for_each_vcpu (i, tmp, kvm)timer_set_offset (vcpu_vtimer (tmp), cntvoff);
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 70/89 KVM: arm64: Refactor kvm_vcpu_enable_ptrauth() for hyp use
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Move kvm_vcpu_enable_ptrauth() to a shared header to be used by hyp in protected mode.
No functional change intended.
--- arch/arm64/include/asm/kvm_emulate.h | 16 ++++++++++++++++ arch/arm64/kvm/reset.c | 16 ---------------- 2 files changed, 16 insertions(+), 16 deletions(-)
arch/arm64/include/asm/kvm_emulate.h +16 -0
@@ -43,6 +43,22 @@ void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr);void kvm_vcpu_wfi (struct kvm_vcpu *vcpu); +static inline int kvm_vcpu_enable_ptrauth (struct kvm_vcpu *vcpu) +{ +/* +* For now make sure that both address/generic pointer authentication +* features are requested by the userspace together and the system +* supports these capabilities. +*/ +if (!test_bit (KVM_ARM_VCPU_PTRAUTH_ADDRESS , vcpu->arch.features) || + !test_bit (KVM_ARM_VCPU_PTRAUTH_GENERIC , vcpu->arch.features) || + !system_has_full_ptr_auth ()) +return -EINVAL ; + + vcpu->arch.flags |=KVM_ARM64_GUEST_HAS_PTRAUTH ; +return 0 ; +} + static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu) {return !(vcpu->arch.hcr_el2 &HCR_RW );
arch/arm64/kvm/reset.c +0 -16
@@ -165,22 +165,6 @@ static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu)memset (vcpu->arch.sve_state,0 ,vcpu_sve_state_size (vcpu)); } -static int kvm_vcpu_enable_ptrauth (struct kvm_vcpu *vcpu) -{ -/* -* For now make sure that both address/generic pointer authentication -* features are requested by the userspace together and the system -* supports these capabilities. -*/ -if (!test_bit (KVM_ARM_VCPU_PTRAUTH_ADDRESS , vcpu->arch.features) || - !test_bit (KVM_ARM_VCPU_PTRAUTH_GENERIC , vcpu->arch.features) || - !system_has_full_ptr_auth ()) -return -EINVAL ; - - vcpu->arch.flags |=KVM_ARM64_GUEST_HAS_PTRAUTH ; -return 0 ; -} -static bool vcpu_allowed_register_width (struct kvm_vcpu *vcpu) {struct kvm_vcpu *tmp;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 71/89 KVM: arm64: Initialize shadow vm state at hyp
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Do not rely on the state of the vm as provided by the host, but initialize it instead at EL2 to a known good and safe state.
--- arch/arm64/kvm/hyp/nvhe/pkvm.c | 71 ++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+)
arch/arm64/kvm/hyp/nvhe/pkvm.c +71 -0
@@ -6,6 +6,9 @@#include <linux/kvm_host.h> #include <linux/mm.h> + +#include <asm/kvm_emulate.h> +#include <nvhe/mem_protect.h> #include <nvhe/memory.h> #include <nvhe/pkvm.h>
@@ -315,6 +318,53 @@ struct kvm_shadow_vcpu_state *pkvm_loaded_shadow_vcpu_state(void)return __this_cpu_read (loaded_shadow_state); } +/* Check and copy the supported features for the vcpu from the host. */ +static void copy_features (struct kvm_vcpu *shadow_vcpu,struct kvm_vcpu *host_vcpu) +{ +DECLARE_BITMAP (allowed_features,KVM_VCPU_MAX_FEATURES ); + +/* No restrictions for non-protected VMs. */ +if (!kvm_vm_is_protected (shadow_vcpu->kvm)) { +bitmap_copy (shadow_vcpu->arch.features, + host_vcpu->arch.features, +KVM_VCPU_MAX_FEATURES ); +return ; + } + +bitmap_zero (allowed_features,KVM_VCPU_MAX_FEATURES ); + +/* +* For protected vms, always allow: +* - CPU starting in poweroff state +* - PSCI v0.2 +*/ +set_bit (KVM_ARM_VCPU_POWER_OFF , allowed_features); +set_bit (KVM_ARM_VCPU_PSCI_0_2 , allowed_features); + +/* +* Check if remaining features are allowed: +* - Performance Monitoring +* - Scalable Vectors +* - Pointer Authentication +*/ +if (FIELD_GET (ARM64_FEATURE_MASK (ID_AA64DFR0_PMUVER ),PVM_ID_AA64DFR0_ALLOW )) +set_bit (KVM_ARM_VCPU_PMU_V3 , allowed_features); + +if (FIELD_GET (ARM64_FEATURE_MASK (ID_AA64PFR0_SVE ),PVM_ID_AA64PFR0_ALLOW )) +set_bit (KVM_ARM_VCPU_SVE , allowed_features); + +if (FIELD_GET (ARM64_FEATURE_MASK (ID_AA64ISAR1_API ),PVM_ID_AA64ISAR1_ALLOW ) && +FIELD_GET (ARM64_FEATURE_MASK (ID_AA64ISAR1_APA ),PVM_ID_AA64ISAR1_ALLOW )) +set_bit (KVM_ARM_VCPU_PTRAUTH_ADDRESS , allowed_features); + +if (FIELD_GET (ARM64_FEATURE_MASK (ID_AA64ISAR1_GPI ),PVM_ID_AA64ISAR1_ALLOW ) && +FIELD_GET (ARM64_FEATURE_MASK (ID_AA64ISAR1_GPA ),PVM_ID_AA64ISAR1_ALLOW )) +set_bit (KVM_ARM_VCPU_PTRAUTH_GENERIC , allowed_features); + +bitmap_and (shadow_vcpu->arch.features, host_vcpu->arch.features, + allowed_features,KVM_VCPU_MAX_FEATURES ); +} +static void unpin_host_vcpus (struct kvm_shadow_vcpu_state *shadow_vcpu_states,unsigned int nr_vcpus) {
@@ -350,6 +400,17 @@ static int set_host_vcpus(struct kvm_shadow_vcpu_state *shadow_vcpu_states,return 0 ; } +static int init_ptrauth (struct kvm_vcpu *shadow_vcpu) +{ +int ret =0 ; + +if (test_bit (KVM_ARM_VCPU_PTRAUTH_ADDRESS , shadow_vcpu->arch.features) || +test_bit (KVM_ARM_VCPU_PTRAUTH_GENERIC , shadow_vcpu->arch.features)) + ret =kvm_vcpu_enable_ptrauth (shadow_vcpu); + +return ret; +} + static int init_shadow_structs(struct kvm *kvm, struct kvm_shadow_vm *vm, struct kvm_vcpu **vcpu_array, int *last_ran,
@@ -357,10 +418,12 @@ static int init_shadow_structs(struct kvm *kvm, struct kvm_shadow_vm *vm, unsigned int nr_vcpus) {int i; +int ret; vm->host_kvm = kvm; vm->kvm.created_vcpus = nr_vcpus; vm->kvm.arch.vtcr = host_kvm.arch.vtcr; + vm->kvm.arch.pkvm.enabled =READ_ONCE (kvm->arch.pkvm.enabled); vm->kvm.arch.mmu.last_vcpu_ran = last_ran; vm->last_ran_size = last_ran_size;memset (vm->kvm.arch.mmu.last_vcpu_ran,-1 ,sizeof (int ) * hyp_nr_cpus);
@@ -377,8 +440,16 @@ static int init_shadow_structs(struct kvm *kvm, struct kvm_shadow_vm *vm, shadow_vcpu->vcpu_idx = i; shadow_vcpu->arch.hw_mmu = &vm->kvm.arch.mmu; + shadow_vcpu->arch.power_off = true; + +copy_features (shadow_vcpu, host_vcpu); + + ret =init_ptrauth (shadow_vcpu); +if (ret) +return ret;pkvm_vcpu_init_traps (shadow_vcpu, host_vcpu); +kvm_reset_pvm_sys_regs (shadow_vcpu); }return 0 ;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 72/89 KVM: arm64: Track the SVE state in the shadow vcpu
In reply to Will DeaconFrom: Marc Zyngier <[email protected]>
When dealing with a guest with SVE enabled, make sure the host SVE state is pinned at EL2 S1, and that the shadow state is correctly initialised (and then unpinned on teardown).
--- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 9 ++++---- arch/arm64/kvm/hyp/nvhe/pkvm.c | 33 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-)
arch/arm64/kvm/hyp/nvhe/hyp-main.c +5 -4
@@ -416,8 +416,7 @@ static void flush_shadow_state(struct kvm_shadow_vcpu_state *shadow_state)if (host_flags &KVM_ARM64_PKVM_STATE_DIRTY )__flush_vcpu_state (shadow_state); - shadow_vcpu->arch.sve_state =kern_hyp_va (host_vcpu->arch.sve_state); - shadow_vcpu->arch.sve_max_vl = host_vcpu->arch.sve_max_vl; + shadow_vcpu->arch.flags = host_flags; shadow_vcpu->arch.hcr_el2 =HCR_GUEST_FLAGS & ~(HCR_RW |HCR_TWI |HCR_TWE ); shadow_vcpu->arch.hcr_el2 |=READ_ONCE (host_vcpu->arch.hcr_el2);
@@ -488,8 +487,10 @@ static void sync_shadow_state(struct kvm_shadow_vcpu_state *shadow_state,BUG (); } - host_flags =READ_ONCE (host_vcpu->arch.flags) & - ~(KVM_ARM64_PENDING_EXCEPTION |KVM_ARM64_INCREMENT_PC ); + host_flags = shadow_vcpu->arch.flags; +if (shadow_state_is_protected (shadow_state)) + host_flags &= ~(KVM_ARM64_PENDING_EXCEPTION |KVM_ARM64_INCREMENT_PC ); +WRITE_ONCE (host_vcpu->arch.flags, host_flags); shadow_state->exit_code = exit_reason; }
arch/arm64/kvm/hyp/nvhe/pkvm.c +33 -0
@@ -372,7 +372,19 @@ static void unpin_host_vcpus(struct kvm_shadow_vcpu_state *shadow_vcpu_states,for (i =0 ; i < nr_vcpus; i++) {struct kvm_vcpu *host_vcpu = shadow_vcpu_states[i].host_vcpu; +struct kvm_vcpu *shadow_vcpu = &shadow_vcpu_states[i].shadow_vcpu; +size_t sve_state_size; +void *sve_state; +hyp_unpin_shared_mem (host_vcpu, host_vcpu +1 ); + +if (!test_bit (KVM_ARM_VCPU_SVE , shadow_vcpu->arch.features)) +continue ; + + sve_state = shadow_vcpu->arch.sve_state; + sve_state =kern_hyp_va (sve_state); + sve_state_size =vcpu_sve_state_size (shadow_vcpu); +hyp_unpin_shared_mem (sve_state, sve_state + sve_state_size); } }
@@ -448,6 +460,27 @@ static int init_shadow_structs(struct kvm *kvm, struct kvm_shadow_vm *vm,if (ret)return ret; +if (test_bit (KVM_ARM_VCPU_SVE , shadow_vcpu->arch.features)) { +size_t sve_state_size; +void *sve_state; + + shadow_vcpu->arch.sve_state =READ_ONCE (host_vcpu->arch.sve_state); + shadow_vcpu->arch.sve_max_vl =READ_ONCE (host_vcpu->arch.sve_max_vl); + + sve_state =kern_hyp_va (shadow_vcpu->arch.sve_state); + sve_state_size =vcpu_sve_state_size (shadow_vcpu); + +if (!shadow_vcpu->arch.sve_state || !sve_state_size || +hyp_pin_shared_mem (sve_state, + sve_state + sve_state_size)) { +clear_bit (KVM_ARM_VCPU_SVE , + shadow_vcpu->arch.features); + shadow_vcpu->arch.sve_state =NULL ; + shadow_vcpu->arch.sve_max_vl =0 ; +return -EINVAL ; + } + } +pkvm_vcpu_init_traps (shadow_vcpu, host_vcpu);kvm_reset_pvm_sys_regs (shadow_vcpu); }
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 73/89 KVM: arm64: Add HVC handling for protected guests at EL2
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Rather than forwarding guest hypercalls back to the host for handling, implement some basic handling at EL2 which will later be extending to provide additional functionality such as PSCI.
--- arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 2 ++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 24 ++++++++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/pkvm.c | 22 ++++++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/switch.c | 1 + 4 files changed, 49 insertions(+)
arch/arm64/kvm/hyp/include/nvhe/pkvm.h +2 -0
@@ -101,4 +101,6 @@ bool kvm_handle_pvm_restricted(struct kvm_vcpu *vcpu, u64 *exit_code);void kvm_reset_pvm_sys_regs (struct kvm_vcpu *vcpu);int kvm_check_pvm_sysreg_table (void ); +bool kvm_handle_pvm_hvc64 (struct kvm_vcpu *vcpu,u64 *exit_code); +#endif /* __ARM64_KVM_NVHE_PKVM_H__ */
arch/arm64/kvm/hyp/nvhe/hyp-main.c +24 -0
@@ -4,6 +4,8 @@* Author: Andrew Scull <[email protected]> */ +#include <kvm/arm_hypercalls.h> +#include <hyp/adjust_pc.h> #include <asm/pgtable-types.h>
@@ -42,6 +44,13 @@ static void handle_pvm_entry_wfx(struct kvm_vcpu *host_vcpu, struct kvm_vcpu *shKVM_ARM64_INCREMENT_PC ; } +static void handle_pvm_entry_hvc64 (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) +{ +u64 ret =READ_ONCE (host_vcpu->arch.ctxt.regs.regs[0 ]); + +vcpu_set_reg (shadow_vcpu,0 , ret); +} +static void handle_pvm_entry_sys64 (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) {unsigned long host_flags;
@@ -195,6 +204,19 @@ static void handle_pvm_exit_sys64(struct kvm_vcpu *host_vcpu, struct kvm_vcpu *s } } +static void handle_pvm_exit_hvc64 (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) +{ +int i; + +WRITE_ONCE (host_vcpu->arch.fault.esr_el2, + shadow_vcpu->arch.fault.esr_el2); + +/* Pass the hvc function id (r0) as well as any potential arguments. */ +for (i =0 ; i <8 ; i++) +WRITE_ONCE (host_vcpu->arch.ctxt.regs.regs[i], +vcpu_get_reg (shadow_vcpu, i)); +} +static void handle_pvm_exit_iabt (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) { WRITE_ONCE(host_vcpu->arch.fault.esr_el2,
@@ -273,6 +295,7 @@ static void handle_vm_exit_abt(struct kvm_vcpu *host_vcpu, struct kvm_vcpu *shadstatic const shadow_entry_exit_handler_fn entry_pvm_shadow_handlers[] = { [0 ... ESR_ELx_EC_MAX] =NULL , [ESR_ELx_EC_WFx] = handle_pvm_entry_wfx, + [ESR_ELx_EC_HVC64] = handle_pvm_entry_hvc64, [ESR_ELx_EC_SYS64] = handle_pvm_entry_sys64, [ESR_ELx_EC_IABT_LOW] = handle_pvm_entry_iabt, [ESR_ELx_EC_DABT_LOW] = handle_pvm_entry_dabt,
@@ -281,6 +304,7 @@ static const shadow_entry_exit_handler_fn entry_pvm_shadow_handlers[] = {static const shadow_entry_exit_handler_fn exit_pvm_shadow_handlers[] = { [0 ... ESR_ELx_EC_MAX] =NULL , [ESR_ELx_EC_WFx] = handle_pvm_exit_wfx, + [ESR_ELx_EC_HVC64] = handle_pvm_exit_hvc64, [ESR_ELx_EC_SYS64] = handle_pvm_exit_sys64, [ESR_ELx_EC_IABT_LOW] = handle_pvm_exit_iabt, [ESR_ELx_EC_DABT_LOW] = handle_pvm_exit_dabt,
arch/arm64/kvm/hyp/nvhe/pkvm.c +22 -0
@@ -7,6 +7,8 @@#include <linux/kvm_host.h> #include <linux/mm.h> +#include <kvm/arm_hypercalls.h> +#include <asm/kvm_emulate.h> #include <nvhe/mem_protect.h>
@@ -797,3 +799,23 @@ int __pkvm_teardown_shadow(unsigned int shadow_handle)hyp_spin_unlock (&shadow_lock);return err; } + +/* +* Handler for protected VM HVC calls. +* +* Returns true if the hypervisor has handled the exit, and control should go +* back to the guest, or false if it hasn't. +*/ +bool kvm_handle_pvm_hvc64 (struct kvm_vcpu *vcpu,u64 *exit_code) +{ +u32 fn =smccc_get_function (vcpu); + +switch (fn) { +case ARM_SMCCC_VERSION_FUNC_ID : +/* Nothing to be handled by the host. Go back to the guest. */ +smccc_set_retval (vcpu,ARM_SMCCC_VERSION_1_1 ,0 ,0 ,0 ); +return true; +default : +return false; + } +}
arch/arm64/kvm/hyp/nvhe/switch.c +1 -0
@@ -205,6 +205,7 @@ static const exit_handler_fn hyp_exit_handlers[] = {static const exit_handler_fn pvm_exit_handlers[] = { [0 ... ESR_ELx_EC_MAX] =NULL , + [ESR_ELx_EC_HVC64] = kvm_handle_pvm_hvc64, [ESR_ELx_EC_SYS64] = kvm_handle_pvm_sys64, [ESR_ELx_EC_SVE] = kvm_handle_pvm_restricted, [ESR_ELx_EC_FP_ASIMD] = kvm_handle_pvm_fpsimd,
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 74/89 KVM: arm64: Move pstate reset values to kvm_arm.h
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Move the macro defines of the pstate reset values to a shared header to be used by hyp in protected mode.
No functional change intended.
--- arch/arm64/include/asm/kvm_arm.h | 9 +++++++++ arch/arm64/kvm/reset.c | 9 --------- 2 files changed, 9 insertions(+), 9 deletions(-)
arch/arm64/include/asm/kvm_arm.h +9 -0
@@ -359,4 +359,13 @@#define CPACR_EL1_DEFAULT (CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN |\ CPACR_EL1_ZEN_EL1EN) +/* +* ARMv8 Reset Values +*/ +#define VCPU_RESET_PSTATE_EL1 (PSR_MODE_EL1h | PSR_A_BIT | PSR_I_BIT | \ + PSR_F_BIT | PSR_D_BIT) + +#define VCPU_RESET_PSTATE_SVC (PSR_AA32_MODE_SVC | PSR_AA32_A_BIT | \ + PSR_AA32_I_BIT | PSR_AA32_F_BIT) +#endif /* __ARM64_KVM_ARM_H__ */
arch/arm64/kvm/reset.c +0 -9
@@ -32,15 +32,6 @@/* Maximum phys_shift supported for any VM on this host */ static u32 kvm_ipa_limit; -/* -* ARMv8 Reset Values -*/ -#define VCPU_RESET_PSTATE_EL1 (PSR_MODE_EL1h | PSR_A_BIT | PSR_I_BIT | \ - PSR_F_BIT | PSR_D_BIT) - -#define VCPU_RESET_PSTATE_SVC (PSR_AA32_MODE_SVC | PSR_AA32_A_BIT | \ - PSR_AA32_I_BIT | PSR_AA32_F_BIT) -unsigned int kvm_sve_max_vl;int kvm_arm_init_sve (void )
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 75/89 KVM: arm64: Move some kvm_psci functions to a shared header
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Move some PSCI functions and macros to a shared header to be used by hyp in protected mode.
No functional change intended.
--- arch/arm64/include/asm/kvm_emulate.h | 30 ++++++++++++++++++++++++++++ arch/arm64/kvm/psci.c | 28 -------------------------- 2 files changed, 30 insertions(+), 28 deletions(-)
arch/arm64/include/asm/kvm_emulate.h +30 -0
@@ -492,4 +492,34 @@ static inline bool vcpu_has_feature(struct kvm_vcpu *vcpu, int feature) return test_bit(feature, vcpu->arch.features); } +/* Narrow the PSCI register arguments (r1 to r3) to 32 bits. */ +static inline void kvm_psci_narrow_to_32bit (struct kvm_vcpu *vcpu) +{ +int i; + +/* +* Zero the input registers' upper 32 bits. They will be fully +* zeroed on exit, so we're fine changing them in place. +*/ +for (i =1 ; i <4 ; i++) +vcpu_set_reg (vcpu, i,lower_32_bits (vcpu_get_reg (vcpu, i))); +} + +static inline bool kvm_psci_valid_affinity (struct kvm_vcpu *vcpu, +unsigned long affinity) +{ +return !(affinity & ~MPIDR_HWID_BITMASK ); +} + + +#define AFFINITY_MASK (level) ~((0x1UL << ((level) * MPIDR_LEVEL_BITS)) - 1) + +static inline unsigned long psci_affinity_mask (unsigned long affinity_level) +{ +if (affinity_level <=3 ) +return MPIDR_HWID_BITMASK &AFFINITY_MASK (affinity_level); + +return 0 ; +} +#endif /* __ARM64_KVM_EMULATE_H__ */
arch/arm64/kvm/psci.c +0 -28
@@ -21,16 +21,6 @@* as described in ARM document number ARM DEN 0022A. */ -#define AFFINITY_MASK (level) ~((0x1UL << ((level) * MPIDR_LEVEL_BITS)) - 1) - -static unsigned long psci_affinity_mask (unsigned long affinity_level) -{ -if (affinity_level <=3 ) -return MPIDR_HWID_BITMASK &AFFINITY_MASK (affinity_level); - -return 0 ; -} -static unsigned long kvm_psci_vcpu_suspend (struct kvm_vcpu *vcpu) {/*
@@ -58,12 +48,6 @@ static void kvm_psci_vcpu_off(struct kvm_vcpu *vcpu)kvm_vcpu_kick (vcpu); } -static inline bool kvm_psci_valid_affinity (struct kvm_vcpu *vcpu, -unsigned long affinity) -{ -return !(affinity & ~MPIDR_HWID_BITMASK ); -} -static unsigned long kvm_psci_vcpu_on (struct kvm_vcpu *source_vcpu) {struct vcpu_reset_state *reset_state;
@@ -201,18 +185,6 @@ static void kvm_psci_system_reset2(struct kvm_vcpu *vcpu) KVM_SYSTEM_EVENT_RESET_FLAG_PSCI_RESET2); } -static void kvm_psci_narrow_to_32bit (struct kvm_vcpu *vcpu) -{ -int i; - -/* -* Zero the input registers' upper 32 bits. They will be fully -* zeroed on exit, so we're fine changing them in place. -*/ -for (i =1 ; i <4 ; i++) -vcpu_set_reg (vcpu, i,lower_32_bits (vcpu_get_reg (vcpu, i))); -} -static unsigned long kvm_psci_check_allowed_function (struct kvm_vcpu *vcpu,u32 fn) {switch (fn) {
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 76/89 KVM: arm64: Factor out vcpu_reset code for core registers and PSCI
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Factor out logic that resets a vcpu's core registers, including additional PSCI handling. This code will be reused when resetting VMs in protected mode.
--- arch/arm64/include/asm/kvm_emulate.h | 41 +++++++++++++++++++++++++ arch/arm64/kvm/reset.c | 45 +++++----------------------- 2 files changed, 48 insertions(+), 38 deletions(-)
arch/arm64/include/asm/kvm_emulate.h +41 -0
@@ -522,4 +522,45 @@ static inline unsigned long psci_affinity_mask(unsigned long affinity_level) return 0; } +/* Reset a vcpu's core registers. */ +static inline void kvm_reset_vcpu_core (struct kvm_vcpu *vcpu) +{ +u32 pstate; + +if (test_bit (KVM_ARM_VCPU_EL1_32BIT , vcpu->arch.features)) { + pstate =VCPU_RESET_PSTATE_SVC ; + }else { + pstate =VCPU_RESET_PSTATE_EL1 ; + } + +/* Reset core registers */ +memset (vcpu_gp_regs (vcpu),0 ,sizeof (*vcpu_gp_regs (vcpu))); +memset (&vcpu->arch.ctxt.fp_regs,0 ,sizeof (vcpu->arch.ctxt.fp_regs)); + vcpu->arch.ctxt.spsr_abt =0 ; + vcpu->arch.ctxt.spsr_und =0 ; + vcpu->arch.ctxt.spsr_irq =0 ; + vcpu->arch.ctxt.spsr_fiq =0 ; +vcpu_gp_regs (vcpu)->pstate = pstate; +} + +/* PSCI reset handling for a vcpu. */ +static inline void kvm_reset_vcpu_psci (struct kvm_vcpu *vcpu, +struct vcpu_reset_state *reset_state) +{ +unsigned long target_pc = reset_state->pc; + +/* Gracefully handle Thumb2 entry point */ +if (vcpu_mode_is_32bit (vcpu) && (target_pc &1 )) { + target_pc &= ~1UL ; +vcpu_set_thumb (vcpu); + } + +/* Propagate caller endianness */ +if (reset_state->be) +kvm_vcpu_set_be (vcpu); + + *vcpu_pc (vcpu) = target_pc; +vcpu_set_reg (vcpu,0 , reset_state->r0); +} +#endif /* __ARM64_KVM_EMULATE_H__ */
arch/arm64/kvm/reset.c +7 -38
72 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 77/89 KVM: arm64: Handle PSCI for protected VMs in EL2
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Add PSCI 1.1 support for protected VMs at EL2.
--- arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 13 + arch/arm64/kvm/hyp/nvhe/hyp-main.c | 69 +++++- arch/arm64/kvm/hyp/nvhe/pkvm.c | 320 ++++++++++++++++++++++++- 3 files changed, 399 insertions(+), 3 deletions(-)
arch/arm64/kvm/hyp/include/nvhe/pkvm.h +13 -0
@@ -28,6 +28,15 @@ struct kvm_shadow_vcpu_state {/* Tracks exit code for the protected guest. */ u32 exit_code; +/* +* Track the power state transition of a protected vcpu. +* Can be in one of three states: +* PSCI_0_2_AFFINITY_LEVEL_ON +* PSCI_0_2_AFFINITY_LEVEL_OFF +* PSCI_0_2_AFFINITY_LEVEL_PENDING +*/ +int power_state; +/* * Points to the per-cpu pointer of the cpu where it's loaded, or NULL * if not loaded.
@@ -101,6 +110,10 @@ bool kvm_handle_pvm_restricted(struct kvm_vcpu *vcpu, u64 *exit_code);void kvm_reset_pvm_sys_regs (struct kvm_vcpu *vcpu);int kvm_check_pvm_sysreg_table (void ); +void pkvm_reset_vcpu (struct kvm_shadow_vcpu_state *shadow_state); +bool kvm_handle_pvm_hvc64 (struct kvm_vcpu *vcpu,u64 *exit_code); +struct kvm_shadow_vcpu_state *pkvm_mpidr_to_vcpu_state (struct kvm_shadow_vm *vm,unsigned long mpidr); +#endif /* __ARM64_KVM_NVHE_PKVM_H__ */
arch/arm64/kvm/hyp/nvhe/hyp-main.c +67 -2
100 lines not on this page · raw message
arch/arm64/kvm/hyp/nvhe/pkvm.c +319 -1
349 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 78/89 KVM: arm64: Don't expose TLBI hypercalls after de-privilege
In reply to Will DeaconNow that TLBI invalidation is handled entirely at EL2 for both protected and non-protected guests when protected KVM has initialised, unplug the unused TLBI hypercalls.
--- arch/arm64/include/asm/kvm_asm.h | 8 ++++---- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-)
arch/arm64/include/asm/kvm_asm.h +4 -4
@@ -59,6 +59,10 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___kvm_enable_ssbs, __KVM_HOST_SMCCC_FUNC___vgic_v3_init_lrs, __KVM_HOST_SMCCC_FUNC___vgic_v3_get_gic_config, + __KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context, + __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_ipa, + __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid, + __KVM_HOST_SMCCC_FUNC___kvm_flush_cpu_context, __KVM_HOST_SMCCC_FUNC___pkvm_prot_finalize, /* Hypercalls available after pKVM finalisation */
@@ -68,10 +72,6 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___pkvm_host_map_guest, __KVM_HOST_SMCCC_FUNC___kvm_adjust_pc, __KVM_HOST_SMCCC_FUNC___kvm_vcpu_run, - __KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context, - __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_ipa, - __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid, - __KVM_HOST_SMCCC_FUNC___kvm_flush_cpu_context, __KVM_HOST_SMCCC_FUNC___kvm_timer_set_cntvoff, __KVM_HOST_SMCCC_FUNC___vgic_v3_save_vmcr_aprs, __KVM_HOST_SMCCC_FUNC___vgic_v3_restore_vmcr_aprs,
arch/arm64/kvm/hyp/nvhe/hyp-main.c +4 -4
@@ -1030,6 +1030,10 @@ static const hcall_t host_hcall[] = {HANDLE_FUNC (__kvm_enable_ssbs),HANDLE_FUNC (__vgic_v3_init_lrs),HANDLE_FUNC (__vgic_v3_get_gic_config), +HANDLE_FUNC (__kvm_flush_vm_context), +HANDLE_FUNC (__kvm_tlb_flush_vmid_ipa), +HANDLE_FUNC (__kvm_tlb_flush_vmid), +HANDLE_FUNC (__kvm_flush_cpu_context),HANDLE_FUNC (__pkvm_prot_finalize), HANDLE_FUNC(__pkvm_host_share_hyp),
@@ -1038,10 +1042,6 @@ static const hcall_t host_hcall[] = {HANDLE_FUNC (__pkvm_host_map_guest),HANDLE_FUNC (__kvm_adjust_pc),HANDLE_FUNC (__kvm_vcpu_run), -HANDLE_FUNC (__kvm_flush_vm_context), -HANDLE_FUNC (__kvm_tlb_flush_vmid_ipa), -HANDLE_FUNC (__kvm_tlb_flush_vmid), -HANDLE_FUNC (__kvm_flush_cpu_context),HANDLE_FUNC (__kvm_timer_set_cntvoff),HANDLE_FUNC (__vgic_v3_save_vmcr_aprs), HANDLE_FUNC(__vgic_v3_restore_vmcr_aprs),
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 79/89 KVM: arm64: Add is_pkvm_initialized() helper
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
Add a helper allowing to check when the pkvm static key is enabled to ease the introduction of pkvm hooks in other parts of the code.
--- arch/arm64/include/asm/virt.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
arch/arm64/include/asm/virt.h +8 -4
@@ -74,6 +74,12 @@ void __hyp_reset_vectors(void);DECLARE_STATIC_KEY_FALSE (kvm_protected_mode_initialized); +static inline bool is_pkvm_initialized (void ) +{ +return IS_ENABLED (CONFIG_KVM ) && +static_branch_likely (&kvm_protected_mode_initialized); +} +/* Reports the availability of HYP mode */ static inline bool is_hyp_mode_available (void ) {
@@ -81,8 +87,7 @@ static inline bool is_hyp_mode_available(void)* If KVM protected mode is initialized, all CPUs must have been booted * in EL2. Avoid checking __boot_cpu_mode as CPUs now come up in EL1. */ -if (IS_ENABLED (CONFIG_KVM ) && -static_branch_likely (&kvm_protected_mode_initialized)) +if (is_pkvm_initialized ())return true; return (__boot_cpu_mode[0] == BOOT_CPU_MODE_EL2 &&
@@ -96,8 +101,7 @@ static inline bool is_hyp_mode_mismatched(void)* If KVM protected mode is initialized, all CPUs must have been booted * in EL2. Avoid checking __boot_cpu_mode as CPUs now come up in EL1. */ -if (IS_ENABLED (CONFIG_KVM ) && -static_branch_likely (&kvm_protected_mode_initialized)) +if (is_pkvm_initialized ())return false;return __boot_cpu_mode[0 ] != __boot_cpu_mode[1 ];
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 80/89 KVM: arm64: Refactor enter_exception64()
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
In order to simplify the injection of exceptions in the host in pkvm context, let's factor out of enter_exception64() the code calculating the exception offset from VBAR_EL1 and the cpsr.
--- arch/arm64/include/asm/kvm_emulate.h | 5 ++ arch/arm64/kvm/hyp/exception.c | 89 ++++++++++++++++------------ 2 files changed, 57 insertions(+), 37 deletions(-)
arch/arm64/include/asm/kvm_emulate.h +5 -0
@@ -41,6 +41,11 @@ void kvm_inject_vabt(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); +unsigned long get_except64_offset (unsigned long psr,unsigned long target_mode, +enum exception_type type); +unsigned long get_except64_cpsr (unsigned long old,bool has_mte, +unsigned long sctlr,unsigned long mode); +void kvm_vcpu_wfi (struct kvm_vcpu *vcpu);static inline int kvm_vcpu_enable_ptrauth (struct kvm_vcpu *vcpu)
arch/arm64/kvm/hyp/exception.c +52 -37
117 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 81/89 KVM: arm64: Inject SIGSEGV on illegal accesses
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
The pKVM hypervisor will currently panic if the host tries to access memory that it doesn't own (e.g. protected guest memory). Sadly, as guest memory can still be mapped into the VMM's address space, userspace can trivially crash the kernel/hypervisor by poking into guest memory.
To prevent this, inject the abort back in the host with S1PTW set in the ESR, hence allowing the host to differentiate this abort from normal userspace faults and inject a SIGSEGV cleanly.
--- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 50 ++++++++++++++++++++++++++- arch/arm64/mm/fault.c | 22 ++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-)
arch/arm64/kvm/hyp/nvhe/mem_protect.c +49 -1
@@ -549,6 +549,50 @@ static int host_stage2_idmap(u64 addr)return ret; } +static void host_inject_abort (struct kvm_cpu_context *host_ctxt) +{ +u64 spsr =read_sysreg_el2 (SYS_SPSR ); +u64 esr =read_sysreg_el2 (SYS_ESR ); +u64 ventry, ec; + +/* Repaint the ESR to report a same-level fault if taken from EL1 */ +if ((spsr &PSR_MODE_MASK ) != PSR_MODE_EL0t) { + ec =ESR_ELx_EC (esr); +if (ec == ESR_ELx_EC_DABT_LOW) + ec = ESR_ELx_EC_DABT_CUR; +else if (ec == ESR_ELx_EC_IABT_LOW) + ec = ESR_ELx_EC_IABT_CUR; +else +WARN_ON (1 ); + esr &= ~ESR_ELx_EC_MASK; + esr |= ec << ESR_ELx_EC_SHIFT; + } + +/* +* Since S1PTW should only ever be set for stage-2 faults, we're pretty +* much guaranteed that it won't be set in ESR_EL1 by the hardware. So, +* let's use that bit to allow the host abort handler to differentiate +* this abort from normal userspace faults. +* +* Note: although S1PTW is RES0 at EL1, it is guaranteed by the +* architecture to be backed by flops, so it should be safe to use. +*/ + esr |= ESR_ELx_S1PTW; + +write_sysreg_el1 (esr,SYS_ESR ); +write_sysreg_el1 (spsr,SYS_SPSR ); +write_sysreg_el1 (read_sysreg_el2 (SYS_ELR ),SYS_ELR ); +write_sysreg_el1 (read_sysreg_el2 (SYS_FAR ),SYS_FAR ); + + ventry =read_sysreg_el1 (SYS_VBAR ); + ventry +=get_except64_offset (spsr, PSR_MODE_EL1h, except_type_sync); +write_sysreg_el2 (ventry,SYS_ELR ); + + spsr =get_except64_cpsr (spsr,system_supports_mte (), +read_sysreg_el1 (SYS_SCTLR ), PSR_MODE_EL1h); +write_sysreg_el2 (spsr,SYS_SPSR ); +} +void handle_host_mem_abort (struct kvm_cpu_context *host_ctxt) {struct kvm_vcpu_fault_info fault;
@@ -560,7 +604,11 @@ void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt) addr = (fault.hpfar_el2 &HPFAR_MASK ) <<8 ; ret =host_stage2_idmap (addr); -BUG_ON (ret && ret != -EAGAIN ); + +if (ret == -EPERM ) +host_inject_abort (host_ctxt); +else +BUG_ON (ret && ret != -EAGAIN ); }struct pkvm_mem_transition {
arch/arm64/mm/fault.c +22 -0
@@ -41,6 +41,7 @@#include <asm/system_misc.h> #include <asm/tlbflush.h> #include <asm/traps.h> +#include <asm/virt.h> struct fault_info { int (*fn)(unsigned long far, unsigned int esr,
@@ -257,6 +258,15 @@ static inline bool is_el1_permission_fault(unsigned long addr, unsigned int esr,return false; } +static bool is_pkvm_stage2_abort (unsigned int esr) +{ +/* +* S1PTW should only ever be set in ESR_EL1 if the pkvm hypervisor +* injected a stage-2 abort -- see host_inject_abort(). +*/ +return is_pkvm_initialized () && (esr & ESR_ELx_S1PTW); +} +static bool __kprobes is_spurious_el1_translation_fault (unsigned long addr,unsigned int esr,struct pt_regs *regs)
@@ -268,6 +278,9 @@ static bool __kprobes is_spurious_el1_translation_fault(unsigned long addr, (esr & ESR_ELx_FSC_TYPE) != ESR_ELx_FSC_FAULT)return false; +if (is_pkvm_stage2_abort (esr)) +return false; +local_irq_save (flags); asmvolatile ("at s1e1r, %0" ::"r" (addr));isb ();
@@ -383,6 +396,8 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr, msg ="read from unreadable memory" ; }else if (addr <PAGE_SIZE ) { msg ="NULL pointer dereference" ; + }else if (is_pkvm_stage2_abort (esr)) { + msg ="access to hypervisor-protected memory" ; }else {if (kfence_handle_page_fault (addr, esr & ESR_ELx_WNR, regs))return ;
@@ -572,6 +587,13 @@ static int __kprobes do_page_fault(unsigned long far, unsigned int esr, addr, esr, regs); } +if (is_pkvm_stage2_abort (esr)) { +if (!user_mode (regs)) + goto no_context; +arm64_force_sig_fault (SIGSEGV ,SEGV_ACCERR , far,"stage-2 fault" ); +return 0 ; + } +perf_sw_event (PERF_COUNT_SW_PAGE_FAULTS ,1 , regs, addr);/*
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 82/89 KVM: arm64: Support TLB invalidation in guest context
In reply to Will DeaconTypically, TLB invalidation of guest stage-2 mappings using nVHE is performed by a hypercall originating from the host. For the invalidation instruction to be effective, therefore, __tlb_switch_to_{guest,host}() swizzle the active stage-2 context around the TLBI instruction.
With guest-to-host memory sharing and unsharing hypercalls originating from the guest under pKVM, there is now a need to support both guest and host VMID invalidations issued from guest context.
Replace the __tlb_switch_to_{guest,host}() functions with a more general {enter,exit}_vmid_context() implementation which supports being invoked from guest context and acts as a no-op if the target context matches the running context.
--- arch/arm64/kvm/hyp/nvhe/tlb.c | 96 ++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 18 deletions(-)
arch/arm64/kvm/hyp/nvhe/tlb.c +78 -18
@@ -11,26 +11,62 @@#include <nvhe/mem_protect.h> struct tlb_inv_context { -u64 tcr; +struct kvm_s2_mmu *mmu; +u64 tcr; +u64 sctlr; }; -static void __tlb_switch_to_guest (struct kvm_s2_mmu *mmu, -struct tlb_inv_context *cxt) +static void enter_vmid_context (struct kvm_s2_mmu *mmu, +struct tlb_inv_context *cxt) { +struct kvm_s2_mmu *host_mmu = &host_kvm.arch.mmu; +struct kvm_cpu_context *host_ctxt; +struct kvm_vcpu *vcpu; + + host_ctxt = &this_cpu_ptr (&kvm_host_data)->host_ctxt; + vcpu = host_ctxt->__hyp_running_vcpu; + cxt->mmu =NULL ; + +/* +* If we're already in the desired context, then there's nothing +* to do. +*/ +if (vcpu) { +if (mmu == vcpu->arch.hw_mmu ||WARN_ON (mmu != host_mmu)) +return ; + }else if (mmu == host_mmu) { +return ; + } + + cxt->mmu = mmu;if (cpus_have_final_cap (ARM64_WORKAROUND_SPECULATIVE_AT )) {u64 val;/* * For CPUs that are affected by ARM 1319367, we need to -* avoid a host Stage-1 walk while we have the guest's -* VMID set in the VTTBR in order to invalidate TLBs. -* We're guaranteed that the S1 MMU is enabled, so we can -* simply set the EPD bits to avoid any further TLB fill. +* avoid a Stage-1 walk with the old VMID while we have +* the new VMID set in the VTTBR in order to invalidate TLBs. +* We're guaranteed that the host S1 MMU is enabled, so +* we can simply set the EPD bits to avoid any further +* TLB fill. For guests, we ensure that the S1 MMU is +* temporarily enabled in the next context. */ val = cxt->tcr =read_sysreg_el1 (SYS_TCR ); val |=TCR_EPD1_MASK |TCR_EPD0_MASK ;write_sysreg_el1 (val,SYS_TCR );isb (); + +if (vcpu) { + val = cxt->sctlr =read_sysreg_el1 (SYS_SCTLR ); +if (!(val & SCTLR_ELx_M)) { + val |= SCTLR_ELx_M; +write_sysreg_el1 (val,SYS_SCTLR ); +isb (); + } + }else { +/* The host S1 MMU is always enabled. */ + cxt->sctlr = SCTLR_ELx_M; + } }/*
@@ -39,20 +75,44 @@ static void __tlb_switch_to_guest(struct kvm_s2_mmu *mmu,* ensuring that we always have an ISB, but not two ISBs back * to back. */ -__load_stage2 (mmu,kern_hyp_va (mmu->arch)); +if (vcpu) +__load_host_stage2 (); +else +__load_stage2 (mmu,kern_hyp_va (mmu->arch)); + asm(ALTERNATIVE("isb", "nop", ARM64_WORKAROUND_SPECULATIVE_AT)); } -static void __tlb_switch_to_host (struct tlb_inv_context *cxt) +static void exit_vmid_context (struct tlb_inv_context *cxt) { -__load_host_stage2 (); +struct kvm_s2_mmu *mmu = cxt->mmu; +struct kvm_cpu_context *host_ctxt; +struct kvm_vcpu *vcpu; + + host_ctxt = &this_cpu_ptr (&kvm_host_data)->host_ctxt; + vcpu = host_ctxt->__hyp_running_vcpu; + +if (!mmu) +return ; + +if (vcpu) +__load_stage2 (mmu,kern_hyp_va (mmu->arch)); +else +__load_host_stage2 ();if (cpus_have_final_cap (ARM64_WORKAROUND_SPECULATIVE_AT )) { -/* Ensure write of the host VMID */ +/* Ensure write of the old VMID */ isb (); -/* Restore the host's TCR_EL1 */ + +if (!(cxt->sctlr & SCTLR_ELx_M)) { +write_sysreg_el1 (cxt->sctlr,SYS_SCTLR ); +isb (); + } +write_sysreg_el1 (cxt->tcr,SYS_TCR ); } + + cxt->mmu =NULL ; } void __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu,
@@ -63,7 +123,7 @@ void __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu,dsb (ishst);/* Switch to requested VMID */ -__tlb_switch_to_guest (mmu, &cxt); +enter_vmid_context (mmu, &cxt);/* * We could do so much better if we had the VA as well.
@@ -106,7 +166,7 @@ void __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu,if (icache_is_vpipt ())icache_inval_all_pou (); -__tlb_switch_to_host (&cxt); +exit_vmid_context (&cxt); }void __kvm_tlb_flush_vmid (struct kvm_s2_mmu *mmu)
@@ -116,13 +176,13 @@ void __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu)dsb (ishst);/* Switch to requested VMID */ -__tlb_switch_to_guest (mmu, &cxt); +enter_vmid_context (mmu, &cxt);__tlbi (vmalls12e1is);dsb (ish);isb (); -__tlb_switch_to_host (&cxt); +exit_vmid_context (&cxt); }void __kvm_flush_cpu_context (struct kvm_s2_mmu *mmu)
@@ -130,14 +190,14 @@ void __kvm_flush_cpu_context(struct kvm_s2_mmu *mmu)struct tlb_inv_context cxt;/* Switch to requested VMID */ -__tlb_switch_to_guest (mmu, &cxt); +enter_vmid_context (mmu, &cxt);__tlbi (vmalle1); asmvolatile ("ic iallu" );dsb (nsh);isb (); -__tlb_switch_to_host (&cxt); +exit_vmid_context (&cxt); }void __kvm_flush_vm_context (void )
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 83/89 KVM: arm64: Avoid BBM when changing only s/w bits in Stage-2 PTE
In reply to Will DeaconBreak-before-make (BBM) can be expensive, as transitioning via an invalid mapping (i.e. the "break" step) requires the completion of TLB invalidation and can also cause other agents to fault concurrently on the invalid mapping.
Since BBM is not required when changing only the software bits of a PTE, avoid the sequence in this case and just update the PTE directly.
--- arch/arm64/kvm/hyp/pgtable.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
arch/arm64/kvm/hyp/pgtable.c +10 -1
@@ -744,6 +744,13 @@ static int stage2_map_walker_try_leaf(u64 addr, u64 end, u32 level,if (!stage2_pte_needs_update (old, new))return -EAGAIN ; +/* +* If we're only changing software bits, then we don't need to +* do anything else/ +*/ +if (!((old ^ new) & ~KVM_PTE_LEAF_ATTR_HI_SW )) + goto out_set_pte; +stage2_put_pte (ptep, data->mmu, addr, level, mm_ops); }
@@ -754,9 +761,11 @@ static int stage2_map_walker_try_leaf(u64 addr, u64 end, u32 level,if (mm_ops->icache_inval_pou &&stage2_pte_executable (new)) mm_ops->icache_inval_pou (kvm_pte_follow (new, mm_ops), granule); -smp_store_release (ptep, new);if (stage2_pte_is_counted (new)) mm_ops->get_page (ptep); + +out_set_pte: +smp_store_release (ptep, new);if (kvm_phys_is_valid (phys)) data->phys += granule;return 0 ;
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 84/89 KVM: arm64: Extend memory sharing to allow guest-to-host transitions
In reply to Will DeaconA guest that can only operate on private memory is pretty useless, as it has no way to share buffers with the host for things like virtio.
Extend our memory protection mechanisms to support the sharing and unsharing of guest pages from the guest to the host. For now, this functionality is unused but will later be exposed to the guest via hypercalls.
--- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 2 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 232 ++++++++++++++++++ 2 files changed, 234 insertions(+)
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +2 -0
@@ -70,6 +70,8 @@ int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages);int __pkvm_hyp_donate_host (u64 pfn,u64 nr_pages);int __pkvm_host_share_guest (u64 pfn,u64 gfn,struct kvm_vcpu *vcpu);int __pkvm_host_donate_guest (u64 pfn,u64 gfn,struct kvm_vcpu *vcpu); +int __pkvm_guest_share_host (struct kvm_vcpu *vcpu,u64 ipa); +int __pkvm_guest_unshare_host (struct kvm_vcpu *vcpu,u64 ipa);bool addr_is_memory (phys_addr_t phys);int host_stage2_idmap_locked (phys_addr_t addr,u64 size,enum kvm_pgtable_prot prot);
arch/arm64/kvm/hyp/nvhe/mem_protect.c +232 -0
303 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 85/89 KVM: arm64: Document the KVM/arm64-specific calls in hypercalls.rst
In reply to Will DeaconKVM/arm64 makes use of the SMCCC "Vendor Specific Hypervisor Service Call Range" to expose KVM-specific hypercalls to guests in a discoverable and extensible fashion.
Document the existence of this interface and the discovery hypercall.
--- Documentation/virt/kvm/arm/hypercalls.rst | 46 +++++++++++++++++++++++ Documentation/virt/kvm/arm/index.rst | 1 + 2 files changed, 47 insertions(+) create mode 100644 Documentation/virt/kvm/arm/hypercalls.rst
Documentation/virt/kvm/arm/hypercalls.rst new file +46 -0
@@ -0,0 +1,46 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=============================================== +KVM/arm64-specific hypercalls exposed to guests +=============================================== + +This file documents the KVM/arm64-specific hypercalls which may be +exposed by KVM/arm64 to guest operating systems. These hypercalls are +issued using the HVC instruction according to version 1.1 of the Arm SMC +Calling Convention (DEN0028/C): + +https://developer.arm.com/docs/den0028/c + +All KVM/arm64-specific hypercalls are allocated within the "Vendor +Specific Hypervisor Service Call" range with a UID of +``28b46fb6-2ec5-11e9-a9ca-4b564d003a74``. This UID should be queried by the +guest using the standard "Call UID" function for the service range in +order to determine that the KVM/arm64-specific hypercalls are available. + +``ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID`` +--------------------------------------------- + +Provides a discovery mechanism for other KVM/arm64 hypercalls. + ++---------------------+-------------------------------------------------------------+ +| Presence: | Mandatory for the KVM/arm64 UID | ++---------------------+-------------------------------------------------------------+ +| Calling convention: | HVC32 | ++---------------------+----------+--------------------------------------------------+ +| Function ID: | (uint32) | 0x86000000 | ++---------------------+----------+--------------------------------------------------+ +| Arguments: | None | ++---------------------+----------+----+---------------------------------------------+ +| Return Values: | (uint32) | R0 | Bitmap of available function numbers 0-31 | +| +----------+----+---------------------------------------------+ +| | (uint32) | R1 | Bitmap of available function numbers 32-63 | +| +----------+----+---------------------------------------------+ +| | (uint32) | R2 | Bitmap of available function numbers 64-95 | +| +----------+----+---------------------------------------------+ +| | (uint32) | R3 | Bitmap of available function numbers 96-127 | ++---------------------+----------+----+---------------------------------------------+ + +``ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID`` +---------------------------------------- + +See ptp_kvm.rst
Documentation/virt/kvm/arm/index.rst +1 -0
@@ -8,6 +8,7 @@ ARM :maxdepth: 2 hyp-abi + hypercalls psci pvtime ptp_kvm
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 86/89 KVM: arm64: Reformat/beautify PTP hypercall documentation
In reply to Will DeaconThe PTP hypercall documentation doesn't produce the best-looking table when formatting in HTML as all of the return value definitions end up on the same line.
Reformat the PTP hypercall documentation to follow the formatting used by hypercalls.rst.
--- Documentation/virt/kvm/arm/ptp_kvm.rst | 38 ++++++++++++++++---------- 1 file changed, 24 insertions(+), 14 deletions(-)
Documentation/virt/kvm/arm/ptp_kvm.rst +24 -14
@@ -7,19 +7,29 @@ PTP_KVM is used for high precision time sync between host and guests. It relies on transferring the wall clock and counter value from the host to the guest using a KVM-specific hypercall. -* ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID: 0x86000001 +``ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID`` +---------------------------------------- -This hypercall uses the SMC32/HVC32 calling convention: +Retrieve current time information for the specific counter. There are no +endianness restrictions. -ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID - ============== ======== ===================================== - Function ID: (uint32) 0x86000001 - Arguments: (uint32) KVM_PTP_VIRT_COUNTER(0) - KVM_PTP_PHYS_COUNTER(1) - Return Values: (int32) NOT_SUPPORTED(-1) on error, or - (uint32) Upper 32 bits of wall clock time (r0) - (uint32) Lower 32 bits of wall clock time (r1) - (uint32) Upper 32 bits of counter (r2) - (uint32) Lower 32 bits of counter (r3) - Endianness: No Restrictions. - ============== ======== ===================================== ++---------------------+-------------------------------------------------------+ +| Presence: | Optional | ++---------------------+-------------------------------------------------------+ +| Calling convention: | HVC32 | ++---------------------+----------+--------------------------------------------+ +| Function ID: | (uint32) | 0x86000001 | ++---------------------+----------+----+---------------------------------------+ +| Arguments: | (uint32) | R1 | ``KVM_PTP_VIRT_COUNTER (0)`` | +| | | +---------------------------------------+ +| | | | ``KVM_PTP_PHYS_COUNTER (1)`` | ++---------------------+----------+----+---------------------------------------+ +| Return Values: | (int32) | R0 | ``NOT_SUPPORTED (-1)`` on error, else | +| | | | upper 32 bits of wall clock time | +| +----------+----+---------------------------------------+ +| | (uint32) | R1 | Lower 32 bits of wall clock time | +| +----------+----+---------------------------------------+ +| | (uint32) | R2 | Upper 32 bits of counter | +| +----------+----+---------------------------------------+ +| | (uint32) | R3 | Lower 32 bits of counter | ++---------------------+----------+----+---------------------------------------+
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 87/89 KVM: arm64: Expose memory sharing hypercalls to protected guests
In reply to Will DeaconExtend our KVM "vendor" hypercalls to expose three new hypercalls to protected guests for the purpose of opening and closing shared memory windows with the host:
MEMINFO: Query the stage-2 page size (i.e. the minimum granule at
which memory can be shared)
MEM_SHARE: Share a page RWX with the host, faulting the page in if
necessary.
MEM_UNSHARE: Unshare a page with the host. Subsequent host accesses
to the page will result in a fault being injected by the hypervisor.
--- Documentation/virt/kvm/arm/hypercalls.rst | 72 ++++++++++++++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 24 ++++- arch/arm64/kvm/hyp/nvhe/pkvm.c | 109 +++++++++++++++++++++- include/linux/arm-smccc.h | 21 +++++ 4 files changed, 223 insertions(+), 3 deletions(-)
Documentation/virt/kvm/arm/hypercalls.rst +72 -0
@@ -44,3 +44,75 @@ Provides a discovery mechanism for other KVM/arm64 hypercalls. ---------------------------------------- See ptp_kvm.rst + +``ARM_SMCCC_KVM_FUNC_HYP_MEMINFO`` +---------------------------------- + +Query the memory protection parameters for a protected virtual machine. + ++---------------------+-------------------------------------------------------------+ +| Presence: | Optional; protected guests only. | ++---------------------+-------------------------------------------------------------+ +| Calling convention: | HVC64 | ++---------------------+----------+--------------------------------------------------+ +| Function ID: | (uint32) | 0xC6000002 | ++---------------------+----------+----+---------------------------------------------+ +| Arguments: | (uint64) | R1 | Reserved / Must be zero | +| +----------+----+---------------------------------------------+ +| | (uint64) | R2 | Reserved / Must be zero | +| +----------+----+---------------------------------------------+ +| | (uint64) | R3 | Reserved / Must be zero | ++---------------------+----------+----+---------------------------------------------+ +| Return Values: | (int64) | R0 | ``INVALID_PARAMETER (-3)`` on error, else | +| | | | memory protection granule in bytes | ++---------------------+----------+----+---------------------------------------------+ + +``ARM_SMCCC_KVM_FUNC_MEM_SHARE`` +-------------------------------- + +Share a region of memory with the KVM host, granting it read, write and execute +permissions. The size of the region is equal to the memory protection granule +advertised by ``ARM_SMCCC_KVM_FUNC_HYP_MEMINFO``. + ++---------------------+-------------------------------------------------------------+ +| Presence: | Optional; protected guests only. | ++---------------------+-------------------------------------------------------------+ +| Calling convention: | HVC64 | ++---------------------+----------+--------------------------------------------------+ +| Function ID: | (uint32) | 0xC6000003 | ++---------------------+----------+----+---------------------------------------------+ +| Arguments: | (uint64) | R1 | Base IPA of memory region to share | +| +----------+----+---------------------------------------------+ +| | (uint64) | R2 | Reserved / Must be zero | +| +----------+----+---------------------------------------------+ +| | (uint64) | R3 | Reserved / Must be zero | ++---------------------+----------+----+---------------------------------------------+ +| Return Values: | (int64) | R0 | ``SUCCESS (0)`` | +| | | +---------------------------------------------+ +| | | | ``INVALID_PARAMETER (-3)`` | ++---------------------+----------+----+---------------------------------------------+ + +``ARM_SMCCC_KVM_FUNC_MEM_UNSHARE`` +---------------------------------- + +Revoke access permission from the KVM host to a memory region previously shared +with ``ARM_SMCCC_KVM_FUNC_MEM_SHARE``. The size of the region is equal to the +memory protection granule advertised by ``ARM_SMCCC_KVM_FUNC_HYP_MEMINFO``. + ++---------------------+-------------------------------------------------------------+ +| Presence: | Optional; protected guests only. | ++---------------------+-------------------------------------------------------------+ +| Calling convention: | HVC64 | ++---------------------+----------+--------------------------------------------------+ +| Function ID: | (uint32) | 0xC6000004 | ++---------------------+----------+----+---------------------------------------------+ +| Arguments: | (uint64) | R1 | Base IPA of memory region to unshare | +| +----------+----+---------------------------------------------+ +| | (uint64) | R2 | Reserved / Must be zero | +| +----------+----+---------------------------------------------+ +| | (uint64) | R3 | Reserved / Must be zero | ++---------------------+----------+----+---------------------------------------------+ +| Return Values: | (int64) | R0 | ``SUCCESS (0)`` | +| | | +---------------------------------------------+ +| | | | ``INVALID_PARAMETER (-3)`` | ++---------------------+----------+----+---------------------------------------------+
arch/arm64/kvm/hyp/nvhe/hyp-main.c +23 -1
@@ -45,7 +45,7 @@ static void handle_pvm_entry_wfx(struct kvm_vcpu *host_vcpu, struct kvm_vcpu *shKVM_ARM64_INCREMENT_PC ; } -static void handle_pvm_entry_hvc64 (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) +static void handle_pvm_entry_psci (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) {u32 psci_fn =smccc_get_function (shadow_vcpu);u64 ret =READ_ONCE (host_vcpu->arch.ctxt.regs.regs[0 ]);
@@ -81,6 +81,22 @@ static void handle_pvm_entry_hvc64(struct kvm_vcpu *host_vcpu, struct kvm_vcpu *vcpu_set_reg (shadow_vcpu,0 , ret); } +static void handle_pvm_entry_hvc64 (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) +{ +u32 fn =smccc_get_function (shadow_vcpu); + +switch (fn) { +case ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID : + fallthrough; +case ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID : +vcpu_set_reg (shadow_vcpu,0 ,SMCCC_RET_SUCCESS ); +break ; +default : +handle_pvm_entry_psci (host_vcpu, shadow_vcpu); +break ; + } +} +static void handle_pvm_entry_sys64 (struct kvm_vcpu *host_vcpu,struct kvm_vcpu *shadow_vcpu) {unsigned long host_flags;
@@ -257,6 +273,12 @@ static void handle_pvm_exit_hvc64(struct kvm_vcpu *host_vcpu, struct kvm_vcpu *s n =1 ;break ; +case ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID : + fallthrough; +case ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID : + n =4 ; +break ; +case PSCI_1_1_FN_SYSTEM_RESET2 :case PSCI_1_1_FN64_SYSTEM_RESET2 : n =3 ;
arch/arm64/kvm/hyp/nvhe/pkvm.c +107 -2
126 lines not on this page · raw message
include/linux/arm-smccc.h +21 -0
@@ -112,6 +112,9 @@/* KVM "vendor specific" services */ #define ARM_SMCCC_KVM_FUNC_FEATURES 0#define ARM_SMCCC_KVM_FUNC_PTP 1 +#define ARM_SMCCC_KVM_FUNC_HYP_MEMINFO 2 +#define ARM_SMCCC_KVM_FUNC_MEM_SHARE 3 +#define ARM_SMCCC_KVM_FUNC_MEM_UNSHARE 4#define ARM_SMCCC_KVM_FUNC_FEATURES_2 127#define ARM_SMCCC_KVM_NUM_FUNCS 128
@@ -134,6 +137,24 @@ ARM_SMCCC_OWNER_VENDOR_HYP, \ ARM_SMCCC_KVM_FUNC_PTP) +#define ARM_SMCCC_VENDOR_HYP_KVM_HYP_MEMINFO_FUNC_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + ARM_SMCCC_OWNER_VENDOR_HYP, \ + ARM_SMCCC_KVM_FUNC_HYP_MEMINFO) + +#define ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + ARM_SMCCC_OWNER_VENDOR_HYP, \ + ARM_SMCCC_KVM_FUNC_MEM_SHARE) + +#define ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + ARM_SMCCC_OWNER_VENDOR_HYP, \ + ARM_SMCCC_KVM_FUNC_MEM_UNSHARE) +/* ptp_kvm counter type ID */ #define KVM_PTP_VIRT_COUNTER 0#define KVM_PTP_PHYS_COUNTER 1
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 88/89 KVM: arm64: Introduce KVM_VM_TYPE_ARM_PROTECTED machine type for PVMs
In reply to Will DeaconIntroduce a new virtual machine type, KVM_VM_TYPE_ARM_PROTECTED, which specifies that the guest memory pages are to be unmapped from the host stage-2 by the hypervisor.
--- arch/arm64/include/asm/kvm_pkvm.h | 2 +- arch/arm64/kvm/arm.c | 5 ++++- arch/arm64/kvm/mmu.c | 3 --- arch/arm64/kvm/pkvm.c | 10 +++++++++- include/uapi/linux/kvm.h | 6 ++++++ 5 files changed, 20 insertions(+), 6 deletions(-)
arch/arm64/include/asm/kvm_pkvm.h +1 -1
@@ -16,7 +16,7 @@#define HYP_MEMBLOCK_REGIONS 128 -int kvm_init_pvm (struct kvm *kvm); +int kvm_init_pvm (struct kvm *kvm,unsigned long type);int kvm_shadow_create (struct kvm *kvm);void kvm_shadow_destroy (struct kvm *kvm);
arch/arm64/kvm/arm.c +4 -1
@@ -141,11 +141,14 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) {int ret; +if (type & ~KVM_VM_TYPE_MASK ) +return -EINVAL ; + ret =kvm_share_hyp (kvm, kvm +1 );if (ret)return ret; - ret =kvm_init_pvm (kvm); + ret =kvm_init_pvm (kvm, type);if (ret) goto err_unshare_kvm;
arch/arm64/kvm/mmu.c +0 -3
@@ -652,9 +652,6 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long tu64 mmfr0, mmfr1;u32 phys_shift; -if (type & ~KVM_VM_TYPE_ARM_IPA_SIZE_MASK ) -return -EINVAL ; - phys_shift =KVM_VM_TYPE_ARM_IPA_SIZE (type);if (is_protected_kvm_enabled ()) { phys_shift = kvm_ipa_limit;
arch/arm64/kvm/pkvm.c +9 -1
@@ -218,8 +218,16 @@ void kvm_shadow_destroy(struct kvm *kvm) } } -int kvm_init_pvm (struct kvm *kvm) +int kvm_init_pvm (struct kvm *kvm,unsigned long type) {mutex_init (&kvm->arch.pkvm.shadow_lock); + +if (!(type &KVM_VM_TYPE_ARM_PROTECTED )) +return 0 ; + +if (!is_protected_kvm_enabled ()) +return -EINVAL ; + + kvm->arch.pkvm.enabled = true;return 0 ; }
include/uapi/linux/kvm.h +6 -0
@@ -887,6 +887,12 @@ struct kvm_ppc_resize_hpt {#define KVM_VM_TYPE_ARM_IPA_SIZE_MASK 0xffULL#define KVM_VM_TYPE_ARM_IPA_SIZE (x) \ ((x) & KVM_VM_TYPE_ARM_IPA_SIZE_MASK) + +#define KVM_VM_TYPE_ARM_PROTECTED (1UL << 8) + +#define KVM_VM_TYPE_MASK (KVM_VM_TYPE_ARM_IPA_SIZE_MASK | \ + KVM_VM_TYPE_ARM_PROTECTED) +/* * ioctls for /dev/kvm fds: */
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Will Deacon
maintainerPATCH 89/89 Documentation: KVM: Add some documentation for Protected KVM on arm64
In reply to Will DeaconAdd some initial documentation for the Protected KVM (pKVM) feature on arm64, describing the user ABI for creating protected VMs as well as their limitations.
--- .../admin-guide/kernel-parameters.txt | 4 +- Documentation/virt/kvm/arm/index.rst | 1 + Documentation/virt/kvm/arm/pkvm.rst | 96 +++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 Documentation/virt/kvm/arm/pkvm.rst
Documentation/admin-guide/kernel-parameters.txt +3 -1
@@ -2437,7 +2437,9 @@ protected guests. protected: nVHE-based mode with support for guests whose - state is kept private from the host. + state is kept private from the host. See + Documentation/virt/kvm/arm/pkvm.rst for more + information about this mode of operation. Defaults to VHE/nVHE based on hardware support. Setting mode to "protected" will disable kexec and hibernation
Documentation/virt/kvm/arm/index.rst +1 -0
@@ -9,6 +9,7 @@ ARM hyp-abi hypercalls + pkvm psci pvtime ptp_kvm
Documentation/virt/kvm/arm/pkvm.rst new file +96 -0
96 lines not on this page · raw message
Signature · 6 lines
2.36.1.124.g0e6072fb45-goog _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Re: [PATCH 89/89] Documentation: KVM: Add some documentation for Protected KVM on arm64
Quoted 84 lines · Will Deacon
Add some initial documentation for the Protected KVM (pKVM) feature on arm64, describing the user ABI for creating protected VMs as well as their limitations.
Signed-off-by Will Deacon <[email protected]>--- .../admin-guide/kernel-parameters.txt | 4 +- Documentation/virt/kvm/arm/index.rst | 1 + Documentation/virt/kvm/arm/pkvm.rst | 96 +++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 Documentation/virt/kvm/arm/pkvm.rstDocumentation/admin-guide/kernel-parameters.txt +3
-1@@ -2437,7 +2437,9 @@ protected guests. protected: nVHE-based mode with support for guests whose - state is kept private from the host. + state is kept private from the host. See + Documentation/virt/kvm/arm/pkvm.rst for more + information about this mode of operation. Defaults to VHE/nVHE based on hardware support. Setting mode to "protected" will disable kexec and hibernationDocumentation/virt/kvm/arm/index.rst +1
-0@@ -9,6 +9,7 @@ ARM hyp-abi hypercalls + pkvm psci pvtime ptp_kvmDocumentation/virt/kvm/arm/pkvm.rst new file +38
-0@@ -0,0 +1,96 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Protected virtual machines (pKVM) +================================= + +Introduction +------------ + +Protected KVM (pKVM) is a KVM/arm64 extension which uses the two-stage +translation capability of the Armv8 MMU to isolate guest memory from the host +system. This allows for the creation of a confidential computing environment +without relying on whizz-bang features in hardware, but still allowing room for +complementary technologies such as memory encryption and hardware-backed +attestation. + +The major implementation change brought about by pKVM is that the hypervisor +code running at EL2 is now largely independent of (and isolated from) the rest +of the host kernel running at EL1 and therefore additional hypercalls are +introduced to manage manipulation of guest stage-2 page tables, creation of VM +data structures and reclamation of memory on teardown. An immediate consequence +of this change is that the host itself runs with an identity mapping enabled +at stage-2, providing the hypervisor code with a mechanism to restrict host +access to an arbitrary physical page. + +Enabling pKVM +------------- + +The pKVM hypervisor is enabled by booting the host kernel at EL2 with +"``kvm-arm.mode=protected``" on the command-line. Once enabled, VMs can be spawned +in either protected or non-protected state, although the hypervisor is still +responsible for managing most of the VM metadata in either case. + +Limitations +----------- + +Enabling pKVM places some significant limitations on KVM guests, regardless of +whether they are spawned in protected state. It is therefore recommended only +to enable pKVM if protected VMs are required, with non-protected state acting
Documentation/virt/kvm/arm/pkvm.rst line 48
Quoted 10 lines
Documentation/virt/kvm/arm/pkvm.rst new file +10
-0+primarily as a debug and development aid. + +If you're still keen, then here is an incomplete list of caveats that apply +to all VMs running under pKVM: + +- Guest memory cannot be file-backed (with the exception of shmem/memfd) and is + pinned as it is mapped into the guest. This prevents the host from + swapping-out, migrating, merging or generally doing anything useful with the + guest pages. It also requires that the VMM has either ``CAP_IPC_LOCK`` or + sufficient ``RLIMIT_MEMLOCK`` to account for this pinned memory.
I think it would be useful to also add a note to Documentation/virt/kvm/api.rst saying that ioctl(KVM_RUN) can return ENOMEM if the VMM does not have CAP_IPC_LOCK or sufficient RLIMIT_MEMLOCK, since that's where people are going to look when they see that return value.
Peter
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel