diff mbox series

[v2,33/43] arm64: rme: Enable PMU support with a realm guest

Message ID 20240412084309.1733783-34-steven.price@arm.com (mailing list archive)
State New, archived
Headers show
Series arm64: Support for Arm CCA in KVM | expand

Commit Message

Steven Price April 12, 2024, 8:42 a.m. UTC
Use the PMU registers from the RmiRecExit structure to identify when an
overflow interrupt is due and inject it into the guest. Also hook up the
configuration option for enabling the PMU within the guest.

When entering a realm guest with a PMU interrupt pending, it is
necessary to disable the physical interrupt. Otherwise when the RMM
restores the PMU state the physical interrupt will trigger causing an
immediate exit back to the host. The guest is expected to acknowledge
the interrupt causing a host exit (to update the GIC state) which gives
the opportunity to re-enable the physical interrupt before the next PMU
event.

Number of PMU counters is configured by the VMM by writing to PMCR.N.

Signed-off-by: Steven Price <steven.price@arm.com>
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
 arch/arm64/kvm/arm.c      | 15 +++++++++++++++
 arch/arm64/kvm/guest.c    |  7 +++++++
 arch/arm64/kvm/pmu-emul.c |  4 +++-
 arch/arm64/kvm/rme.c      |  8 ++++++++
 arch/arm64/kvm/sys_regs.c |  2 +-
 5 files changed, 34 insertions(+), 2 deletions(-)

Comments

kernel test robot April 13, 2024, 11:44 p.m. UTC | #1
Hi Steven,

kernel test robot noticed the following build errors:

[auto build test ERROR on kvmarm/next]
[also build test ERROR on kvm/queue arm64/for-next/core linus/master v6.9-rc3 next-20240412]
[cannot apply to kvm/linux-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Steven-Price/KVM-Prepare-for-handling-only-shared-mappings-in-mmu_notifier-events/20240412-170311
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm.git next
patch link:    https://lore.kernel.org/r/20240412084309.1733783-34-steven.price%40arm.com
patch subject: [PATCH v2 33/43] arm64: rme: Enable PMU support with a realm guest
config: arm64-randconfig-r064-20240414 (https://download.01.org/0day-ci/archive/20240414/202404140723.GKwnJxeZ-lkp@intel.com/config)
compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project 8b3b4a92adee40483c27f26c478a384cd69c6f05)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240414/202404140723.GKwnJxeZ-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202404140723.GKwnJxeZ-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from arch/arm64/kvm/arm.c:9:
   In file included from include/linux/entry-kvm.h:6:
   In file included from include/linux/resume_user_mode.h:8:
   In file included from include/linux/memcontrol.h:21:
   In file included from include/linux/mm.h:2208:
   include/linux/vmstat.h:508:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     508 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     509 |                            item];
         |                            ~~~~
   include/linux/vmstat.h:515:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     515 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     516 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:522:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
     522 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
   include/linux/vmstat.h:527:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     527 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     528 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:536:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     536 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     537 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
>> arch/arm64/kvm/arm.c:1115:13: error: no member named 'irq_level' in 'struct kvm_pmu'
    1115 |                         if (pmu->irq_level) {
         |                             ~~~  ^
>> arch/arm64/kvm/arm.c:1117:5: error: call to undeclared function 'arm_pmu_set_phys_irq'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    1117 |                                 arm_pmu_set_phys_irq(false);
         |                                 ^
   arch/arm64/kvm/arm.c:1224:4: error: call to undeclared function 'arm_pmu_set_phys_irq'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    1224 |                         arm_pmu_set_phys_irq(true);
         |                         ^
   5 warnings and 3 errors generated.


vim +1115 arch/arm64/kvm/arm.c

  1044	
  1045	/**
  1046	 * kvm_arch_vcpu_ioctl_run - the main VCPU run function to execute guest code
  1047	 * @vcpu:	The VCPU pointer
  1048	 *
  1049	 * This function is called through the VCPU_RUN ioctl called from user space. It
  1050	 * will execute VM code in a loop until the time slice for the process is used
  1051	 * or some emulation is needed from user space in which case the function will
  1052	 * return with return value 0 and with the kvm_run structure filled in with the
  1053	 * required data for the requested emulation.
  1054	 */
  1055	int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
  1056	{
  1057		struct kvm_run *run = vcpu->run;
  1058		int ret;
  1059	
  1060		if (run->exit_reason == KVM_EXIT_MMIO) {
  1061			ret = kvm_handle_mmio_return(vcpu);
  1062			if (ret)
  1063				return ret;
  1064		}
  1065	
  1066		vcpu_load(vcpu);
  1067	
  1068		if (run->immediate_exit) {
  1069			ret = -EINTR;
  1070			goto out;
  1071		}
  1072	
  1073		kvm_sigset_activate(vcpu);
  1074	
  1075		ret = 1;
  1076		run->exit_reason = KVM_EXIT_UNKNOWN;
  1077		run->flags = 0;
  1078		while (ret > 0) {
  1079			bool pmu_stopped = false;
  1080	
  1081			/*
  1082			 * Check conditions before entering the guest
  1083			 */
  1084			ret = xfer_to_guest_mode_handle_work(vcpu);
  1085			if (!ret)
  1086				ret = 1;
  1087	
  1088			if (ret > 0)
  1089				ret = check_vcpu_requests(vcpu);
  1090	
  1091			/*
  1092			 * Preparing the interrupts to be injected also
  1093			 * involves poking the GIC, which must be done in a
  1094			 * non-preemptible context.
  1095			 */
  1096			preempt_disable();
  1097	
  1098			/*
  1099			 * The VMID allocator only tracks active VMIDs per
  1100			 * physical CPU, and therefore the VMID allocated may not be
  1101			 * preserved on VMID roll-over if the task was preempted,
  1102			 * making a thread's VMID inactive. So we need to call
  1103			 * kvm_arm_vmid_update() in non-premptible context.
  1104			 */
  1105			if (kvm_arm_vmid_update(&vcpu->arch.hw_mmu->vmid) &&
  1106			    has_vhe())
  1107				__load_stage2(vcpu->arch.hw_mmu,
  1108					      vcpu->arch.hw_mmu->arch);
  1109	
  1110			kvm_pmu_flush_hwstate(vcpu);
  1111	
  1112			if (vcpu_is_rec(vcpu)) {
  1113				struct kvm_pmu *pmu = &vcpu->arch.pmu;
  1114	
> 1115				if (pmu->irq_level) {
  1116					pmu_stopped = true;
> 1117					arm_pmu_set_phys_irq(false);
  1118				}
  1119			}
  1120	
  1121			local_irq_disable();
  1122	
  1123			kvm_vgic_flush_hwstate(vcpu);
  1124	
  1125			kvm_pmu_update_vcpu_events(vcpu);
  1126	
  1127			/*
  1128			 * Ensure we set mode to IN_GUEST_MODE after we disable
  1129			 * interrupts and before the final VCPU requests check.
  1130			 * See the comment in kvm_vcpu_exiting_guest_mode() and
  1131			 * Documentation/virt/kvm/vcpu-requests.rst
  1132			 */
  1133			smp_store_mb(vcpu->mode, IN_GUEST_MODE);
  1134	
  1135			if (ret <= 0 || kvm_vcpu_exit_request(vcpu, &ret)) {
  1136				vcpu->mode = OUTSIDE_GUEST_MODE;
  1137				isb(); /* Ensure work in x_flush_hwstate is committed */
  1138				kvm_pmu_sync_hwstate(vcpu);
  1139				if (static_branch_unlikely(&userspace_irqchip_in_use))
  1140					kvm_timer_sync_user(vcpu);
  1141				kvm_vgic_sync_hwstate(vcpu);
  1142				local_irq_enable();
  1143				preempt_enable();
  1144				continue;
  1145			}
  1146	
  1147			kvm_arm_setup_debug(vcpu);
  1148			kvm_arch_vcpu_ctxflush_fp(vcpu);
  1149	
  1150			/**************************************************************
  1151			 * Enter the guest
  1152			 */
  1153			trace_kvm_entry(*vcpu_pc(vcpu));
  1154			guest_timing_enter_irqoff();
  1155	
  1156			if (vcpu_is_rec(vcpu))
  1157				ret = kvm_rec_enter(vcpu);
  1158			else
  1159				ret = kvm_arm_vcpu_enter_exit(vcpu);
  1160	
  1161			vcpu->mode = OUTSIDE_GUEST_MODE;
  1162			vcpu->stat.exits++;
  1163			/*
  1164			 * Back from guest
  1165			 *************************************************************/
  1166	
  1167			kvm_arm_clear_debug(vcpu);
  1168	
  1169			/*
  1170			 * We must sync the PMU state before the vgic state so
  1171			 * that the vgic can properly sample the updated state of the
  1172			 * interrupt line.
  1173			 */
  1174			kvm_pmu_sync_hwstate(vcpu);
  1175	
  1176			/*
  1177			 * Sync the vgic state before syncing the timer state because
  1178			 * the timer code needs to know if the virtual timer
  1179			 * interrupts are active.
  1180			 */
  1181			kvm_vgic_sync_hwstate(vcpu);
  1182	
  1183			/*
  1184			 * Sync the timer hardware state before enabling interrupts as
  1185			 * we don't want vtimer interrupts to race with syncing the
  1186			 * timer virtual interrupt state.
  1187			 */
  1188			if (static_branch_unlikely(&userspace_irqchip_in_use))
  1189				kvm_timer_sync_user(vcpu);
  1190	
  1191			kvm_arch_vcpu_ctxsync_fp(vcpu);
  1192	
  1193			/*
  1194			 * We must ensure that any pending interrupts are taken before
  1195			 * we exit guest timing so that timer ticks are accounted as
  1196			 * guest time. Transiently unmask interrupts so that any
  1197			 * pending interrupts are taken.
  1198			 *
  1199			 * Per ARM DDI 0487G.b section D1.13.4, an ISB (or other
  1200			 * context synchronization event) is necessary to ensure that
  1201			 * pending interrupts are taken.
  1202			 */
  1203			if (ARM_EXCEPTION_CODE(ret) == ARM_EXCEPTION_IRQ) {
  1204				local_irq_enable();
  1205				isb();
  1206				local_irq_disable();
  1207			}
  1208	
  1209			guest_timing_exit_irqoff();
  1210	
  1211			local_irq_enable();
  1212	
  1213			/* Exit types that need handling before we can be preempted */
  1214			if (!vcpu_is_rec(vcpu)) {
  1215				trace_kvm_exit(ret, kvm_vcpu_trap_get_class(vcpu),
  1216					       *vcpu_pc(vcpu));
  1217	
  1218				handle_exit_early(vcpu, ret);
  1219			}
  1220	
  1221			preempt_enable();
  1222	
  1223			if (pmu_stopped)
  1224				arm_pmu_set_phys_irq(true);
  1225	
  1226			/*
  1227			 * The ARMv8 architecture doesn't give the hypervisor
  1228			 * a mechanism to prevent a guest from dropping to AArch32 EL0
  1229			 * if implemented by the CPU. If we spot the guest in such
  1230			 * state and that we decided it wasn't supposed to do so (like
  1231			 * with the asymmetric AArch32 case), return to userspace with
  1232			 * a fatal error.
  1233			 */
  1234			if (vcpu_mode_is_bad_32bit(vcpu)) {
  1235				/*
  1236				 * As we have caught the guest red-handed, decide that
  1237				 * it isn't fit for purpose anymore by making the vcpu
  1238				 * invalid. The VMM can try and fix it by issuing  a
  1239				 * KVM_ARM_VCPU_INIT if it really wants to.
  1240				 */
  1241				vcpu_clear_flag(vcpu, VCPU_INITIALIZED);
  1242				ret = ARM_EXCEPTION_IL;
  1243			}
  1244	
  1245			if (vcpu_is_rec(vcpu))
  1246				ret = handle_rme_exit(vcpu, ret);
  1247			else
  1248				ret = handle_exit(vcpu, ret);
  1249		}
  1250	
  1251		/* Tell userspace about in-kernel device output levels */
  1252		if (unlikely(!irqchip_in_kernel(vcpu->kvm))) {
  1253			kvm_timer_update_run(vcpu);
  1254			kvm_pmu_update_run(vcpu);
  1255		}
  1256	
  1257		kvm_sigset_deactivate(vcpu);
  1258	
  1259	out:
  1260		/*
  1261		 * In the unlikely event that we are returning to userspace
  1262		 * with pending exceptions or PC adjustment, commit these
  1263		 * adjustments in order to give userspace a consistent view of
  1264		 * the vcpu state. Note that this relies on __kvm_adjust_pc()
  1265		 * being preempt-safe on VHE.
  1266		 */
  1267		if (unlikely(vcpu_get_flag(vcpu, PENDING_EXCEPTION) ||
  1268			     vcpu_get_flag(vcpu, INCREMENT_PC)))
  1269			kvm_call_hyp(__kvm_adjust_pc, vcpu);
  1270	
  1271		vcpu_put(vcpu);
  1272		return ret;
  1273	}
  1274
Suzuki K Poulose April 18, 2024, 4:06 p.m. UTC | #2
On 14/04/2024 00:44, kernel test robot wrote:
> Hi Steven,
> 
> kernel test robot noticed the following build errors:
> 
> [auto build test ERROR on kvmarm/next]
> [also build test ERROR on kvm/queue arm64/for-next/core linus/master v6.9-rc3 next-20240412]
> [cannot apply to kvm/linux-next]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/Steven-Price/KVM-Prepare-for-handling-only-shared-mappings-in-mmu_notifier-events/20240412-170311
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm.git next
> patch link:    https://lore.kernel.org/r/20240412084309.1733783-34-steven.price%40arm.com
> patch subject: [PATCH v2 33/43] arm64: rme: Enable PMU support with a realm guest
> config: arm64-randconfig-r064-20240414 (https://download.01.org/0day-ci/archive/20240414/202404140723.GKwnJxeZ-lkp@intel.com/config)
> compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project 8b3b4a92adee40483c27f26c478a384cd69c6f05)
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240414/202404140723.GKwnJxeZ-lkp@intel.com/reproduce)
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202404140723.GKwnJxeZ-lkp@intel.com/

