static DEFINE_PER_CPU(unsigned char, kvm_arm_hardware_enabled);
 DEFINE_STATIC_KEY_FALSE(userspace_irqchip_in_use);
 
+extern u64 kvm_nvhe_sym(__cpu_logical_map)[NR_CPUS];
+
 int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
 {
        return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
 }
 #endif
 
+static void init_cpu_logical_map(void)
+{
+       unsigned int cpu;
+
+       /*
+        * Copy the MPIDR <-> logical CPU ID mapping to hyp.
+        * Only copy the set of online CPUs whose features have been chacked
+        * against the finalized system capabilities. The hypervisor will not
+        * allow any other CPUs from the `possible` set to boot.
+        */
+       for_each_online_cpu(cpu)
+               kvm_nvhe_sym(__cpu_logical_map)[cpu] = cpu_logical_map(cpu);
+}
+
 static int init_common_resources(void)
 {
        return kvm_set_ipa_limit();
                }
        }
 
+       if (is_protected_kvm_enabled())
+               init_cpu_logical_map();
+
        return 0;
 
 out_err:
 
 #include <asm/kvm_hyp.h>
 #include <asm/kvm_mmu.h>
 
+/*
+ * nVHE copy of data structures tracking available CPU cores.
+ * Only entries for CPUs that were online at KVM init are populated.
+ * Other CPUs should not be allowed to boot because their features were
+ * not checked against the finalized system capabilities.
+ */
+u64 __ro_after_init __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };
+
+u64 cpu_logical_map(unsigned int cpu)
+{
+       if (cpu >= ARRAY_SIZE(__cpu_logical_map))
+               hyp_panic();
+
+       return __cpu_logical_map[cpu];
+}
+
 unsigned long __hyp_per_cpu_offset(unsigned int cpu)
 {
        unsigned long *cpu_base_array;