public inbox for kvm@vger.kernel.org
 help / color / mirror / Atom feed
From: Marcelo Tosatti <mtosatti@redhat.com>
To: Avi Kivity <avi@qumranet.com>
Cc: Chris Wright <chrisw@redhat.com>,
	Glauber Costa <gcosta@redhat.com>,
	Anthony Liguori <aliguori@us.ibm.com>,
	kvm@vger.kernel.org, Marcelo Tosatti <mtosatti@redhat.com>
Subject: [patch 02/12] KVM: allow multiple IO bitmap pages, provide userspace interface
Date: Thu, 29 May 2008 19:22:51 -0300	[thread overview]
Message-ID: <20080529222828.606999090@localhost.localdomain> (raw)
In-Reply-To: 20080529222249.563011248@localhost.localdomain

[-- Attachment #1: kvm-open-pmtimer --]
[-- Type: text/plain, Size: 20262 bytes --]

Allow multiple IO bitmap pages and provide an interface for userspace
to control which ports are open for a particular guest.

Only tested on Intel VMX.

Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>

Index: kvm/arch/x86/kvm/kvm_svm.h
===================================================================
--- kvm.orig/arch/x86/kvm/kvm_svm.h
+++ kvm/arch/x86/kvm/kvm_svm.h
@@ -41,6 +41,7 @@ struct vcpu_svm {
 	unsigned long host_dr7;
 
 	u32 *msrpm;
+	struct kvm_io_bitmap_page *io_bitmap_page;
 };
 
 #endif
Index: kvm/arch/x86/kvm/svm.c
===================================================================
--- kvm.orig/arch/x86/kvm/svm.c
+++ kvm/arch/x86/kvm/svm.c
@@ -24,6 +24,7 @@
 #include <linux/vmalloc.h>
 #include <linux/highmem.h>
 #include <linux/sched.h>
+#include <linux/kref.h>
 
 #include <asm/desc.h>
 
@@ -68,7 +69,10 @@ static inline struct vcpu_svm *to_svm(st
 	return container_of(vcpu, struct vcpu_svm, vcpu);
 }
 
-static unsigned long iopm_base;
+static LIST_HEAD(io_bitmap_pages);
+static DEFINE_MUTEX(io_bitmap_mutex);
+
+static struct kvm_io_bitmap_page *main_iopm_page;
 
 struct kvm_ldttss_desc {
 	u16 limit0;
@@ -413,18 +417,28 @@ static __init int svm_hardware_setup(voi
 {
 	int cpu;
 	struct page *iopm_pages;
+	struct kvm_io_bitmap_page *bitmap_page;
 	void *iopm_va;
 	int r;
 
-	iopm_pages = alloc_pages(GFP_KERNEL, IOPM_ALLOC_ORDER);
+	bitmap_page = kmalloc(sizeof(struct kvm_io_bitmap_page), GFP_KERNEL);
+	if (!bitmap_page)
+		return -ENOMEM;
+	kref_init(&bitmap_page->ref);
+	INIT_LIST_HEAD(&bitmap_page->io_bitmap_list);
 
-	if (!iopm_pages)
+	iopm_pages = alloc_pages(GFP_KERNEL, IOPM_ALLOC_ORDER);
+	if (!iopm_pages) {
+		kfree(bitmap_page);
 		return -ENOMEM;
+	}
 
 	iopm_va = page_address(iopm_pages);
 	memset(iopm_va, 0xff, PAGE_SIZE * (1 << IOPM_ALLOC_ORDER));
 	clear_bit(0x80, iopm_va); /* allow direct access to PC debug port */
-	iopm_base = page_to_pfn(iopm_pages) << PAGE_SHIFT;
+	bitmap_page->page = iopm_pages;
+	list_add(&bitmap_page->io_bitmap_list, &io_bitmap_pages);
+	main_iopm_page = bitmap_page;
 
 	if (boot_cpu_has(X86_FEATURE_NX))
 		kvm_enable_efer_bits(EFER_NX);
@@ -454,14 +468,16 @@ static __init int svm_hardware_setup(voi
 
 err:
 	__free_pages(iopm_pages, IOPM_ALLOC_ORDER);
-	iopm_base = 0;
+	list_del(&bitmap_page->io_bitmap_list);
+	kfree(bitmap_page);
 	return r;
 }
 
 static __exit void svm_hardware_unsetup(void)
 {
-	__free_pages(pfn_to_page(iopm_base >> PAGE_SHIFT), IOPM_ALLOC_ORDER);
-	iopm_base = 0;
+	__free_pages(main_iopm_page->page, IOPM_ALLOC_ORDER);
+	list_del(&main_iopm_page->io_bitmap_list);
+	kfree(main_iopm_page);
 }
 
 static void init_seg(struct vmcb_seg *seg)
@@ -534,7 +550,7 @@ static void init_vmcb(struct vcpu_svm *s
 				(1ULL << INTERCEPT_MONITOR) |
 				(1ULL << INTERCEPT_MWAIT);
 
-	control->iopm_base_pa = iopm_base;
+	control->iopm_base_pa = page_to_pfn(svm->io_bitmap_page->page) << PAGE_SHIFT;
 	control->msrpm_base_pa = __pa(svm->msrpm);
 	control->tsc_offset = 0;
 	control->int_ctl = V_INTR_MASKING_MASK;
@@ -641,6 +657,8 @@ static struct kvm_vcpu *svm_create_vcpu(
 	svm->msrpm = page_address(msrpm_pages);
 	svm_vcpu_init_msrpm(svm->msrpm);
 
+	svm->io_bitmap_page = kvm->arch.io_bitmap_a;
+
 	svm->vmcb = page_address(page);
 	clear_page(svm->vmcb);
 	svm->vmcb_pa = page_to_pfn(page) << PAGE_SHIFT;
@@ -1912,6 +1930,98 @@ static int get_npt_level(void)
 #endif
 }
 
+static struct kvm_io_bitmap_page *svm_find_io_bitmap_page(void *va)
+{
+	struct kvm_io_bitmap_page *bitmap_page;
+	struct page *iopm_pages;
+
+	list_for_each_entry(bitmap_page, &io_bitmap_pages, io_bitmap_list) {
+		bool found;
+		found = !memcmp(va, page_address(bitmap_page->page),
+				PAGE_SIZE * (1 << IOPM_ALLOC_ORDER));
+		if (found) {
+			kref_get(&bitmap_page->ref);
+			return bitmap_page;
+		}
+	}
+
+	bitmap_page = kmalloc(sizeof(struct kvm_io_bitmap_page), GFP_KERNEL);
+	if (!bitmap_page)
+		return NULL;
+
+	iopm_pages = alloc_pages(GFP_KERNEL, IOPM_ALLOC_ORDER);
+	if (!iopm_pages) {
+		kfree(bitmap_page);
+		return NULL;
+	}
+	memcpy(page_address(iopm_pages), va, (1 << IOPM_ALLOC_ORDER));
+	bitmap_page->page = iopm_pages;
+	kref_init(&bitmap_page->ref);
+	INIT_LIST_HEAD(&bitmap_page->io_bitmap_list);
+	list_add(&bitmap_page->io_bitmap_list, &io_bitmap_pages);
+
+	return bitmap_page;
+}
+
+static void svm_build_io_bitmap(void *va, struct kvm_ioport_list *ioports)
+{
+	int i;
+
+	memset(va, 0xff, PAGE_SIZE * (1 << IOPM_ALLOC_ORDER));
+	for (i = 0; i < ioports->nranges; i++) {
+		struct kvm_ioport *ioport = &ioports->ioports[i];
+		__u32 addr, n;
+
+		addr = ioport->addr;
+		for (n = addr; n < addr + ioport->len; n++)
+			clear_bit(n, va);
+	}
+}
+
+static int svm_open_io_ports(struct kvm *kvm, struct kvm_ioport_list *ioports)
+{
+	void *area;
+	struct kvm_io_bitmap_page *bitmap_page;
+	int ret = -ENOMEM;
+
+	mutex_lock(&io_bitmap_mutex);
+	area = vmalloc(PAGE_SIZE * (1 << IOPM_ALLOC_ORDER));
+	if (!area)
+		goto out_unlock;
+	svm_build_io_bitmap(area, ioports);
+	bitmap_page = svm_find_io_bitmap_page(area);
+	vfree(area);
+	if (!bitmap_page)
+		goto out_unlock;
+
+	kvm->arch.io_bitmap_a = bitmap_page;
+	ret = 0;
+out_unlock:
+	mutex_unlock(&io_bitmap_mutex);
+	return ret;
+}
+
+static void svm_io_bitmap_kref_put(struct kref *kref)
+{
+	struct kvm_io_bitmap_page *page =
+		container_of(kref, struct kvm_io_bitmap_page, ref);
+
+	mutex_lock(&io_bitmap_mutex);
+	list_del(&page->io_bitmap_list);
+	__free_pages(page->page, IOPM_ALLOC_ORDER);
+	kfree(page);
+	mutex_unlock(&io_bitmap_mutex);
+}
+
+static void svm_release_io_bitmaps(struct kvm *kvm)
+{
+	struct kvm_io_bitmap_page *io_bitmap;
+
+	io_bitmap = kvm->arch.io_bitmap_a;
+	if (io_bitmap)
+		kref_put(&io_bitmap->ref, svm_io_bitmap_kref_put);
+}
+
 static struct kvm_x86_ops svm_x86_ops = {
 	.cpu_has_kvm_support = has_svm,
 	.disabled_by_bios = is_disabled,
@@ -1969,6 +2079,8 @@ static struct kvm_x86_ops svm_x86_ops = 
 
 	.set_tss_addr = svm_set_tss_addr,
 	.get_tdp_level = get_npt_level,
+	.open_io_ports = svm_open_io_ports,
+	.release_io_bitmaps = svm_release_io_bitmaps,
 };
 
 static int __init svm_init(void)
Index: kvm/arch/x86/kvm/vmx.c
===================================================================
--- kvm.orig/arch/x86/kvm/vmx.c
+++ kvm/arch/x86/kvm/vmx.c
@@ -26,6 +26,7 @@
 #include <linux/highmem.h>
 #include <linux/sched.h>
 #include <linux/moduleparam.h>
+#include <linux/kref.h>
 
 #include <asm/io.h>
 #include <asm/desc.h>
@@ -47,6 +48,10 @@ module_param(flexpriority_enabled, bool,
 static int enable_ept = 1;
 module_param(enable_ept, bool, 0);
 
+static LIST_HEAD(io_bitmap_pages_a);
+static LIST_HEAD(io_bitmap_pages_b);
+static DEFINE_MUTEX(io_bitmap_mutex);
+
 struct vmcs {
 	u32 revision_id;
 	u32 abort;
@@ -83,6 +88,8 @@ struct vcpu_vmx {
 		} irq;
 	} rmode;
 	int vpid;
+	struct kvm_io_bitmap_page *io_bitmap_a;
+	struct kvm_io_bitmap_page *io_bitmap_b;
 };
 
 static inline struct vcpu_vmx *to_vmx(struct kvm_vcpu *vcpu)
@@ -96,8 +103,6 @@ static DEFINE_PER_CPU(struct vmcs *, vmx
 static DEFINE_PER_CPU(struct vmcs *, current_vmcs);
 static DEFINE_PER_CPU(struct list_head, vcpus_on_cpu);
 
-static struct page *vmx_io_bitmap_a;
-static struct page *vmx_io_bitmap_b;
 static struct page *vmx_msr_bitmap;
 
 static DECLARE_BITMAP(vmx_vpid_bitmap, VMX_NR_VPIDS);
@@ -1875,8 +1880,8 @@ static int vmx_vcpu_setup(struct vcpu_vm
 	u32 exec_control;
 
 	/* I/O */
-	vmcs_write64(IO_BITMAP_A, page_to_phys(vmx_io_bitmap_a));
-	vmcs_write64(IO_BITMAP_B, page_to_phys(vmx_io_bitmap_b));
+	vmcs_write64(IO_BITMAP_A, page_to_phys(vmx->io_bitmap_a->page));
+	vmcs_write64(IO_BITMAP_B, page_to_phys(vmx->io_bitmap_b->page));
 
 	if (cpu_has_vmx_msr_bitmap())
 		vmcs_write64(MSR_BITMAP, page_to_phys(vmx_msr_bitmap));
@@ -3126,6 +3131,9 @@ static struct kvm_vcpu *vmx_create_vcpu(
 
 	vmcs_clear(vmx->vmcs);
 
+	vmx->io_bitmap_a = kvm->arch.io_bitmap_a;
+	vmx->io_bitmap_b = kvm->arch.io_bitmap_b;
+
 	cpu = get_cpu();
 	vmx_vcpu_load(&vmx->vcpu, cpu);
 	err = vmx_vcpu_setup(vmx);
@@ -3175,6 +3183,131 @@ static int get_ept_level(void)
 	return VMX_EPT_DEFAULT_GAW + 1;
 }
 
+
+static struct kvm_io_bitmap_page *
+vmx_find_io_bitmap_page(struct list_head *list, struct page *page,
+			void *va_new_pg)
+{
+	struct kvm_io_bitmap_page *bitmap_page;
+
+	list_for_each_entry(bitmap_page, list, io_bitmap_list) {
+		bool found;
+		void *va;
+
+		va = kmap(bitmap_page->page);
+		found = !memcmp(va_new_pg, va, PAGE_SIZE);
+		kunmap(bitmap_page->page);
+		if (found) {
+			kref_get(&bitmap_page->ref);
+			return bitmap_page;
+		}
+	}
+
+	bitmap_page = kmalloc(sizeof(struct kvm_io_bitmap_page), GFP_KERNEL);
+	if (!bitmap_page)
+		return NULL;
+
+	get_page(page);
+	bitmap_page->page = page;
+	kref_init(&bitmap_page->ref);
+	INIT_LIST_HEAD(&bitmap_page->io_bitmap_list);
+	list_add(&bitmap_page->io_bitmap_list, list);
+
+	return bitmap_page;
+}
+
+static void vmx_build_io_bitmap(void *va, struct kvm_ioport_list *ioports,
+			        unsigned int start, unsigned int limit)
+{
+	int i;
+
+	memset(va, 0xff, PAGE_SIZE);
+	for (i = 0; i < ioports->nranges; i++) {
+		struct kvm_ioport *ioport = &ioports->ioports[i];
+		__u32 addr, n;
+
+		addr = ioport->addr;
+		for (n = addr; n < addr + ioport->len; n++) {
+			if (n < start)
+				continue;
+			if (n > limit)
+				goto out;
+			clear_bit(n - start, va);
+		}
+	}
+out:
+	return;
+}
+
+static int vmx_open_io_ports(struct kvm *kvm, struct kvm_ioport_list *ioports)
+{
+	struct page *page;
+	struct kvm_io_bitmap_page *bitmap_page_a, *bitmap_page_b;
+	void *va;
+	int ret = -ENOMEM;
+
+	mutex_lock(&io_bitmap_mutex);
+	page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
+	if (!page)
+		goto out_unlock;
+
+	va = kmap(page);
+	vmx_build_io_bitmap(va, ioports, 0, 0x7fff);
+	bitmap_page_a = vmx_find_io_bitmap_page(&io_bitmap_pages_a, page, va);
+	kunmap(page);
+	__free_page(page);
+
+	page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
+	if (!page)
+		goto out_unlock;
+
+	va = kmap(page);
+	vmx_build_io_bitmap(va, ioports, 0x8000, 0xffff);
+	bitmap_page_b = vmx_find_io_bitmap_page(&io_bitmap_pages_b, page, va);
+	kunmap(page);
+	__free_page(page);
+
+	if (!bitmap_page_a || !bitmap_page_b)
+		goto out_unlock;
+
+	kvm->arch.io_bitmap_a = bitmap_page_a;
+	kvm->arch.io_bitmap_b = bitmap_page_b;
+
+	ret = 0;
+out_unlock:
+	mutex_unlock(&io_bitmap_mutex);
+	return ret;
+
+}
+
+static void vmx_io_bitmap_kref_put(struct kref *kref)
+{
+	struct kvm_io_bitmap_page *page =
+		container_of(kref, struct kvm_io_bitmap_page, ref);
+
+	mutex_lock(&io_bitmap_mutex);
+	list_del(&page->io_bitmap_list);
+	__free_page(page->page);
+	kfree(page);
+	mutex_unlock(&io_bitmap_mutex);
+}
+
+static void vmx_release_io_bitmaps(struct kvm *kvm)
+{
+	struct kvm_io_bitmap_page *io_bitmap_a, *io_bitmap_b;
+
+	io_bitmap_a = kvm->arch.io_bitmap_a;
+	io_bitmap_b = kvm->arch.io_bitmap_b;
+
+	WARN_ON(!kvm->arch.io_bitmap_a);
+	WARN_ON(!kvm->arch.io_bitmap_b);
+
+	if (io_bitmap_a)
+		kref_put(&io_bitmap_a->ref, vmx_io_bitmap_kref_put);
+	if (io_bitmap_b)
+		kref_put(&io_bitmap_b->ref, vmx_io_bitmap_kref_put);
+}
+
 static struct kvm_x86_ops vmx_x86_ops = {
 	.cpu_has_kvm_support = cpu_has_kvm_support,
 	.disabled_by_bios = vmx_disabled_by_bios,
@@ -3231,6 +3364,9 @@ static struct kvm_x86_ops vmx_x86_ops = 
 
 	.set_tss_addr = vmx_set_tss_addr,
 	.get_tdp_level = get_ept_level,
+
+	.open_io_ports = vmx_open_io_ports,
+	.release_io_bitmaps = vmx_release_io_bitmaps,
 };
 
 static int __init vmx_init(void)
@@ -3238,35 +3374,12 @@ static int __init vmx_init(void)
 	void *va;
 	int r;
 
-	vmx_io_bitmap_a = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
-	if (!vmx_io_bitmap_a)
-		return -ENOMEM;
-
-	vmx_io_bitmap_b = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
-	if (!vmx_io_bitmap_b) {
-		r = -ENOMEM;
-		goto out;
-	}
-
 	vmx_msr_bitmap = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
 	if (!vmx_msr_bitmap) {
 		r = -ENOMEM;
-		goto out1;
+		goto out;
 	}
 
-	/*
-	 * Allow direct access to the PC debug port (it is often used for I/O
-	 * delays, but the vmexits simply slow things down).
-	 */
-	va = kmap(vmx_io_bitmap_a);
-	memset(va, 0xff, PAGE_SIZE);
-	clear_bit(0x80, va);
-	kunmap(vmx_io_bitmap_a);
-
-	va = kmap(vmx_io_bitmap_b);
-	memset(va, 0xff, PAGE_SIZE);
-	kunmap(vmx_io_bitmap_b);
-
 	va = kmap(vmx_msr_bitmap);
 	memset(va, 0xff, PAGE_SIZE);
 	kunmap(vmx_msr_bitmap);
@@ -3275,7 +3388,7 @@ static int __init vmx_init(void)
 
 	r = kvm_init(&vmx_x86_ops, sizeof(struct vcpu_vmx), THIS_MODULE);
 	if (r)
-		goto out2;
+		goto out1;
 
 	vmx_disable_intercept_for_msr(vmx_msr_bitmap, MSR_FS_BASE);
 	vmx_disable_intercept_for_msr(vmx_msr_bitmap, MSR_GS_BASE);
@@ -3293,20 +3406,15 @@ static int __init vmx_init(void)
 
 	return 0;
 
-out2:
-	__free_page(vmx_msr_bitmap);
 out1:
-	__free_page(vmx_io_bitmap_b);
+	__free_page(vmx_msr_bitmap);
 out:
-	__free_page(vmx_io_bitmap_a);
 	return r;
 }
 
 static void __exit vmx_exit(void)
 {
 	__free_page(vmx_msr_bitmap);
-	__free_page(vmx_io_bitmap_b);
-	__free_page(vmx_io_bitmap_a);
 
 	kvm_exit();
 }
Index: kvm/arch/x86/kvm/x86.c
===================================================================
--- kvm.orig/arch/x86/kvm/x86.c
+++ kvm/arch/x86/kvm/x86.c
@@ -447,6 +447,8 @@ static u32 emulated_msrs[] = {
 	MSR_IA32_MISC_ENABLE,
 };
 
+static struct kvm_ioport_list *allowed_open_ioports;
+
 static void set_efer(struct kvm_vcpu *vcpu, u64 efer)
 {
 	if (efer & efer_reserved_bits) {
@@ -790,6 +792,7 @@ int kvm_dev_ioctl_check_extension(long e
 	case KVM_CAP_PIT:
 	case KVM_CAP_NOP_IO_DELAY:
 	case KVM_CAP_MP_STATE:
+	case KVM_CAP_OPEN_IOPORT:
 		r = 1;
 		break;
 	case KVM_CAP_VAPIC:
@@ -1494,6 +1497,56 @@ static int kvm_vm_ioctl_set_pit(struct k
 	return r;
 }
 
+static int kvm_vm_ioctl_set_ioport(struct kvm *kvm, __u32 nranges,
+				   struct kvm_ioport_list __user *ioports)
+{
+	struct kvm_ioport_list *ioport_entries;
+	int r, i, nr_ports_ok = 0;
+
+	r = -E2BIG;
+	if (nranges > KVM_MAX_IOPORT_RANGES)
+		goto out;
+	r = -ENOMEM;
+	ioport_entries = vmalloc(nranges * sizeof(struct kvm_ioport) +
+				 sizeof(struct kvm_ioport_list));
+	if (!ioport_entries)
+		goto out;
+	r = -EFAULT;
+	if (copy_from_user(ioport_entries, ioports,
+			   nranges * sizeof(struct kvm_ioport) +
+			   sizeof(struct kvm_ioport_list)))
+		goto out_free;
+	r = -EPERM;
+	if (ioport_entries->nranges != nranges)
+		goto out_free;
+
+	for (i = 0; i < ioport_entries->nranges; i++) {
+		int n;
+		struct kvm_ioport *user_ioport = &ioport_entries->ioports[i];
+
+		for (n = 0; n < allowed_open_ioports->nranges; n++) {
+			struct kvm_ioport *allowed_ioport;
+			allowed_ioport = &allowed_open_ioports->ioports[n];
+			if (user_ioport->addr != allowed_ioport->addr ||
+			    user_ioport->len > allowed_ioport->len)
+				continue;
+			else {
+				nr_ports_ok++;
+				break;
+			}
+		}
+	}
+	if (nr_ports_ok != ioport_entries->nranges)
+		goto out_free;
+
+	r = kvm_x86_ops->open_io_ports(kvm, ioport_entries);
+
+out_free:
+	vfree(ioport_entries);
+out:
+	return r;
+}
+
 /*
  * Get (and clear) the dirty memory log for a memory slot.
  */
@@ -1678,6 +1731,16 @@ long kvm_arch_vm_ioctl(struct file *filp
 		r = 0;
 		break;
 	}
+	case KVM_SET_OPEN_IOPORT: {
+		struct kvm_ioport_list ioport_list;
+		r = -EFAULT;
+		if (copy_from_user(&ioport_list, argp, sizeof ioport_list))
+			goto out;
+		r = kvm_vm_ioctl_set_ioport(kvm, ioport_list.nranges, argp);
+		if (r)
+			goto out;
+		break;
+	}
 	default:
 		;
 	}
@@ -1700,6 +1763,25 @@ static void kvm_init_msr_list(void)
 	num_msrs_to_save = j;
 }
 
+static int kvm_init_ioport_list(void)
+{
+	allowed_open_ioports = kmalloc(sizeof(struct kvm_ioport) +
+				       sizeof(struct kvm_ioport_list), GFP_KERNEL);
+	if (!allowed_open_ioports)
+		return -ENOMEM;
+	allowed_open_ioports->nranges = 1;
+	allowed_open_ioports->ioports[0].addr = 0x80;
+	allowed_open_ioports->ioports[0].len = 1;
+
+	return 0;
+}
+
+static void kvm_exit_ioport_list(void)
+{
+	kfree(allowed_open_ioports);
+	allowed_open_ioports = NULL;
+}
+
 /*
  * Only apic need an MMIO device hook, so shortcut now..
  */
@@ -2384,6 +2466,9 @@ int kvm_arch_init(void *opaque)
 	r = kvm_mmu_module_init();
 	if (r)
 		goto out;
+	r = kvm_init_ioport_list();
+	if (r)
+		goto out_exit_mmu;
 
 	kvm_init_msr_list();
 
@@ -2394,6 +2479,8 @@ int kvm_arch_init(void *opaque)
 			PT_DIRTY_MASK, PT64_NX_MASK, 0);
 	return 0;
 
+out_exit_mmu:
+	kvm_mmu_module_exit();
 out:
 	return r;
 }
@@ -2402,6 +2489,7 @@ void kvm_arch_exit(void)
 {
 	kvm_x86_ops = NULL;
 	kvm_mmu_module_exit();
+	kvm_exit_ioport_list();
 }
 
 int kvm_emulate_halt(struct kvm_vcpu *vcpu)
@@ -3859,14 +3947,43 @@ void kvm_arch_vcpu_uninit(struct kvm_vcp
 	free_page((unsigned long)vcpu->arch.pio_data);
 }
 
+static int kvm_open_def_ioports(struct kvm *kvm)
+{
+	struct kvm_ioport_list *ioports;
+	int ret;
+
+	ioports = kzalloc(sizeof(struct kvm_ioport) +
+			  sizeof(struct kvm_ioport_list), GFP_KERNEL);
+	if (!ioports)
+		return -ENOMEM;
+
+	/*
+	 * Allow direct access to the PC debug port (it is often used for I/O
+	 * delays, but the vmexits simply slow things down).
+	 */
+	ioports->nranges = 1;
+	ioports->ioports[0].addr = 0x80;
+	ioports->ioports[0].len = 1;
+
+	ret = kvm_x86_ops->open_io_ports(kvm, ioports);
+	kfree(ioports);
+	return ret;
+}
+
 struct  kvm *kvm_arch_create_vm(void)
 {
+	int ret;
 	struct kvm *kvm = kzalloc(sizeof(struct kvm), GFP_KERNEL);
 
 	if (!kvm)
 		return ERR_PTR(-ENOMEM);
 
 	INIT_LIST_HEAD(&kvm->arch.active_mmu_pages);
+	ret = kvm_open_def_ioports(kvm);
+	if (ret) {
+		kfree(kvm);
+		kvm = ERR_PTR(ret);
+	}
 
 	return kvm;
 }
@@ -3908,6 +4025,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm
 		put_page(kvm->arch.apic_access_page);
 	if (kvm->arch.ept_identity_pagetable)
 		put_page(kvm->arch.ept_identity_pagetable);
+	kvm_x86_ops->release_io_bitmaps(kvm);
 	kfree(kvm);
 }
 
Index: kvm/include/asm-x86/kvm.h
===================================================================
--- kvm.orig/include/asm-x86/kvm.h
+++ kvm/include/asm-x86/kvm.h
@@ -209,6 +209,17 @@ struct kvm_pit_state {
 	struct kvm_pit_channel_state channels[3];
 };
 
+/* for KVM_SET_OPEN_IOPORT */
+struct kvm_ioport {
+	__u32 addr;
+	__u32 len;
+};
+
+struct kvm_ioport_list {
+	__u32 nranges;
+	struct kvm_ioport ioports[0];
+};
+
 #define KVM_TRC_INJ_VIRQ         (KVM_TRC_HANDLER + 0x02)
 #define KVM_TRC_REDELIVER_EVT    (KVM_TRC_HANDLER + 0x03)
 #define KVM_TRC_PEND_INTR        (KVM_TRC_HANDLER + 0x04)
Index: kvm/include/asm-x86/kvm_host.h
===================================================================
--- kvm.orig/include/asm-x86/kvm_host.h
+++ kvm/include/asm-x86/kvm_host.h
@@ -78,6 +78,7 @@
 #define KVM_MIN_FREE_MMU_PAGES 5
 #define KVM_REFILL_PAGES 25
 #define KVM_MAX_CPUID_ENTRIES 40
+#define KVM_MAX_IOPORT_RANGES 100
 
 extern spinlock_t kvm_lock;
 extern struct list_head vm_list;
@@ -296,6 +297,12 @@ struct kvm_mem_alias {
 	gfn_t target_gfn;
 };
 
+struct kvm_io_bitmap_page {
+	struct page *page;
+	struct list_head io_bitmap_list;
+	struct kref ref;
+};
+
 struct kvm_arch{
 	int naliases;
 	struct kvm_mem_alias aliases[KVM_ALIAS_SLOTS];
@@ -320,6 +327,9 @@ struct kvm_arch{
 
 	struct page *ept_identity_pagetable;
 	bool ept_identity_pagetable_done;
+
+	struct kvm_io_bitmap_page *io_bitmap_a;
+	struct kvm_io_bitmap_page *io_bitmap_b;
 };
 
 struct kvm_vm_stat {
@@ -429,6 +439,9 @@ struct kvm_x86_ops {
 
 	int (*set_tss_addr)(struct kvm *kvm, unsigned int addr);
 	int (*get_tdp_level)(void);
+
+	int (*open_io_ports)(struct kvm *kvm, struct kvm_ioport_list *ioports);
+	void (*release_io_bitmaps)(struct kvm *kvm);
 };
 
 extern struct kvm_x86_ops *kvm_x86_ops;
Index: kvm/include/linux/kvm.h
===================================================================
--- kvm.orig/include/linux/kvm.h
+++ kvm/include/linux/kvm.h
@@ -346,6 +346,7 @@ struct kvm_trace_rec {
 #define KVM_CAP_NOP_IO_DELAY 12
 #define KVM_CAP_PV_MMU 13
 #define KVM_CAP_MP_STATE 14
+#define KVM_CAP_OPEN_IOPORT 15
 
 /*
  * ioctls for VM fds
@@ -371,6 +372,7 @@ struct kvm_trace_rec {
 #define KVM_CREATE_PIT		  _IO(KVMIO,  0x64)
 #define KVM_GET_PIT		  _IOWR(KVMIO, 0x65, struct kvm_pit_state)
 #define KVM_SET_PIT		  _IOR(KVMIO,  0x66, struct kvm_pit_state)
+#define KVM_SET_OPEN_IOPORT	  _IOR(KVMIO,  0x67, struct kvm_ioport_list)
 
 /*
  * ioctls for vcpu fds

-- 


  parent reply	other threads:[~2008-05-29 22:37 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-05-29 22:22 [patch 00/12] fake ACPI C2 emulation v2 Marcelo Tosatti
2008-05-29 22:22 ` [patch 01/12] expose ACPI pmtimer to userspace (/dev/pmtimer) Marcelo Tosatti
2008-06-01 16:34   ` Thomas Gleixner
2008-06-01 16:56     ` Anthony Liguori
2008-06-04  9:53       ` Avi Kivity
2008-06-04 10:01         ` Thomas Gleixner
2008-06-04 10:35           ` Avi Kivity
2008-06-01 17:56     ` Marcelo Tosatti
2008-06-01 18:17       ` Thomas Gleixner
2008-06-02 16:43       ` John Stultz
2008-06-03  4:09         ` Marcelo Tosatti
2008-05-29 22:22 ` Marcelo Tosatti [this message]
2008-05-29 22:22 ` [patch 03/12] KVM: allow userspace to open access to ACPI pmtimer Marcelo Tosatti
2008-05-29 22:22 ` [patch 04/12] KVM: move muldiv64 to x86.c, export Marcelo Tosatti
2008-05-29 22:22 ` [patch 05/12] KVM: in-kernel ACPI timer emulation Marcelo Tosatti
2008-05-29 22:22 ` [patch 06/12] QEMU/KVM: self-disabling C2 emulation Marcelo Tosatti
2008-05-29 22:22 ` [patch 07/12] libkvm: interface to KVM_SET_OPEN_IOPORT Marcelo Tosatti
2008-05-29 22:22 ` [patch 08/12] QEMU/KVM: non-virtualized ACPI PMTimer support Marcelo Tosatti
2008-05-29 22:22 ` [patch 09/12] libkvm: in-kernel ACPI pmtimer interface Marcelo Tosatti
2008-05-29 22:22 ` [patch 10/12] QEMU/KVM: add option to disable in-kernel pmtimer emulation Marcelo Tosatti
2008-05-29 22:23 ` [patch 11/12] libkvm: interface for pmtimer save/restore Marcelo Tosatti
2008-05-29 22:23 ` [patch 12/12] QEMU/KVM: in-kernel pmtimer save/restore support Marcelo Tosatti
2008-06-01  9:21 ` [patch 00/12] fake ACPI C2 emulation v2 Avi Kivity
2008-06-02 16:08   ` Marcelo Tosatti
2008-06-04 10:49     ` Avi Kivity
2008-06-05  3:12       ` Marcelo Tosatti
2008-06-05  7:56         ` Avi Kivity

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20080529222828.606999090@localhost.localdomain \
    --to=mtosatti@redhat.com \
    --cc=aliguori@us.ibm.com \
    --cc=avi@qumranet.com \
    --cc=chrisw@redhat.com \
    --cc=gcosta@redhat.com \
    --cc=kvm@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox