If the guest does not explicitly set the GPA of vcpu_info structure in
memory then, for guests with 32 vCPUs or fewer, the vcpu_info embedded
in the shared_info page may be used. As described in a previous commit,
the shared_info page is an overlay at a fixed HVA within the VMM, so in
this case it also more optimal to activate the vcpu_info cache with a
fixed HVA to avoid unnecessary invalidation if the guest memory layout
is modified.
Signed-off-by: Paul Durrant <pdurrant@amazon.com>
Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
Link: https://lore.kernel.org/r/20240215152916.1158-14-paul@xen.org
[sean: use kvm_gpc_is_{gpa,hva}_active()]
Signed-off-by: Sean Christopherson <seanjc@google.com>
 
   Sets the guest physical frame number at which the Xen shared_info
   page resides. Note that although Xen places vcpu_info for the first
   32 vCPUs in the shared_info page, KVM does not automatically do so
-  and instead requires that KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO be used
-  explicitly even when the vcpu_info for a given vCPU resides at the
-  "default" location in the shared_info page. This is because KVM may
-  not be aware of the Xen CPU id which is used as the index into the
-  vcpu_info[] array, so may know the correct default location.
+  and instead requires that KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO or
+  KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO_HVA be used explicitly even when
+  the vcpu_info for a given vCPU resides at the "default" location
+  in the shared_info page. This is because KVM may not be aware of
+  the Xen CPU id which is used as the index into the vcpu_info[]
+  array, so may know the correct default location.
 
   Note that the shared_info page may be constantly written to by KVM;
   it contains the event channel bitmap used to deliver interrupts to
   on dirty logging. Setting the gpa to KVM_XEN_INVALID_GPA will disable
   the vcpu_info.
 
+KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO_HVA
+  If the KVM_XEN_HVM_CONFIG_SHARED_INFO_HVA flag is also set in the
+  Xen capabilities, then this attribute may be used to set the
+  userspace address of the vcpu_info for a given vCPU. It should
+  only be used when the vcpu_info resides at the "default" location
+  in the shared_info page. In this case it is safe to assume the
+  userspace address will not change, because the shared_info page is
+  an overlay on guest memory and remains at a fixed host address
+  regardless of where it is mapped in guest physical address space
+  and hence unnecessary invalidation of an internal cache may be
+  avoided if the guest memory layout is modified.
+  If the vcpu_info does not reside at the "default" location then
+  it is not guaranteed to remain at the same host address and
+  hence the aforementioned cache invalidation is required.
+
 KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO
   Sets the guest physical address of an additional pvclock structure
   for a given vCPU. This is typically used for guest vsyscall support.
 
        union {
                __u64 gpa;
 #define KVM_XEN_INVALID_GPA ((__u64)-1)
+               __u64 hva;
                __u64 pad[8];
                struct {
                        __u64 state;
 #define KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID         0x6
 #define KVM_XEN_VCPU_ATTR_TYPE_TIMER           0x7
 #define KVM_XEN_VCPU_ATTR_TYPE_UPCALL_VECTOR   0x8
+/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO_HVA */
+#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO_HVA   0x9
 
 /* Secure Encrypted Virtualization command */
 enum sev_cmd_id {
 
 
        switch (data->type) {
        case KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO:
+       case KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO_HVA:
                /* No compat necessary here. */
                BUILD_BUG_ON(sizeof(struct vcpu_info) !=
                             sizeof(struct compat_vcpu_info));
                BUILD_BUG_ON(offsetof(struct vcpu_info, time) !=
                             offsetof(struct compat_vcpu_info, time));
 
-               if (data->u.gpa == KVM_XEN_INVALID_GPA) {
-                       kvm_gpc_deactivate(&vcpu->arch.xen.vcpu_info_cache);
-                       r = 0;
-                       break;
+               if (data->type == KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO) {
+                       if (data->u.gpa == KVM_XEN_INVALID_GPA) {
+                               kvm_gpc_deactivate(&vcpu->arch.xen.vcpu_info_cache);
+                               r = 0;
+                               break;
+                       }
+
+                       r = kvm_gpc_activate(&vcpu->arch.xen.vcpu_info_cache,
+                                            data->u.gpa, sizeof(struct vcpu_info));
+               } else {
+                       if (data->u.hva == 0) {
+                               kvm_gpc_deactivate(&vcpu->arch.xen.vcpu_info_cache);
+                               r = 0;
+                               break;
+                       }
+
+                       r = kvm_gpc_activate_hva(&vcpu->arch.xen.vcpu_info_cache,
+                                                data->u.hva, sizeof(struct vcpu_info));
                }
 
-               r = kvm_gpc_activate(&vcpu->arch.xen.vcpu_info_cache,
-                                    data->u.gpa, sizeof(struct vcpu_info));
                if (!r)
                        kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);
 
 
        switch (data->type) {
        case KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO:
-               if (vcpu->arch.xen.vcpu_info_cache.active)
+               if (kvm_gpc_is_gpa_active(&vcpu->arch.xen.vcpu_info_cache))
                        data->u.gpa = vcpu->arch.xen.vcpu_info_cache.gpa;
                else
                        data->u.gpa = KVM_XEN_INVALID_GPA;
                r = 0;
                break;
 
+       case KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO_HVA:
+               if (kvm_gpc_is_hva_active(&vcpu->arch.xen.vcpu_info_cache))
+                       data->u.hva = vcpu->arch.xen.vcpu_info_cache.uhva;
+               else
+                       data->u.hva = 0;
+               r = 0;
+               break;
+
        case KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO:
                if (vcpu->arch.xen.vcpu_time_info_cache.active)
                        data->u.gpa = vcpu->arch.xen.vcpu_time_info_cache.gpa;