Signed-off-by: Gregory Haskins <[EMAIL PROTECTED]> --- drivers/kvm/Kconfig | 27 +++++- drivers/kvm/Makefile | 3 - drivers/kvm/ioq.h | 39 +++++++++ drivers/kvm/ioq_guest.c | 192 +++++++++++++++++++++++++++++++++++++++++++++ drivers/kvm/pvbus.h | 41 ++++++++++ drivers/kvm/pvbus_guest.c | 189 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/kvm.h | 4 + 7 files changed, 487 insertions(+), 8 deletions(-)
diff --git a/drivers/kvm/Kconfig b/drivers/kvm/Kconfig index 22d0eb4..cba03d2 100644 --- a/drivers/kvm/Kconfig +++ b/drivers/kvm/Kconfig @@ -47,16 +47,31 @@ config KVM_BALLOON The driver inflate/deflate guest physical memory on demand. This ability provides memory over commit for the host -config KVM_NET - tristate "Para virtual network device" - depends on KVM - ---help--- - Provides support for guest paravirtualization networking - config KVM_NET_HOST tristate "Para virtual network host device" depends on KVM ---help--- Provides support for host paravirtualization networking +config KVM_GUEST + bool "KVM Guest support" + depends on X86 + default y + +config KVM_PVBUS_GUEST + tristate "Paravirtualized Bus (PVBUS) support" + depends on KVM_GUEST + select IOQ + ---help--- + PVBUS is an infrastructure for generic PV drivers to take advantage + of an underlying hypervisor without having to understand the details + of the hypervisor itself. You only need this option if you plan to + run this kernel as a KVM guest. + +config KVM_NET + tristate "Para virtual network device" + depends on KVM && KVM_GUEST + ---help--- + Provides support for guest paravirtualization networking + endif # VIRTUALIZATION diff --git a/drivers/kvm/Makefile b/drivers/kvm/Makefile index 92600d8..c6a59bb 100644 --- a/drivers/kvm/Makefile +++ b/drivers/kvm/Makefile @@ -14,4 +14,5 @@ kvm-net-objs = kvm_net.o obj-$(CONFIG_KVM_NET) += kvm-net.o kvm-net-host-objs = kvm_net_host.o obj-$(CONFIG_KVM_NET_HOST) += kvm_net_host.o - +kvm-pvbus-objs := ioq_guest.o pvbus_guest.o +obj-$(CONFIG_KVM_PVBUS_GUEST) += kvm-pvbus.o diff --git a/drivers/kvm/ioq.h b/drivers/kvm/ioq.h new file mode 100644 index 0000000..7e955f1 --- /dev/null +++ b/drivers/kvm/ioq.h @@ -0,0 +1,39 @@ +/* + * Copyright 2007 Novell. All Rights Reserved. + * + * See include/linux/ioq.h for documentation + * + * Author: + * Gregory Haskins <[EMAIL PROTECTED]> + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _KVM_IOQ_H_ +#define _KVM_IOQ_H_ + +#include <linux/ioq.h> + +#define IOQHC_REGISTER 1 +#define IOQHC_UNREGISTER 2 +#define IOQHC_SIGNAL 3 + +struct ioq_register { + ioq_id_t id; + u32 irq; + u64 ring; +}; + + +#endif /* _KVM_IOQ_H_ */ diff --git a/drivers/kvm/ioq_guest.c b/drivers/kvm/ioq_guest.c new file mode 100644 index 0000000..f7c88aa --- /dev/null +++ b/drivers/kvm/ioq_guest.c @@ -0,0 +1,192 @@ +/* + * Copyright 2007 Novell. All Rights Reserved. + * + * See include/linux/ioq.h for documentation + * + * Author: + * Gregory Haskins <[EMAIL PROTECTED]> + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/ioq.h> +#include <asm/hypercall.h> + +#include "ioq.h" +#include "kvm.h" + +struct kvmguest_ioq { + struct ioq ioq; + int irq; +}; + +struct kvmguest_ioq* to_ioq(struct ioq *ioq) +{ + return container_of(ioq, struct kvmguest_ioq, ioq); +} + +static int ioq_hypercall(unsigned long nr, void *data) +{ + return hypercall(2, __NR_hypercall_ioq, nr, __pa(data)); +} + +/* + * ------------------ + * interrupt handler + * ------------------ + */ +irqreturn_t kvmguest_ioq_intr(int irq, void *dev) +{ + struct kvmguest_ioq *_ioq = to_ioq(dev); + + ioq_wakeup(&_ioq->ioq); + + return IRQ_HANDLED; +} + +/* + * ------------------ + * ioq implementation + * ------------------ + */ + +static int kvmguest_ioq_signal(struct ioq *ioq) +{ + return ioq_hypercall(IOQHC_SIGNAL, &ioq->id); +} + +static void kvmguest_ioq_destroy(struct ioq *ioq) +{ + struct kvmguest_ioq *_ioq = to_ioq(ioq); + int ret; + + ret = ioq_hypercall(IOQHC_UNREGISTER, &ioq->id); + BUG_ON (ret < 0); + + free_irq(_ioq->irq, NULL); + destroy_irq(_ioq->irq); + + kfree(_ioq->ioq.ring); + kfree(_ioq->ioq.head_desc); + kfree(_ioq); +} + +/* + * ------------------ + * ioqmgr implementation + * ------------------ + */ +static int kvmguest_ioq_register(struct kvmguest_ioq *ioq, ioq_id_t id, + int irq, void *ring) +{ + struct ioq_register data = { + .id = id, + .irq = irq, + .ring = (u64)__pa(ring), + }; + + return ioq_hypercall(IOQHC_REGISTER, &data); +} + +static int kvmguest_ioq_create(struct ioq_mgr *t, struct ioq **ioq, + size_t ringsize, int flags) +{ + struct kvmguest_ioq *_ioq = NULL; + struct ioq_ring_head *head_desc = NULL; + void *ring = NULL; + size_t ringlen = sizeof(struct ioq_ring_desc) * ringsize; + int ret = -ENOMEM; + + _ioq = kzalloc(sizeof(*_ioq), GFP_KERNEL); + if (!_ioq) + goto error; + + head_desc = kzalloc(sizeof(*head_desc), GFP_KERNEL | GFP_DMA); + if (!head_desc) + goto error; + + ring = kzalloc(ringlen, GFP_KERNEL | GFP_DMA); + if (!ring) + goto error; + + head_desc->magic = IOQ_RING_MAGIC; + head_desc->ver = IOQ_RING_VER; + head_desc->id = (ioq_id_t)_ioq; + head_desc->count = ringsize; + head_desc->ptr = (u64)__pa(ring); + + /* Dynamically assign a free IRQ to this resource */ + _ioq->irq = create_irq(); + + ioq_init(&_ioq->ioq); + + _ioq->ioq.signal = kvmguest_ioq_signal; + _ioq->ioq.destroy = kvmguest_ioq_destroy; + + _ioq->ioq.id = head_desc->id; + _ioq->ioq.locale = ioq_locality_north; + _ioq->ioq.mgr = t; + _ioq->ioq.head_desc = head_desc; + _ioq->ioq.ring = ring; + + ret = request_irq(_ioq->irq, kvmguest_ioq_intr, 0, "KVM-IOQ", _ioq); + if (ret < 0) + goto error; + + ret = kvmguest_ioq_register(_ioq, _ioq->ioq.id, _ioq->irq, ring); + if (ret < 0) + goto error; + + *ioq = &_ioq->ioq; + + return 0; + + error: + if (_ioq) + kfree(_ioq); + if (head_desc) + kfree(head_desc); + if (ring) + kfree(ring); + + return ret; +} + +static int kvmguest_ioq_connect(struct ioq_mgr *t, ioq_id_t id, + struct ioq **ioq, int flags) +{ + /* You cannot connect to queues on the guest */ + return -EINVAL; + +} + +int kvmguest_ioqmgr_alloc(struct ioq_mgr **mgr) +{ + struct ioq_mgr *_mgr = kzalloc(sizeof(*_mgr), GFP_KERNEL); + if (!_mgr) + return -ENOMEM; + + _mgr->create = kvmguest_ioq_create; + _mgr->connect = kvmguest_ioq_connect; + + *mgr = _mgr; + + return 0; +} + + + + + + diff --git a/drivers/kvm/pvbus.h b/drivers/kvm/pvbus.h new file mode 100644 index 0000000..97b5dce --- /dev/null +++ b/drivers/kvm/pvbus.h @@ -0,0 +1,41 @@ +/* + * Copyright 2007 Novell. All Rights Reserved. + * + * Author: + * Gregory Haskins <[EMAIL PROTECTED]> + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _KVM_PVBUS_H +#define _KVM_PVBUS_H + +#define KVM_PVBUS_OP_ENUM 1 +#define KVM_PVBUS_OP_CALL 2 + +struct enumerate_params { + u32 name_offset; + u32 inst_offset; + u32 count; + u8 data[1]; +}; + +struct call_params { + u64 inst; + u32 func; + u64 data; + u64 len; +}; + +#endif /* _KVM_PVBUS_H */ diff --git a/drivers/kvm/pvbus_guest.c b/drivers/kvm/pvbus_guest.c new file mode 100644 index 0000000..3003745 --- /dev/null +++ b/drivers/kvm/pvbus_guest.c @@ -0,0 +1,189 @@ +/* + * Copyright 2007 Novell. All Rights Reserved. + * + * Author: + * Gregory Haskins <[EMAIL PROTECTED]> + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/pvbus.h> +#include <linux/kvm_para.h> +#include <linux/kvm.h> +#include <linux/mm.h> +#include <linux/ioq.h> + +#include <asm/hypercall.h> + +#include "pvbus.h" + +MODULE_AUTHOR ("Gregory Haskins"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1"); + +static int kvm_pvbus_hypercall(unsigned long nr, void *data, unsigned long len) +{ + return hypercall(3, __NR_hypercall_pvbus, nr, __pa(data), len); +} + +static int kvm_pvbus_enumerate(const char *dev, struct pvbus_dev inst[], + size_t *cnt, int flags) +{ + int ret; + size_t namelen = strlen(dev); + size_t total = sizeof(struct enumerate_params) + + namelen /* dont add one...extra is already in data[1] */ + + (sizeof(struct pvbus_dev) * (*cnt)); + + struct enumerate_params *e = kzalloc(total, GFP_KERNEL | GFP_DMA); + if (!e) + return -ENOMEM; + + e->name_offset = 0; + memcpy(&e->data[e->name_offset], dev, namelen); + e->inst_offset = namelen + 1; + e->count = *cnt; + + ret = kvm_pvbus_hypercall(KVM_PVBUS_OP_ENUM, e, total); + if (ret < 0) + goto out; + + *cnt = e->count; + + if (e->count) + memcpy(inst, &e->data[e->inst_offset], + (sizeof(struct pvbus_dev) * e->count)); + + out: + kfree(e); + + return ret; +} + +static int kvm_pvbus_call(u64 inst, u32 func, void *data, + size_t len, int flags) +{ + struct call_params params = { + .inst = inst, + .func = func, + .data = (u64)__pa(data), + .len = len, + }; + + return kvm_pvbus_hypercall(KVM_PVBUS_OP_CALL, ¶ms, sizeof(params)); +} + +/* + * This is the vm-syscall address - to be patched by the host to + * VMCALL (Intel) or VMMCALL (AMD), depending on the CPU model: + */ +asm ( + " .globl hypercall_addr \n" + " .align 4 \n" + " hypercall_addr: \n" + " movl $-38, %eax \n" + " ret \n" +); + +extern unsigned char hypercall_addr[6]; + +#ifndef CONFIG_X86_64 +static DEFINE_PER_CPU(struct kvm_vcpu_para_state, para_state); +#endif + +static int __init kvm_pvbus_probe(void) +{ + struct page *hypercall_addr_page; + struct kvm_vcpu_para_state *para_state; + +#ifdef CONFIG_X86_64 + struct page *pstate_page; + if ((pstate_page = alloc_page(GFP_KERNEL)) == NULL) + return -ENOMEM; + para_state = (struct kvm_vcpu_para_state*)page_address(pstate_page); +#else + para_state = &per_cpu(para_state, cpu); +#endif + /* + * Try to write to a magic MSR (which is invalid on any real CPU), + * and thus signal to KVM that we wish to entering para-virtualized + * mode: + */ + para_state->guest_version = KVM_PARA_API_VERSION; + para_state->host_version = -1; + para_state->size = sizeof(*para_state); + para_state->ret = -1; + + hypercall_addr_page = vmalloc_to_page(hypercall_addr); + para_state->hypercall_gpa = page_to_pfn(hypercall_addr_page) + << PAGE_SHIFT | offset_in_page(hypercall_addr); + printk(KERN_DEBUG "kvm guest: hypercall gpa is 0x%lx\n", + (long)para_state->hypercall_gpa); + + if (wrmsr_safe(MSR_KVM_API_MAGIC, __pa(para_state), 0)) { + printk(KERN_INFO "KVM guest: WRMSR probe failed.\n"); + return -1; + } + + printk(KERN_DEBUG "kvm guest: host returned %d\n", + para_state->ret); + printk(KERN_DEBUG "kvm guest: host version: %d\n", + para_state->host_version); + printk(KERN_DEBUG "kvm guest: syscall entry: %02x %02x %02x %02x\n", + hypercall_addr[0], hypercall_addr[1], + hypercall_addr[2], hypercall_addr[3]); + + if (para_state->ret) { + printk(KERN_ERR "kvm guest: host refused registration.\n"); + return -1; + } + + return 0; + +} + +static struct pvbus_ops kvm_pvbus_ops = { + .enumerate = kvm_pvbus_enumerate, + .call = kvm_pvbus_call, + .ioqmgr = NULL, +}; + +int kvmguest_ioqmgr_alloc(struct ioq_mgr **mgr); + +int __init kvm_pvbus_init(void) +{ + int ret = kvm_pvbus_probe(); + if (ret < 0) + return ret; + + ret = kvmguest_ioqmgr_alloc(&kvm_pvbus_ops.ioqmgr); + if (ret < 0) + return ret; + + pvbus_ops = &kvm_pvbus_ops; + + return 0; + +} + +static void __exit kvm_pvbus_exit(void) +{ + +} + +module_init(kvm_pvbus_init); +module_exit(kvm_pvbus_exit); + + diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 992aeec..bc2b51e 100755 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -377,13 +377,15 @@ struct kvm_pvnet_config { * No registers are clobbered by the hypercall, except that the * return value is in RAX. */ -#define KVM_NR_HYPERCALLS 5 +#define KVM_NR_HYPERCALLS 7 #define __NR_hypercall_test 0 #define __NR_hypercall_register_eth 1 #define __NR_hypercall_send_eth 2 #define __NR_hypercall_set_multicast_eth 3 #define __NR_hypercall_start_stop_eth 4 +#define __NR_hypercall_ioq 5 +#define __NR_hypercall_pvbus 6 #define __NR_hypercall_balloon (KVM_NR_HYPERCALLS + 0) ------------------------------------------------------------------------- This SF.net email is sponsored by: Splunk Inc. Still grepping through log files to find problems? Stop. Now Search log events and configuration files using AJAX and a browser. Download your FREE copy of Splunk now >> http://get.splunk.com/ _______________________________________________ kvm-devel mailing list kvm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/kvm-devel