[PATCH v1 26/26] KVM: s390: arm64: Finalize page fault handling
From: Steffen Eiden <seiden@linux.ibm.com>
Date: 2026-05-29 15:56:42
Also in:
kvm, kvmarm, linux-s390, lkml
Subsystem:
kernel virtual machine for s390 (kvm/s390), s390 architecture, the rest · Maintainers:
Christian Borntraeger, Janosch Frank, Claudio Imbrenda, Heiko Carstens, Vasily Gorbik, Alexander Gordeev, Linus Torvalds
Complete the page fault handling implementation by replacing temporary error returns with proper ARM64 exception injection. Signed-off-by: Steffen Eiden <seiden@linux.ibm.com> --- arch/s390/kvm/arm64/mmu.c | 62 ++++++++++++++++++++++----------- include/kvm/arm64/kvm_feature.h | 3 ++ 2 files changed, 44 insertions(+), 21 deletions(-)
diff --git a/arch/s390/kvm/arm64/mmu.c b/arch/s390/kvm/arm64/mmu.c
index 8759cbafbaff..4224503276fb 100644
--- a/arch/s390/kvm/arm64/mmu.c
+++ b/arch/s390/kvm/arm64/mmu.c@@ -31,12 +31,14 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, gpa_t fault_ipa, ret = kvm_s390_faultin_gfn(vcpu, NULL, &f); if (ret <= 0) return ret; - if (ret == PGM_ADDRESSING) + if (ret == PGM_ADDRESSING) { /* - * Without the relevant sysregs we cannot do anything for now. - * Go back to userspace with an error. TODO sysreg handling + * There is no page with the requested address. Inject size fault + * which is the closest arm match to PGM-addressing */ - return -ENOEXEC; + kvm_inject_size_fault(vcpu); + return 1; + } KVM_BUG_ON(ret, vcpu->kvm); return -EINVAL; }
@@ -69,6 +71,27 @@ static int kvm_handle_pic(struct kvm_vcpu *vcpu, bool *translation) return 0; } +static u32 kvm_get_pa_bits(struct kvm *kvm) +{ + u32 id_parange = get_idreg_field_enum(kvm, ID_AA64MMFR0_EL1, PARANGE); + + switch (id_parange) { + case ID_AA64MMFR0_EL1_PARANGE_32: return 32; + case ID_AA64MMFR0_EL1_PARANGE_36: return 36; + case ID_AA64MMFR0_EL1_PARANGE_40: return 40; + case ID_AA64MMFR0_EL1_PARANGE_42: return 42; + case ID_AA64MMFR0_EL1_PARANGE_44: return 44; + case ID_AA64MMFR0_EL1_PARANGE_48: return 48; + case ID_AA64MMFR0_EL1_PARANGE_52: return 52; + case ID_AA64MMFR0_EL1_PARANGE_56: return 56; + } + /* + * Future values must be higher than we know already. + * See ARM DDI 0487C.a. Return a safe limit. + */ + return ID_AA64MMFR0_EL1_PARANGE_48; +} + int kvm_handle_guest_abort(struct kvm_vcpu *vcpu) { struct kvm_memory_slot *memslot;
@@ -98,16 +121,17 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu) } if (translation) { - /* - * For both cases: - * Without the relevant sysregs we cannot do anything for now. - * Go back to userspace with an error. TODO sysreg handling - */ - if (fault_ipa >= BIT_ULL(get_kvm_ipa_limit())) - return -ENOEXEC; + /* Beyond sanitised PA range (which is the IPA limit) */ + if (fault_ipa >= BIT_ULL(kvm_get_pa_bits(vcpu->kvm))) { + kvm_inject_size_fault(vcpu); + return 1; + } - if (fault_ipa >= kvm_phys_size(vcpu->kvm)) - return -ENOEXEC; + /* Falls between the IPA range and the PA range? */ + if (fault_ipa >= kvm_phys_size(vcpu->kvm)) { + fault_ipa |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0); + return kvm_inject_sea(vcpu, is_iabt, fault_ipa); + } } idx = srcu_read_lock(&vcpu->kvm->srcu);
@@ -123,18 +147,14 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu) * The guest has put either its instructions or its page-tables * somewhere it shouldn't have. Userspace won't be able to do * anything about this (there's no syndrome for a start). - * - * Without the relevant sysregs we cannot do anything for now. - * Go back to userspace with an error. TODO sysreg handling */ - if (is_iabt) + if (is_iabt) { + ret = kvm_inject_sea_iabt(vcpu, kvm_vcpu_get_hfar(vcpu)); goto out_unlock; + } if (kvm_vcpu_abt_iss1tw(vcpu)) { - /* - * Without the relevant sysregs we cannot do anything for now. - * Go back to userspace with an error. TODO sysreg handling - */ + ret = kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu)); goto out_unlock; }
diff --git a/include/kvm/arm64/kvm_feature.h b/include/kvm/arm64/kvm_feature.h
index 945abbbf1aa8..193c9ca7045f 100644
--- a/include/kvm/arm64/kvm_feature.h
+++ b/include/kvm/arm64/kvm_feature.h@@ -47,6 +47,9 @@ #define kvm_has_feat_enum(kvm, ...) __kvm_has_feat_enum(kvm, __VA_ARGS__) +#define get_idreg_field_enum(kvm, id, fld) \ + SYS_FIELD_GET(id, fld, kvm_read_vm_id_reg((kvm), SYS_##id)) + /* Check for a given level of PAuth support */ #define kvm_has_pauth(k, l) \ ({ \
--
2.53.0