* [PATCH v3 1/8] KVM: arm64: Fix Trace Buffer trapping for protected VMs
2025-11-10 13:45 [PATCH v3 0/8] KVM: arm64: Fixes for guest CPU feature trapping and enabling Fuad Tabba
@ 2025-11-10 13:45 ` Fuad Tabba
2025-11-10 13:45 ` [PATCH v3 2/8] KVM: arm64: Fix Trace Buffer trap polarity " Fuad Tabba
` (6 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Fuad Tabba @ 2025-11-10 13:45 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel
Cc: maz, oliver.upton, will, joey.gouly, suzuki.poulose, yuzenghui,
catalin.marinas, vladimir.murzin, tabba
For protected VMs in pKVM, the hypervisor should trap accesses to trace
buffer system registers if Trace Buffer isn't supported by the VM.
However, the current code only traps if Trace Buffer External Mode isn't
supported.
Fix this by checking for FEAT_TRBE (Trace Buffer) rather than
FEAT_TRBE_EXT.
Fixes: 9d5261269098 ("KVM: arm64: Trap external trace for protected VMs")
Reported-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/kvm/hyp/nvhe/pkvm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 43bde061b65d..8d06a246dfd1 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -117,7 +117,7 @@ static void pvm_init_traps_mdcr(struct kvm_vcpu *vcpu)
if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, TraceFilt, IMP))
val |= MDCR_EL2_TTRF;
- if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, ExtTrcBuff, IMP))
+ if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, TraceBuffer, IMP))
val |= MDCR_EL2_E2TB_MASK;
/* Trap Debug Communications Channel registers */
--
2.51.2.1041.gc1ab5b90ca-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v3 2/8] KVM: arm64: Fix Trace Buffer trap polarity for protected VMs
2025-11-10 13:45 [PATCH v3 0/8] KVM: arm64: Fixes for guest CPU feature trapping and enabling Fuad Tabba
2025-11-10 13:45 ` [PATCH v3 1/8] KVM: arm64: Fix Trace Buffer trapping for protected VMs Fuad Tabba
@ 2025-11-10 13:45 ` Fuad Tabba
2025-11-10 13:45 ` [PATCH v3 3/8] KVM: arm64: Fix MTE flag initialization " Fuad Tabba
` (5 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Fuad Tabba @ 2025-11-10 13:45 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel
Cc: maz, oliver.upton, will, joey.gouly, suzuki.poulose, yuzenghui,
catalin.marinas, vladimir.murzin, tabba
The E2TB bits in MDCR_EL2 control trapping of Trace Buffer system
register accesses. These accesses are trapped to EL2 when the bits are
clear.
The trap initialization logic for protected VMs in pvm_init_traps_mdcr()
had the polarity inverted. When a guest did not support the Trace Buffer
feature, the code was setting E2TB. This incorrectly disabled the trap,
potentially allowing a protected guest to access registers for a feature
it was not given.
Fix this by inverting the operation.
Fixes: f50758260bff ("KVM: arm64: Group setting traps for protected VMs by control register")
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/kvm/hyp/nvhe/pkvm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 8d06a246dfd1..f6f8996c4f97 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -118,7 +118,7 @@ static void pvm_init_traps_mdcr(struct kvm_vcpu *vcpu)
val |= MDCR_EL2_TTRF;
if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, TraceBuffer, IMP))
- val |= MDCR_EL2_E2TB_MASK;
+ val &= ~MDCR_EL2_E2TB_MASK;
/* Trap Debug Communications Channel registers */
if (!kvm_has_feat(kvm, ID_AA64MMFR0_EL1, FGT, IMP))
--
2.51.2.1041.gc1ab5b90ca-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v3 3/8] KVM: arm64: Fix MTE flag initialization for protected VMs
2025-11-10 13:45 [PATCH v3 0/8] KVM: arm64: Fixes for guest CPU feature trapping and enabling Fuad Tabba
2025-11-10 13:45 ` [PATCH v3 1/8] KVM: arm64: Fix Trace Buffer trapping for protected VMs Fuad Tabba
2025-11-10 13:45 ` [PATCH v3 2/8] KVM: arm64: Fix Trace Buffer trap polarity " Fuad Tabba
@ 2025-11-10 13:45 ` Fuad Tabba
2025-11-10 14:51 ` Ben Horgan
2025-11-10 13:45 ` [PATCH v3 4/8] KVM: arm64: Introduce helper to calculate fault IPA offset Fuad Tabba
` (4 subsequent siblings)
7 siblings, 1 reply; 11+ messages in thread
From: Fuad Tabba @ 2025-11-10 13:45 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel
Cc: maz, oliver.upton, will, joey.gouly, suzuki.poulose, yuzenghui,
catalin.marinas, vladimir.murzin, tabba
The function pkvm_init_features_from_host() initializes guest
features, propagating them from the host. The logic to propagate
KVM_ARCH_FLAG_MTE_ENABLED (Memory Tagging Extension)
has a couple of issues.
First, the check was in the common path, before the divergence for
protected and non-protected VMs. For non-protected VMs, this was
unnecessary, as 'kvm->arch.flags' is completely overwritten by
host_arch_flags immediately after, which already contains the MTE flag.
For protected VMs, this was setting the flag even if the feature is not
allowed.
Second, the check was reading 'host_kvm->arch.flags' instead of using
the local 'host_arch_flags', which is read once from the host flags.
Fix these by moving the MTE flag check inside the protected-VM-only
path, checking if the feature is allowed, and changing it to use the
correct host_arch_flags local variable. This ensures non-protected VMs
get the flag via the bulk copy, and protected VMs get it via an explicit
check.
Fixes: b7f345fbc32a ("KVM: arm64: Fix FEAT_MTE in pKVM")
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/kvm/hyp/nvhe/pkvm.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index f6f8996c4f97..7e370e31260d 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -337,9 +337,6 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
/* CTR_EL0 is always under host control, even for protected VMs. */
hyp_vm->kvm.arch.ctr_el0 = host_kvm->arch.ctr_el0;
- if (test_bit(KVM_ARCH_FLAG_MTE_ENABLED, &host_kvm->arch.flags))
- set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags);
-
/* No restrictions for non-protected VMs. */
if (!kvm_vm_is_protected(kvm)) {
hyp_vm->kvm.arch.flags = host_arch_flags;
@@ -372,6 +369,11 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
kvm->arch.flags |= host_arch_flags & BIT(KVM_ARCH_FLAG_GUEST_HAS_SVE);
}
+ if (kvm_pvm_ext_allowed(KVM_CAP_ARM_MTE)) {
+ set_bit(KVM_CAP_ARM_MTE, allowed_features);
+ kvm->arch.flags |= host_arch_flags & BIT(KVM_ARCH_FLAG_MTE_ENABLED);
+ }
+
bitmap_and(kvm->arch.vcpu_features, host_kvm->arch.vcpu_features,
allowed_features, KVM_VCPU_MAX_FEATURES);
}
--
2.51.2.1041.gc1ab5b90ca-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v3 3/8] KVM: arm64: Fix MTE flag initialization for protected VMs
2025-11-10 13:45 ` [PATCH v3 3/8] KVM: arm64: Fix MTE flag initialization " Fuad Tabba
@ 2025-11-10 14:51 ` Ben Horgan
2025-11-10 15:03 ` Fuad Tabba
0 siblings, 1 reply; 11+ messages in thread
From: Ben Horgan @ 2025-11-10 14:51 UTC (permalink / raw)
To: Fuad Tabba, kvmarm, linux-arm-kernel
Cc: maz, oliver.upton, will, joey.gouly, suzuki.poulose, yuzenghui,
catalin.marinas, vladimir.murzin
Hi Fuad,
On 11/10/25 13:45, Fuad Tabba wrote:
> The function pkvm_init_features_from_host() initializes guest
> features, propagating them from the host. The logic to propagate
> KVM_ARCH_FLAG_MTE_ENABLED (Memory Tagging Extension)
> has a couple of issues.
>
> First, the check was in the common path, before the divergence for
> protected and non-protected VMs. For non-protected VMs, this was
> unnecessary, as 'kvm->arch.flags' is completely overwritten by
> host_arch_flags immediately after, which already contains the MTE flag.
> For protected VMs, this was setting the flag even if the feature is not
> allowed.
>
> Second, the check was reading 'host_kvm->arch.flags' instead of using
> the local 'host_arch_flags', which is read once from the host flags.
>
> Fix these by moving the MTE flag check inside the protected-VM-only
> path, checking if the feature is allowed, and changing it to use the
> correct host_arch_flags local variable. This ensures non-protected VMs
> get the flag via the bulk copy, and protected VMs get it via an explicit
> check.
>
> Fixes: b7f345fbc32a ("KVM: arm64: Fix FEAT_MTE in pKVM")
> Signed-off-by: Fuad Tabba <tabba@google.com>
> ---
> arch/arm64/kvm/hyp/nvhe/pkvm.c | 8 +++++---
> 1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
> index f6f8996c4f97..7e370e31260d 100644
> --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
> +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
> @@ -337,9 +337,6 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
> /* CTR_EL0 is always under host control, even for protected VMs. */
> hyp_vm->kvm.arch.ctr_el0 = host_kvm->arch.ctr_el0;
>
> - if (test_bit(KVM_ARCH_FLAG_MTE_ENABLED, &host_kvm->arch.flags))
> - set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags);
> -
> /* No restrictions for non-protected VMs. */
> if (!kvm_vm_is_protected(kvm)) {
> hyp_vm->kvm.arch.flags = host_arch_flags;
> @@ -372,6 +369,11 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
> kvm->arch.flags |= host_arch_flags & BIT(KVM_ARCH_FLAG_GUEST_HAS_SVE);
> }
>
> + if (kvm_pvm_ext_allowed(KVM_CAP_ARM_MTE)) {
> + set_bit(KVM_CAP_ARM_MTE, allowed_features);
Isn't allowed_features a bitmap for the KVM_ARCH_VCPU_ defines? I think
MTE is treated as a property of the VM.
> + kvm->arch.flags |= host_arch_flags & BIT(KVM_ARCH_FLAG_MTE_ENABLED);
> + }
> +
> bitmap_and(kvm->arch.vcpu_features, host_kvm->arch.vcpu_features,
> allowed_features, KVM_VCPU_MAX_FEATURES);
> }
Thanks,
Ben
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v3 3/8] KVM: arm64: Fix MTE flag initialization for protected VMs
2025-11-10 14:51 ` Ben Horgan
@ 2025-11-10 15:03 ` Fuad Tabba
0 siblings, 0 replies; 11+ messages in thread
From: Fuad Tabba @ 2025-11-10 15:03 UTC (permalink / raw)
To: Ben Horgan
Cc: kvmarm, linux-arm-kernel, maz, oliver.upton, will, joey.gouly,
suzuki.poulose, yuzenghui, catalin.marinas, vladimir.murzin
Hi Ben,
On Mon, 10 Nov 2025 at 14:51, Ben Horgan <ben.horgan@arm.com> wrote:
>
> Hi Fuad,
>
> On 11/10/25 13:45, Fuad Tabba wrote:
> > The function pkvm_init_features_from_host() initializes guest
> > features, propagating them from the host. The logic to propagate
> > KVM_ARCH_FLAG_MTE_ENABLED (Memory Tagging Extension)
> > has a couple of issues.
> >
> > First, the check was in the common path, before the divergence for
> > protected and non-protected VMs. For non-protected VMs, this was
> > unnecessary, as 'kvm->arch.flags' is completely overwritten by
> > host_arch_flags immediately after, which already contains the MTE flag.
> > For protected VMs, this was setting the flag even if the feature is not
> > allowed.
> >
> > Second, the check was reading 'host_kvm->arch.flags' instead of using
> > the local 'host_arch_flags', which is read once from the host flags.
> >
> > Fix these by moving the MTE flag check inside the protected-VM-only
> > path, checking if the feature is allowed, and changing it to use the
> > correct host_arch_flags local variable. This ensures non-protected VMs
> > get the flag via the bulk copy, and protected VMs get it via an explicit
> > check.
> >
> > Fixes: b7f345fbc32a ("KVM: arm64: Fix FEAT_MTE in pKVM")
> > Signed-off-by: Fuad Tabba <tabba@google.com>
> > ---
> > arch/arm64/kvm/hyp/nvhe/pkvm.c | 8 +++++---
> > 1 file changed, 5 insertions(+), 3 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
> > index f6f8996c4f97..7e370e31260d 100644
> > --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
> > +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
> > @@ -337,9 +337,6 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
> > /* CTR_EL0 is always under host control, even for protected VMs. */
> > hyp_vm->kvm.arch.ctr_el0 = host_kvm->arch.ctr_el0;
> >
> > - if (test_bit(KVM_ARCH_FLAG_MTE_ENABLED, &host_kvm->arch.flags))
> > - set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags);
> > -
> > /* No restrictions for non-protected VMs. */
> > if (!kvm_vm_is_protected(kvm)) {
> > hyp_vm->kvm.arch.flags = host_arch_flags;
> > @@ -372,6 +369,11 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
> > kvm->arch.flags |= host_arch_flags & BIT(KVM_ARCH_FLAG_GUEST_HAS_SVE);
> > }
> >
> > + if (kvm_pvm_ext_allowed(KVM_CAP_ARM_MTE)) {
> > + set_bit(KVM_CAP_ARM_MTE, allowed_features);
>
> Isn't allowed_features a bitmap for the KVM_ARCH_VCPU_ defines? I think
> MTE is treated as a property of the VM.
Right! This line should go. The check should only be to propagate the
flag to kvm->arch.flags, i.e.,
+ if (kvm_pvm_ext_allowed(KVM_CAP_ARM_MTE))
+ kvm->arch.flags |= host_arch_flags &
BIT(KVM_ARCH_FLAG_MTE_ENABLED);
Thanks for pointing this out,
/fuad
> > + kvm->arch.flags |= host_arch_flags & BIT(KVM_ARCH_FLAG_MTE_ENABLED);
> > + }
> > +
> > bitmap_and(kvm->arch.vcpu_features, host_kvm->arch.vcpu_features,
> > allowed_features, KVM_VCPU_MAX_FEATURES);
> > }
>
>
> Thanks,
>
> Ben
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 4/8] KVM: arm64: Introduce helper to calculate fault IPA offset
2025-11-10 13:45 [PATCH v3 0/8] KVM: arm64: Fixes for guest CPU feature trapping and enabling Fuad Tabba
` (2 preceding siblings ...)
2025-11-10 13:45 ` [PATCH v3 3/8] KVM: arm64: Fix MTE flag initialization " Fuad Tabba
@ 2025-11-10 13:45 ` Fuad Tabba
2025-11-10 13:45 ` [PATCH v3 5/8] KVM: arm64: Include VM type when checking VM capabilities in pKVM Fuad Tabba
` (3 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Fuad Tabba @ 2025-11-10 13:45 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel
Cc: maz, oliver.upton, will, joey.gouly, suzuki.poulose, yuzenghui,
catalin.marinas, vladimir.murzin, tabba
This 12-bit FAR fault IPA offset mask is hard-coded as 'GENMASK(11, 0)'
in several places to reconstruct the full fault IPA.
Introduce FAR_TO_FIPA_OFFSET() to calculate this value in a shared
header and replace all open-coded instances to improve readability.
No functional change intended.
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/include/asm/kvm_arm.h | 2 ++
arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c | 2 +-
arch/arm64/kvm/inject_fault.c | 2 +-
arch/arm64/kvm/mmu.c | 4 ++--
4 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 1da290aeedce..73abe697bc2d 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -343,6 +343,8 @@
#define PAR_TO_HPFAR(par) \
(((par) & GENMASK_ULL(52 - 1, 12)) >> 8)
+#define FAR_TO_FIPA_OFFSET(far) ((far) & GENMASK_ULL(11, 0))
+
#define ECN(x) { ESR_ELx_EC_##x, #x }
#define kvm_arm_exception_class \
diff --git a/arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c b/arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c
index 78579b31a420..43d7dfa2c517 100644
--- a/arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c
+++ b/arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c
@@ -44,7 +44,7 @@ int __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu)
/* Build the full address */
fault_ipa = kvm_vcpu_get_fault_ipa(vcpu);
- fault_ipa |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
+ fault_ipa |= FAR_TO_FIPA_OFFSET(kvm_vcpu_get_hfar(vcpu));
/* If not for GICV, move on */
if (fault_ipa < vgic->vgic_cpu_base ||
diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c
index dfcd66c65517..c9bbce017dee 100644
--- a/arch/arm64/kvm/inject_fault.c
+++ b/arch/arm64/kvm/inject_fault.c
@@ -258,7 +258,7 @@ void kvm_inject_size_fault(struct kvm_vcpu *vcpu)
unsigned long addr, esr;
addr = kvm_vcpu_get_fault_ipa(vcpu);
- addr |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
+ addr |= FAR_TO_FIPA_OFFSET(kvm_vcpu_get_hfar(vcpu));
__kvm_inject_sea(vcpu, kvm_vcpu_trap_is_iabt(vcpu), addr);
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 7cc964af8d30..5b159cf0d203 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1959,7 +1959,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
/* Falls between the IPA range and the PARange? */
if (fault_ipa >= BIT_ULL(VTCR_EL2_IPA(vcpu->arch.hw_mmu->vtcr))) {
- fault_ipa |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
+ fault_ipa |= FAR_TO_FIPA_OFFSET(kvm_vcpu_get_hfar(vcpu));
return kvm_inject_sea(vcpu, is_iabt, fault_ipa);
}
@@ -2059,7 +2059,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
* faulting VA. This is always 12 bits, irrespective
* of the page size.
*/
- ipa |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
+ ipa |= FAR_TO_FIPA_OFFSET(kvm_vcpu_get_hfar(vcpu));
ret = io_mem_abort(vcpu, ipa);
goto out_unlock;
}
--
2.51.2.1041.gc1ab5b90ca-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v3 5/8] KVM: arm64: Include VM type when checking VM capabilities in pKVM
2025-11-10 13:45 [PATCH v3 0/8] KVM: arm64: Fixes for guest CPU feature trapping and enabling Fuad Tabba
` (3 preceding siblings ...)
2025-11-10 13:45 ` [PATCH v3 4/8] KVM: arm64: Introduce helper to calculate fault IPA offset Fuad Tabba
@ 2025-11-10 13:45 ` Fuad Tabba
2025-11-10 13:45 ` [PATCH v3 6/8] KVM: arm64: Do not allow KVM_CAP_ARM_MTE for any guest " Fuad Tabba
` (2 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Fuad Tabba @ 2025-11-10 13:45 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel
Cc: maz, oliver.upton, will, joey.gouly, suzuki.poulose, yuzenghui,
catalin.marinas, vladimir.murzin, tabba
Certian features and capabilities are restricted in protected mode. Most
of these features are restricted only for protected VMs, but some
are restricted for ALL VMs in protected mode.
Extend the pKVM capability check to pass the VM (kvm), and use that when
determining supported features. Moreover, extend the check to disallow
MTE for all VM types in protected mode.
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/include/asm/kvm_pkvm.h | 10 ++++++----
arch/arm64/kvm/arm.c | 4 ++--
arch/arm64/kvm/hyp/nvhe/pkvm.c | 10 +++++-----
3 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
index 08be89c95466..7195be508d99 100644
--- a/arch/arm64/include/asm/kvm_pkvm.h
+++ b/arch/arm64/include/asm/kvm_pkvm.h
@@ -23,10 +23,12 @@ void pkvm_destroy_hyp_vm(struct kvm *kvm);
int pkvm_create_hyp_vcpu(struct kvm_vcpu *vcpu);
/*
- * This functions as an allow-list of protected VM capabilities.
- * Features not explicitly allowed by this function are denied.
+ * Check whether the specific capability is allowed in pKVM.
+ *
+ * Certain features are allowed only for non-protected VMs in pKVM, which is why
+ * this takes the VM (kvm) as a parameter.
*/
-static inline bool kvm_pvm_ext_allowed(long ext)
+static inline bool kvm_pkvm_ext_allowed(struct kvm *kvm, long ext)
{
switch (ext) {
case KVM_CAP_IRQCHIP:
@@ -43,7 +45,7 @@ static inline bool kvm_pvm_ext_allowed(long ext)
case KVM_CAP_ARM_PTRAUTH_GENERIC:
return true;
default:
- return false;
+ return !kvm || !kvm_vm_is_protected(kvm);
}
}
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 870953b4a8a7..10d853f2722e 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -87,7 +87,7 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
if (cap->flags)
return -EINVAL;
- if (kvm_vm_is_protected(kvm) && !kvm_pvm_ext_allowed(cap->cap))
+ if (is_protected_kvm_enabled() && !kvm_pkvm_ext_allowed(kvm, cap->cap))
return -EINVAL;
switch (cap->cap) {
@@ -299,7 +299,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
{
int r;
- if (kvm && kvm_vm_is_protected(kvm) && !kvm_pvm_ext_allowed(ext))
+ if (is_protected_kvm_enabled() && !kvm_pkvm_ext_allowed(kvm, ext))
return 0;
switch (ext) {
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 7e370e31260d..290e4b46cd32 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -355,21 +355,21 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
set_bit(KVM_ARM_VCPU_PSCI_0_2, allowed_features);
- if (kvm_pvm_ext_allowed(KVM_CAP_ARM_PMU_V3))
+ if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_PMU_V3))
set_bit(KVM_ARM_VCPU_PMU_V3, allowed_features);
- if (kvm_pvm_ext_allowed(KVM_CAP_ARM_PTRAUTH_ADDRESS))
+ if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_PTRAUTH_ADDRESS))
set_bit(KVM_ARM_VCPU_PTRAUTH_ADDRESS, allowed_features);
- if (kvm_pvm_ext_allowed(KVM_CAP_ARM_PTRAUTH_GENERIC))
+ if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_PTRAUTH_GENERIC))
set_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, allowed_features);
- if (kvm_pvm_ext_allowed(KVM_CAP_ARM_SVE)) {
+ if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_SVE)) {
set_bit(KVM_ARM_VCPU_SVE, allowed_features);
kvm->arch.flags |= host_arch_flags & BIT(KVM_ARCH_FLAG_GUEST_HAS_SVE);
}
- if (kvm_pvm_ext_allowed(KVM_CAP_ARM_MTE)) {
+ if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_MTE)) {
set_bit(KVM_CAP_ARM_MTE, allowed_features);
kvm->arch.flags |= host_arch_flags & BIT(KVM_ARCH_FLAG_MTE_ENABLED);
}
--
2.51.2.1041.gc1ab5b90ca-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v3 6/8] KVM: arm64: Do not allow KVM_CAP_ARM_MTE for any guest in pKVM
2025-11-10 13:45 [PATCH v3 0/8] KVM: arm64: Fixes for guest CPU feature trapping and enabling Fuad Tabba
` (4 preceding siblings ...)
2025-11-10 13:45 ` [PATCH v3 5/8] KVM: arm64: Include VM type when checking VM capabilities in pKVM Fuad Tabba
@ 2025-11-10 13:45 ` Fuad Tabba
2025-11-10 13:45 ` [PATCH v3 7/8] KVM: arm64: Check whether a VM IOCTL is allowed " Fuad Tabba
2025-11-10 13:45 ` [PATCH v3 8/8] KVM: arm64: Prevent host from managing timer offsets for protected VMs Fuad Tabba
7 siblings, 0 replies; 11+ messages in thread
From: Fuad Tabba @ 2025-11-10 13:45 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel
Cc: maz, oliver.upton, will, joey.gouly, suzuki.poulose, yuzenghui,
catalin.marinas, vladimir.murzin, tabba
Supporting MTE in pKVM introduces significant complexity to the
hypervisor at EL2, even for non-protected VMs, since it would require
EL2 to handle tag management.
For now, do not allow KVM_CAP_ARM_MTE for all VM types in protected
mode.
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/include/asm/kvm_pkvm.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
index 7195be508d99..ea5d2275d16d 100644
--- a/arch/arm64/include/asm/kvm_pkvm.h
+++ b/arch/arm64/include/asm/kvm_pkvm.h
@@ -44,6 +44,9 @@ static inline bool kvm_pkvm_ext_allowed(struct kvm *kvm, long ext)
case KVM_CAP_ARM_PTRAUTH_ADDRESS:
case KVM_CAP_ARM_PTRAUTH_GENERIC:
return true;
+ case KVM_CAP_ARM_MTE:
+ /* Memory tagging is not supported at all in pKVM. */
+ return false;
default:
return !kvm || !kvm_vm_is_protected(kvm);
}
--
2.51.2.1041.gc1ab5b90ca-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v3 7/8] KVM: arm64: Check whether a VM IOCTL is allowed in pKVM
2025-11-10 13:45 [PATCH v3 0/8] KVM: arm64: Fixes for guest CPU feature trapping and enabling Fuad Tabba
` (5 preceding siblings ...)
2025-11-10 13:45 ` [PATCH v3 6/8] KVM: arm64: Do not allow KVM_CAP_ARM_MTE for any guest " Fuad Tabba
@ 2025-11-10 13:45 ` Fuad Tabba
2025-11-10 13:45 ` [PATCH v3 8/8] KVM: arm64: Prevent host from managing timer offsets for protected VMs Fuad Tabba
7 siblings, 0 replies; 11+ messages in thread
From: Fuad Tabba @ 2025-11-10 13:45 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel
Cc: maz, oliver.upton, will, joey.gouly, suzuki.poulose, yuzenghui,
catalin.marinas, vladimir.murzin, tabba
Certain VM IOCTLs are tied to specific VM features. Since pKVM does not
support all features, restrict which IOCTLs are allowed depending on
whether the associated feature is supported.
Use the existing VM capability check as the source of truth to whether
an IOCTL is allowed for a particular VM by mapping the IOCTLs with their
associated capabilities.
Suggested-by: Oliver Upton <oupton@kernel.org>
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/include/asm/kvm_pkvm.h | 20 ++++++++++++++++++++
arch/arm64/kvm/arm.c | 3 +++
2 files changed, 23 insertions(+)
diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
index ea5d2275d16d..a2b51590bed0 100644
--- a/arch/arm64/include/asm/kvm_pkvm.h
+++ b/arch/arm64/include/asm/kvm_pkvm.h
@@ -52,6 +52,26 @@ static inline bool kvm_pkvm_ext_allowed(struct kvm *kvm, long ext)
}
}
+/*
+ * Check whether the KVM IOCTL is allowed in pKVM.
+ *
+ * Certain features are allowed only for non-protected VMs in pKVM, which is why
+ * this takes the VM (kvm) as a parameter.
+ */
+static inline bool kvm_pkvm_ioctl_allowed(struct kvm *kvm, unsigned int ioctl)
+{
+ switch (ioctl) {
+ case KVM_CREATE_IRQCHIP:
+ return kvm_pkvm_ext_allowed(kvm, KVM_CAP_IRQCHIP);
+ case KVM_ARM_MTE_COPY_TAGS:
+ return kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_MTE);
+ case KVM_ARM_SET_COUNTER_OFFSET:
+ return kvm_pkvm_ext_allowed(kvm, KVM_CAP_COUNTER_OFFSET);
+ default:
+ return true;
+ }
+}
+
extern struct memblock_region kvm_nvhe_sym(hyp_memory)[];
extern unsigned int kvm_nvhe_sym(hyp_memblock_nr);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 10d853f2722e..020cadd811a3 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1879,6 +1879,9 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
void __user *argp = (void __user *)arg;
struct kvm_device_attr attr;
+ if (is_protected_kvm_enabled() && !kvm_pkvm_ioctl_allowed(kvm, ioctl))
+ return -EINVAL;
+
switch (ioctl) {
case KVM_CREATE_IRQCHIP: {
int ret;
--
2.51.2.1041.gc1ab5b90ca-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v3 8/8] KVM: arm64: Prevent host from managing timer offsets for protected VMs
2025-11-10 13:45 [PATCH v3 0/8] KVM: arm64: Fixes for guest CPU feature trapping and enabling Fuad Tabba
` (6 preceding siblings ...)
2025-11-10 13:45 ` [PATCH v3 7/8] KVM: arm64: Check whether a VM IOCTL is allowed " Fuad Tabba
@ 2025-11-10 13:45 ` Fuad Tabba
7 siblings, 0 replies; 11+ messages in thread
From: Fuad Tabba @ 2025-11-10 13:45 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel
Cc: maz, oliver.upton, will, joey.gouly, suzuki.poulose, yuzenghui,
catalin.marinas, vladimir.murzin, tabba
For protected VMs, the guest's timer offset state must not be controlled
by the host and must always run with a virtual counter offset of 0. The
existing timer logic allowed the host to set and manage the timer
counter offsets for protected VMs in certain cases.
Disable all host-side management of timer offsets for protected VMs by
adding checks in the relevant code paths.
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/kvm/arch_timer.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c
index 3f675875abea..f5407301c8f9 100644
--- a/arch/arm64/kvm/arch_timer.c
+++ b/arch/arm64/kvm/arch_timer.c
@@ -1056,10 +1056,14 @@ static void timer_context_init(struct kvm_vcpu *vcpu, int timerid)
ctxt->timer_id = timerid;
- if (timerid == TIMER_VTIMER)
- ctxt->offset.vm_offset = &kvm->arch.timer_data.voffset;
- else
- ctxt->offset.vm_offset = &kvm->arch.timer_data.poffset;
+ if (!kvm_vm_is_protected(vcpu->kvm)) {
+ if (timerid == TIMER_VTIMER)
+ ctxt->offset.vm_offset = &kvm->arch.timer_data.voffset;
+ else
+ ctxt->offset.vm_offset = &kvm->arch.timer_data.poffset;
+ } else {
+ ctxt->offset.vm_offset = NULL;
+ }
hrtimer_setup(&ctxt->hrtimer, kvm_hrtimer_expire, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
@@ -1083,7 +1087,8 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)
timer_context_init(vcpu, i);
/* Synchronize offsets across timers of a VM if not already provided */
- if (!test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, &vcpu->kvm->arch.flags)) {
+ if (!vcpu_is_protected(vcpu) &&
+ !test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, &vcpu->kvm->arch.flags)) {
timer_set_offset(vcpu_vtimer(vcpu), kvm_phys_timer_read());
timer_set_offset(vcpu_ptimer(vcpu), 0);
}
@@ -1687,6 +1692,9 @@ int kvm_vm_ioctl_set_counter_offset(struct kvm *kvm,
if (offset->reserved)
return -EINVAL;
+ if (kvm_vm_is_protected(kvm))
+ return -EINVAL;
+
mutex_lock(&kvm->lock);
if (!kvm_trylock_all_vcpus(kvm)) {
--
2.51.2.1041.gc1ab5b90ca-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread