kvm: arm64: Restrict {GET,SET}_ONE_REG for SPCI partitions

User space is only allowed to access x0-7 for arguments before the first
run and after that point is not permitted to modify or observe any
register state.
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 57ae016..5ebb5ca 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -401,6 +401,11 @@
 
 #define vcpu_gp_regs(v)		(&(v)->arch.ctxt.gp_regs)
 
+static inline u64 core_reg_offset_from_id(u64 id)
+{
+	return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);
+}
+
 /*
  * Only use __vcpu_sys_reg if you know you want the memory backed version of a
  * register, and not the one most recently accessed by a running VCPU.  For
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index f958a61..d2cff69 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1120,6 +1120,10 @@
 		if (copy_from_user(&reg, argp, sizeof(reg)))
 			break;
 
+		r = kvm_spci_check_vcpu_access_reg(vcpu, &reg);
+		if (r)
+			break;
+
 		if (ioctl == KVM_SET_ONE_REG)
 			r = kvm_arm_set_reg(vcpu, &reg);
 		else
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 23ebe51..fae63c3 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -52,11 +52,6 @@
 		off < KVM_REG_ARM_CORE_REG(fp_regs.fpsr);
 }
 
-static u64 core_reg_offset_from_id(u64 id)
-{
-	return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);
-}
-
 static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, u64 off)
 {
 	int size;
diff --git a/arch/arm64/kvm/spci.c b/arch/arm64/kvm/spci.c
index 90e178b..4a1d998 100644
--- a/arch/arm64/kvm/spci.c
+++ b/arch/arm64/kvm/spci.c
@@ -541,6 +541,35 @@
 	return 0;
 }
 
+/*
+ * Checks the registers can be accessed by user space. If the vCPU is part of an
+ * SPCI partition, the only registers that can be accessed are x0-7 and,
+ * further, they can only be accessed before the first run. This allows user
+ * space to pass in some initial arguments but does not allow it the
+ * subsequently modify or observe the registers state of the vCPU.
+ */
+int kvm_spci_check_vcpu_access_reg(struct kvm_vcpu *vcpu,
+				   struct kvm_one_reg *reg)
+{
+	const struct kvm_spci_partition *part = part_get_linked(vcpu->kvm);
+
+	if (!part)
+		return 0;
+
+	if (likely(vcpu->arch.has_run_once))
+		return -EPERM;
+
+	switch (core_reg_offset_from_id(reg->id)) {
+	case KVM_REG_ARM_CORE_REG(regs.regs[0]) ...
+	     KVM_REG_ARM_CORE_REG(regs.regs[7]):
+		     break;
+	default:
+		return -EPERM;
+	}
+
+	return 0;
+}
+
 /* Early memory reservation parsing. */
 static int __init spci_rmem_err(const char *type, struct reserved_mem *rmem,
 				const char *reason)
diff --git a/include/kvm/arm_spci.h b/include/kvm/arm_spci.h
index 872e34b..5bd7071 100644
--- a/include/kvm/arm_spci.h
+++ b/include/kvm/arm_spci.h
@@ -45,6 +45,8 @@
 int kvm_spci_check_vcpu_init_features(const struct kvm_vcpu *vcpu,
 				      const struct kvm_vcpu_init *init);
 int kvm_spci_vcpu_first_run_init(struct kvm_vcpu *vcpu);
+int kvm_spci_check_vcpu_access_reg(struct kvm_vcpu *vcpu,
+				   struct kvm_one_reg *reg);
 
 #else
 
@@ -64,6 +66,11 @@
 {
 	return 0;
 }
+int kvm_spci_check_vcpu_access_reg(struct kvm_vcpu *vcpu,
+				   struct kvm_one_reg *reg)
+{
+	return 0;
+}
 
 #endif /* CONFIG_KVM_ARM_SPCI */
 #endif	/* __KVM_ARM_SPCI_H */