#include <linux/kvm_host.h>
#include <trace/events/kvm.h>
+#include <xen/interface/xen.h>
#include "trace.h"
+static int kvm_xen_map_guest_page(struct kvm *kvm, struct kvm_host_map *map,
+ void **hva, gpa_t gpa, size_t len)
+{
+ gfn_t gfn = gpa_to_gfn(gpa);
+ struct kvm_host_map new_map;
+ bool unmap = !!*hva;
+ int ret;
+
+ if (offset_in_page(gpa) + len > PAGE_SIZE)
+ return -EINVAL;
+
+ ret = kvm_map_gfn(kvm, gfn, &new_map, NULL, false);
+ if (ret)
+ return ret;
+
+ WRITE_ONCE(*hva, new_map.hva + offset_in_page(gpa));
+
+ if (unmap) {
+ synchronize_srcu(&kvm->srcu);
+
+ kvm_unmap_gfn(kvm, map, NULL, true, false);
+ }
+
+ *map = new_map;
+ return 0;
+}
+
+static int kvm_xen_shared_info_init(struct kvm *kvm, gfn_t gfn)
+{
+ gpa_t gpa = gfn_to_gpa(gfn);
+ int ret;
+
+ ret = kvm_xen_map_guest_page(kvm, &kvm->arch.xen.shinfo_map,
+ (void **)&kvm->arch.xen.shinfo, gpa,
+ PAGE_SIZE);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
int kvm_xen_hvm_set_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data)
{
int r = -ENOENT;
kvm->arch.xen.long_mode = !!data->u.long_mode;
r = 0;
break;
+
+ case KVM_XEN_ATTR_TYPE_SHARED_INFO: {
+ gfn_t gfn = data->u.shared_info.gfn;
+
+ r = kvm_xen_shared_info_init(kvm, gfn);
+ break;
+ }
+
default:
break;
}
data->u.long_mode = kvm->arch.xen.long_mode;
r = 0;
break;
+
+ case KVM_XEN_ATTR_TYPE_SHARED_INFO: {
+ if (kvm->arch.xen.shinfo) {
+ data->u.shared_info.gfn = kvm->arch.xen.shinfo_map.gfn;
+ r = 0;
+ }
+ break;
+ }
+
default:
break;
}
return 0;
}
+
+void kvm_xen_destroy_vm(struct kvm *kvm)
+{
+ struct kvm_xen *xen = &kvm->arch.xen;
+
+ if (xen->shinfo) {
+ kvm_unmap_gfn(kvm, &xen->shinfo_map, NULL, true, false);
+ xen->shinfo = NULL;
+ }
+}