[PATCH v7 36/45] arm64: RME: Set breakpoint parameters through SET_ONE_REG

From: Steven Price
Date: Thu Feb 13 2025 - 11:33:01 EST


From: Jean-Philippe Brucker <jean-philippe@xxxxxxxxxx>

Allow userspace to configure the number of breakpoints and watchpoints
of a Realm VM through KVM_SET_ONE_REG ID_AA64DFR0_EL1.

The KVM sys_reg handler checks the user value against the maximum value
given by RMM (arm64_check_features() gets it from the
read_sanitised_id_aa64dfr0_el1() reset handler).

Userspace discovers that it can write these fields by issuing a
KVM_ARM_GET_REG_WRITABLE_MASKS ioctl.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@xxxxxxxxxx>
Signed-off-by: Steven Price <steven.price@xxxxxxx>
---
arch/arm64/kvm/guest.c | 2 ++
arch/arm64/kvm/rme.c | 3 +++
arch/arm64/kvm/sys_regs.c | 17 +++++++++++------
3 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index da8961a5d01a..dbd45d0a0d54 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -802,6 +802,7 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
}

#define KVM_REG_ARM_PMCR_EL0 ARM64_SYS_REG(3, 3, 9, 12, 0)
+#define KVM_REG_ARM_ID_AA64DFR0_EL1 ARM64_SYS_REG(3, 0, 0, 5, 0)

/*
* The RMI ABI only enables setting some GPRs and PC. The selection of GPRs
@@ -820,6 +821,7 @@ static bool validate_realm_set_reg(struct kvm_vcpu *vcpu,
} else {
switch (reg->id) {
case KVM_REG_ARM_PMCR_EL0:
+ case KVM_REG_ARM_ID_AA64DFR0_EL1:
return true;
}
}
diff --git a/arch/arm64/kvm/rme.c b/arch/arm64/kvm/rme.c
index 8c426f575728..f236e1776d8c 100644
--- a/arch/arm64/kvm/rme.c
+++ b/arch/arm64/kvm/rme.c
@@ -355,6 +355,7 @@ static int realm_create_rd(struct kvm *kvm)
void *rd = NULL;
phys_addr_t rd_phys, params_phys;
size_t pgd_size = kvm_pgtable_stage2_pgd_size(kvm->arch.mmu.vtcr);
+ u64 dfr0 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64DFR0_EL1);
int i, r;
int rtt_num_start;

@@ -391,6 +392,8 @@ static int realm_create_rd(struct kvm *kvm)
params->rtt_num_start = rtt_num_start;
params->rtt_base = kvm->arch.mmu.pgd_phys;
params->vmid = realm->vmid;
+ params->num_bps = SYS_FIELD_GET(ID_AA64DFR0_EL1, BRPs, dfr0);
+ params->num_wps = SYS_FIELD_GET(ID_AA64DFR0_EL1, WRPs, dfr0);

if (kvm->arch.arm_pmu) {
params->pmu_num_ctrs = kvm->arch.pmcr_n;
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 5618eff33155..efd86df4e59d 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1829,6 +1829,9 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
{
u8 debugver = SYS_FIELD_GET(ID_AA64DFR0_EL1, DebugVer, val);
u8 pmuver = SYS_FIELD_GET(ID_AA64DFR0_EL1, PMUVer, val);
+ u8 bps = SYS_FIELD_GET(ID_AA64DFR0_EL1, BRPs, val);
+ u8 wps = SYS_FIELD_GET(ID_AA64DFR0_EL1, WRPs, val);
+ u8 ctx_cmps = SYS_FIELD_GET(ID_AA64DFR0_EL1, CTX_CMPs, val);

/*
* Prior to commit 3d0dba5764b9 ("KVM: arm64: PMU: Move the
@@ -1848,10 +1851,11 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;

/*
- * ID_AA64DFR0_EL1.DebugVer is one of those awkward fields with a
- * nonzero minimum safe value.
+ * ID_AA64DFR0_EL1.DebugVer, BRPs and WRPs all have to be greater than
+ * zero. CTX_CMPs is never greater than BRPs.
*/
- if (debugver < ID_AA64DFR0_EL1_DebugVer_IMP)
+ if (debugver < ID_AA64DFR0_EL1_DebugVer_IMP || !bps || !wps ||
+ ctx_cmps > bps)
return -EINVAL;

return set_id_reg(vcpu, rd, val);
@@ -1998,10 +2002,11 @@ static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
mutex_lock(&vcpu->kvm->arch.config_lock);

/*
- * Once the VM has started the ID registers are immutable. Reject any
- * write that does not match the final register value.
+ * Once the VM has started or the Realm descriptor is created, the ID
+ * registers are immutable. Reject any write that does not match the
+ * final register value.
*/
- if (kvm_vm_has_ran_once(vcpu->kvm)) {
+ if (kvm_vm_has_ran_once(vcpu->kvm) || kvm_realm_is_created(vcpu->kvm)) {
if (val != read_id_reg(vcpu, rd))
ret = -EBUSY;
else
--
2.43.0