I guess the problem is with CONFIG_HW_PERF_EVENT not set, arm_pmu is an
empty struct, triggering all these errors.

Suzuki
diff mbox series

Patch

diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index dcd9089877f3..2aad83053b62 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -15,6 +15,7 @@ 
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
 #include <linux/mman.h>
+#include <linux/perf/arm_pmu.h>
 #include <linux/sched.h>
 #include <linux/kvm.h>
 #include <linux/kvm_irqfd.h>
@@ -1075,6 +1076,8 @@  int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
 	run->exit_reason = KVM_EXIT_UNKNOWN;
 	run->flags = 0;
 	while (ret > 0) {
+		bool pmu_stopped = false;
+
 		/*
 		 * Check conditions before entering the guest
 		 */
@@ -1106,6 +1109,15 @@  int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
 
 		kvm_pmu_flush_hwstate(vcpu);
 
+		if (vcpu_is_rec(vcpu)) {
+			struct kvm_pmu *pmu = &vcpu->arch.pmu;
+
+			if (pmu->irq_level) {
+				pmu_stopped = true;
+				arm_pmu_set_phys_irq(false);
+			}
+		}
+
 		local_irq_disable();
 
 		kvm_vgic_flush_hwstate(vcpu);
@@ -1208,6 +1220,9 @@  int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
 
 		preempt_enable();
 
+		if (pmu_stopped)
+			arm_pmu_set_phys_irq(true);
+
 		/*
 		 * The ARMv8 architecture doesn't give the hypervisor
 		 * a mechanism to prevent a guest from dropping to AArch32 EL0
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 5223a828a344..d35367cf527d 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -782,6 +782,8 @@  int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	return kvm_arm_sys_reg_get_reg(vcpu, reg);
 }
 
+#define KVM_REG_ARM_PMCR_EL0		ARM64_SYS_REG(3, 3, 9, 12, 0)
+
 /*
  * The RMI ABI only enables setting the lower GPRs (x0-x7) and PC.
  * All other registers are reset to architectural or otherwise defined reset
@@ -800,6 +802,11 @@  static bool validate_realm_set_reg(struct kvm_vcpu *vcpu,
 		case KVM_REG_ARM_CORE_REG(regs.pc):
 			return true;
 		}
+	} else {
+		switch (reg->id) {
+		case KVM_REG_ARM_PMCR_EL0:
+			return true;
+		}
 	}
 
 	return false;
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index a35ce10e0a9f..ce7c8e55d904 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -341,7 +341,9 @@  static u64 kvm_pmu_overflow_status(struct kvm_vcpu *vcpu)
 {
 	u64 reg = 0;
 
-	if ((kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E)) {
+	if (vcpu_is_rec(vcpu)) {
+		reg = vcpu->arch.rec.run->exit.pmu_ovf_status;
+	} else if ((kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E)) {
 		reg = __vcpu_sys_reg(vcpu, PMOVSSET_EL0);
 		reg &= __vcpu_sys_reg(vcpu, PMCNTENSET_EL0);
 		reg &= __vcpu_sys_reg(vcpu, PMINTENSET_EL1);
diff --git a/arch/arm64/kvm/rme.c b/arch/arm64/kvm/rme.c
index ae9fd12c4e7d..e60a1196a2fe 100644
--- a/arch/arm64/kvm/rme.c
+++ b/arch/arm64/kvm/rme.c
@@ -314,6 +314,11 @@  static int realm_create_rd(struct kvm *kvm)
 	params->rtt_base = kvm->arch.mmu.pgd_phys;
 	params->vmid = realm->vmid;
 
+	if (kvm->arch.arm_pmu) {
+		params->pmu_num_ctrs = kvm->arch.pmcr_n;
+		params->flags |= RMI_REALM_PARAM_FLAG_PMU;
+	}
+
 	params_phys = virt_to_phys(params);
 
 	if (rmi_realm_create(rd_phys, params_phys)) {
@@ -1366,6 +1371,9 @@  int kvm_create_rec(struct kvm_vcpu *vcpu)
 	if (!vcpu_has_feature(vcpu, KVM_ARM_VCPU_PSCI_0_2))
 		return -EINVAL;
 
+	if (vcpu->kvm->arch.arm_pmu && !kvm_vcpu_has_pmu(vcpu))
+		return -EINVAL;
+
 	BUILD_BUG_ON(sizeof(*params) > PAGE_SIZE);
 	BUILD_BUG_ON(sizeof(*rec->run) > PAGE_SIZE);
 
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index c9f4f387155f..60452c6519a4 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1279,7 +1279,7 @@  static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
 	 * implements. Ignore this error to maintain compatibility
 	 * with the existing KVM behavior.
 	 */
-	if (!kvm_vm_has_ran_once(kvm) &&
+	if (!kvm_vm_has_ran_once(kvm) && !kvm_realm_is_created(kvm) &&
 	    new_n <= kvm_arm_pmu_get_max_counters(kvm))
 		kvm->arch.pmcr_n = new_n;