Hi, attached is a patch for the linux 2.6.16 kernel that lets you build the qemu accelerator from within the kernel source (builtin or as a module). Due to the restricting license the binary module is not included of course.
I only tested it on x86, but it should also work on x86_64 (though I didn't test it, since I don't have a x86_64 box to play with). Please cc me in any replies - I'm not subscribed to the list. Regards, Robert
diff -urN linux-2.6.16/drivers/char/Kconfig linux-2.6.16-qemu/drivers/char/Kconfig --- linux-2.6.16/drivers/char/Kconfig 2006-03-20 06:53:29.000000000 +0100 +++ linux-2.6.16-qemu/drivers/char/Kconfig 2006-08-06 16:55:22.000000000 +0200 @@ -1020,5 +1020,6 @@ sysfs directory, /sys/devices/platform/telco_clock, with a number of files for controlling the behavior of this hardware. -endmenu +source "drivers/char/qemu/Kconfig" +endmenu diff -urN linux-2.6.16/drivers/char/Makefile linux-2.6.16-qemu/drivers/char/Makefile --- linux-2.6.16/drivers/char/Makefile 2006-03-20 06:53:29.000000000 +0100 +++ linux-2.6.16-qemu/drivers/char/Makefile 2006-08-06 16:55:22.000000000 +0200 @@ -85,6 +85,7 @@ obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o obj-$(CONFIG_TANBAC_TB0219) += tb0219.o obj-$(CONFIG_TELCLOCK) += tlclk.o +obj-$(CONFIG_QEMU_ACCEL) += qemu/ obj-$(CONFIG_WATCHDOG) += watchdog/ obj-$(CONFIG_MWAVE) += mwave/ diff -urN linux-2.6.16/drivers/char/qemu/Kconfig linux-2.6.16-qemu/drivers/char/qemu/Kconfig --- linux-2.6.16/drivers/char/qemu/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.16-qemu/drivers/char/qemu/Kconfig 2006-08-06 16:55:22.000000000 +0200 @@ -0,0 +1,61 @@ +config QEMU_ACCEL + tristate "QEMU Accelerator" + default n + depends on ((X86 || X86_64) && EXPERIMENTAL) + ---help--- + QEMU is a processor emulator that is used to run an x86 Linux Kernel + on x86 Linux. + + The QEMU Accelerator increases the speed of QEMU when a PC is + emulated on a PC. It runs most of the target application code + directly on the host processor to achieve near native performance. + It is very useful when you want to run another Operating System + (for example Windows) on a Linux desktop. + + Download page: + http://fabrice.bellard.free.fr/qemu/download.html + + The QEMU Accelerator Module (aka KQEMU) is a proprietary + product. It is available without charge. Commercial use of the QEMU + Accelerator Module is allowed. + + Redistribution of the QEMU Accelerator Module: any person or + organisation wishing to distribute it, for example on a CD or as a + binary or source package, must have an explicit authorization from + the author. + + The QEMU Accelerator Module is available without any express or + implied warranty. In no event will the author be held liable for + any damages arising from the use of this software. + + The header file "kqemu.h" is released under the BSD license. + + QEMU is a trademark of Fabrice Bellard. + +config QEMU_ACCEL_PATH + string "QEMU binary module directory path" + default "" + depends on QEMU_ACCEL + help + Specify the directory where the binary module (kqemu-mod-i386.o + or kqemu-mod-x86_64.o) is in. + + Download page: + http://fabrice.bellard.free.fr/qemu/download.html + + The QEMU Accelerator Module (aka KQEMU) is a proprietary + product. It is available without charge. Commercial use of the QEMU + Accelerator Module is allowed. + + Redistribution of the QEMU Accelerator Module: any person or + organisation wishing to distribute it, for example on a CD or as a + binary or source package, must have an explicit authorization from + the author. + + The QEMU Accelerator Module is available without any express or + implied warranty. In no event will the author be held liable for + any damages arising from the use of this software. + + The header file "kqemu.h" is released under the BSD license. + + QEMU is a trademark of Fabrice Bellard. diff -urN linux-2.6.16/drivers/char/qemu/Makefile linux-2.6.16-qemu/drivers/char/qemu/Makefile --- linux-2.6.16/drivers/char/qemu/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.16-qemu/drivers/char/qemu/Makefile 2006-08-06 16:55:22.000000000 +0200 @@ -0,0 +1,7 @@ +kqemu-objs:= kqemu-linux.o kqemu-mod.o +qemu_path := $(shell echo $(CONFIG_QEMU_ACCEL_PATH)) + +$(obj)/kqemu-mod.o: $(qemu_path)/kqemu-mod-${ARCH}.o + cp $< $@ + +obj-$(CONFIG_QEMU_ACCEL):= kqemu.o diff -urN linux-2.6.16/drivers/char/qemu/kqemu-kernel.h linux-2.6.16-qemu/drivers/char/qemu/kqemu-kernel.h --- linux-2.6.16/drivers/char/qemu/kqemu-kernel.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.16-qemu/drivers/char/qemu/kqemu-kernel.h 2006-08-06 16:55:22.000000000 +0200 @@ -0,0 +1,47 @@ +/* + * KQEMU kernel API + * Copyright (c) 2004-2005 Fabrice Bellard + */ +#ifndef KQEMU_KERNEL_H +#define KQEMU_KERNEL_H + +#include "kqemu.h" + +struct kqemu_state; +struct kqemu_global_state; + +#define CDECL __attribute__((regparm(0))) + +struct kqemu_global_state * CDECL kqemu_global_init(int max_locked_pages); +void CDECL kqemu_global_delete(struct kqemu_global_state *g); + +struct kqemu_state * CDECL kqemu_init(struct kqemu_init *d, + struct kqemu_global_state *g); +struct kqemu_cpu_state * CDECL kqemu_get_cpu_state(struct kqemu_state *s); +long CDECL kqemu_exec(struct kqemu_state *s); +void CDECL kqemu_delete(struct kqemu_state *s); + +/* callbacks */ +struct kqemu_page; /* opaque data for host page */ +struct kqemu_user_page; /* opaque data for host user page */ + +struct kqemu_user_page *CDECL kqemu_lock_user_page(unsigned long *ppage_index, + unsigned long user_addr); +void CDECL kqemu_unlock_user_page(struct kqemu_user_page *page); + +struct kqemu_page *CDECL kqemu_alloc_zeroed_page(unsigned long *ppage_index); +void CDECL kqemu_free_page(struct kqemu_page *page); +void * CDECL kqemu_page_kaddr(struct kqemu_page *page); + +void * CDECL kqemu_vmalloc(unsigned int size); +void CDECL kqemu_vfree(void *ptr); +unsigned long CDECL kqemu_vmalloc_to_phys(const void *vaddr); + +void * CDECL kqemu_io_map(unsigned long page_index, unsigned int size); +void CDECL kqemu_io_unmap(void *ptr, unsigned int size); + +int CDECL kqemu_schedule(void); + +void CDECL kqemu_log(const char *fmt, ...); + +#endif /* KQEMU_KERNEL_H */ diff -urN linux-2.6.16/drivers/char/qemu/kqemu-linux.c linux-2.6.16-qemu/drivers/char/qemu/kqemu-linux.c --- linux-2.6.16/drivers/char/qemu/kqemu-linux.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.16-qemu/drivers/char/qemu/kqemu-linux.c 2006-08-06 16:55:22.000000000 +0200 @@ -0,0 +1,372 @@ +/* + * Linux kernel wrapper for KQEMU + * Copyright (c) 2004-2005 Fabrice Bellard + */ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/proc_fs.h> +#include <linux/version.h> +#include <linux/ioctl.h> +#include <linux/smp_lock.h> +#include <linux/miscdevice.h> +#include <asm/atomic.h> +#include <asm/processor.h> +#include <asm/uaccess.h> +#include <asm/io.h> + +#include "kqemu-kernel.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) +#error "Linux 2.4.19 or above needed" +#endif + +/* The pfn_to_page() API appeared in 2.5.14 and changed to function during 2.6.x */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) && !defined(pfn_to_page) +#define page_to_pfn(page) ((page) - mem_map) +#define pfn_to_page(pfn) (mem_map + (pfn)) +#endif + +#ifdef PAGE_KERNEL_EXEC +#if defined(__i386__) +/* problem : i386 kernels usually don't export __PAGE_KERNEL_EXEC */ +#undef PAGE_KERNEL_EXEC +#define PAGE_KERNEL_EXEC __pgprot(__PAGE_KERNEL & ~_PAGE_NX) +#endif +#else +#define PAGE_KERNEL_EXEC PAGE_KERNEL +#endif + +//#define DEBUG + +#ifdef DEBUG +int lock_count; +int page_alloc_count; +#endif + +/* if 0 is used, then devfs/udev is used to automatically create the + device */ +int major = 250; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +module_param(major, int, 0); +#else +MODULE_PARM(major,"i"); +#endif + +/* Lock the page at virtual address 'user_addr' and return its + physical address (page index). Return a host OS private user page + identifier or NULL if error */ +struct kqemu_user_page *CDECL kqemu_lock_user_page(unsigned long *ppage_index, + unsigned long user_addr) +{ + int ret; + struct page *page; + + ret = get_user_pages(current, current->mm, + user_addr, + 1, /* 1 page. */ + 1, /* 'write': intent to write. */ + 0, /* 'force': ? */ + &page, + NULL); + if (ret != 1) + return NULL; + /* we ensure here that the page cannot be swapped out by the + kernel. */ + /* XXX: This test may be incorrect for 2.6 kernels */ + if (!page->mapping) { + put_page(page); + return NULL; + } +#ifdef DEBUG + lock_count++; +#endif + *ppage_index = page_to_pfn(page); + return (struct kqemu_user_page *)page; +} + +void CDECL kqemu_unlock_user_page(struct kqemu_user_page *page1) +{ + struct page *page = (struct page *)page1; + set_page_dirty(page); + put_page(page); +#ifdef DEBUG + lock_count--; +#endif +} + +/* Allocate a new page and return its physical address (page + index). Return a host OS private page identifier or NULL if + error */ +struct kqemu_page *CDECL kqemu_alloc_zeroed_page(unsigned long *ppage_index) +{ + unsigned long vaddr; + struct page *page; + + vaddr = get_zeroed_page(GFP_KERNEL); + if (!vaddr) + return NULL; +#ifdef DEBUG + page_alloc_count++; +#endif + page = virt_to_page(vaddr); + *ppage_index = page_to_pfn(page); + return (struct kqemu_page *)page; +} + +void CDECL kqemu_free_page(struct kqemu_page *page1) +{ + struct page *page = (struct page *)page1; + __free_page(page); +#ifdef DEBUG + page_alloc_count--; +#endif +} + +/* Return a host kernel address of the physical page whose private + identifier is 'page1' */ +void * CDECL kqemu_page_kaddr(struct kqemu_page *page1) +{ + struct page *page = (struct page *)page1; + return page_address(page); +} + +/* Allocate 'size' bytes of memory in host kernel address space (size + is a multiple of 4 KB) and return the address or NULL if error. The + allocated memory must be marked as executable by the host kernel + and must be page aligned. On i386 with PAE (but not on x86_64), it + must be allocated in the first 4 GB of physical memory. */ +void * CDECL kqemu_vmalloc(unsigned int size) +{ + return __vmalloc(size, GFP_KERNEL, PAGE_KERNEL_EXEC); +} + +void CDECL kqemu_vfree(void *ptr) +{ + return vfree(ptr); +} + +/* Convert a page aligned address inside a memory area allocated by + kqemu_vmalloc() to a physical address (page index) */ +unsigned long CDECL kqemu_vmalloc_to_phys(const void *vaddr) +{ + struct page *page; + page = vmalloc_to_page((void *)vaddr); + if (!page) + return -1; + return page_to_pfn(page); +} + +/* Map a IO area in the kernel address space and return its + address. Return NULL if error or not implemented. This function is + only used if an APIC is detected on the host CPU. */ +void * CDECL kqemu_io_map(unsigned long page_index, unsigned int size) +{ + return ioremap(page_index << PAGE_SHIFT, size); +} + +/* Unmap the IO area */ +void CDECL kqemu_io_unmap(void *ptr, unsigned int size) +{ + return iounmap(ptr); +} + +/* return TRUE if a signal is pending (i.e. the guest must stop + execution) */ +int CDECL kqemu_schedule(void) +{ + if (need_resched()) { + schedule(); + } + return signal_pending(current); +} + +char log_buf[4096]; + +void CDECL kqemu_log(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(log_buf, sizeof(log_buf), fmt, ap); + printk("kqemu: %s", log_buf); + va_end(ap); +} + +/*********************************************************/ + +static struct kqemu_global_state *kqemu_gs; + +struct kqemu_instance { + struct semaphore sem; + struct kqemu_state *state; +}; + +static int kqemu_open(struct inode *inode, struct file *filp) +{ + struct kqemu_instance *ks; + + ks = kmalloc(sizeof(struct kqemu_instance), GFP_KERNEL); + if (!ks) + return -ENOMEM; + init_MUTEX(&ks->sem); + ks->state = NULL; + filp->private_data = ks; + return 0; +} + +static int kqemu_release(struct inode *inode, struct file *filp) +{ + struct kqemu_instance *ks = filp->private_data; + + down(&ks->sem); + if (ks->state) { + kqemu_delete(ks->state); + ks->state = NULL; + } + up(&ks->sem); + + kfree(ks); + +#ifdef DEBUG + printk("lock_count=%d page_alloc_count=%d\n", + lock_count, page_alloc_count); +#endif + return 0; +} + +static int kqemu_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct kqemu_instance *ks = filp->private_data; + struct kqemu_state *s = ks->state; + long ret; + + down(&ks->sem); + switch(cmd) { + case KQEMU_INIT: + { + struct kqemu_init d1, *d = &d1; + if (s) { + ret = -EIO; + break; + } + if (copy_from_user(d, (void *)arg, sizeof(*d))) { + ret = -EFAULT; + break; + } + s = kqemu_init(d, kqemu_gs); + if (!s) { + ret = -ENOMEM; + break; + } + ks->state = s; + ret = 0; + } + break; + case KQEMU_EXEC: + { + struct kqemu_cpu_state *ctx; + if (!s) { + ret = -EIO; + break; + } + + ctx = kqemu_get_cpu_state(s); + if (copy_from_user(ctx, (void *)arg, sizeof(*ctx))) { + ret = -EFAULT; + break; + } + unlock_kernel(); + ret = kqemu_exec(s); + lock_kernel(); + if (copy_to_user((void *)arg, ctx, sizeof(*ctx))) { + ret = -EFAULT; + break; + } + } + break; + case KQEMU_GET_VERSION: + { + if (put_user(KQEMU_VERSION, (int *)arg) < 0) { + ret = -EFAULT; + } else { + ret = 0; + } + } + break; + default: + ret = -ENOIOCTLCMD; + break; + } + up(&ks->sem); + return ret; +} + +static struct file_operations kqemu_fops = { + owner: THIS_MODULE, + ioctl: kqemu_ioctl, + open: kqemu_open, + release: kqemu_release, +}; + +static struct miscdevice kqemu_dev = +{ + .minor = MISC_DYNAMIC_MINOR, + .name = "kqemu", + .fops = &kqemu_fops, +}; + +int init_module(void) +{ + int ret, max_locked_pages; + struct sysinfo si; + + printk("QEMU Accelerator Module version %d.%d.%d, Copyright (c) 2005-2006 Fabrice Bellard\n" + "This is a proprietary product. Read the LICENSE file for more information\n" + "Redistribution of this module is prohibited without authorization\n", + (KQEMU_VERSION >> 16), + (KQEMU_VERSION >> 8) & 0xff, + (KQEMU_VERSION) & 0xff); + si_meminfo(&si); + max_locked_pages = si.totalram / 2; + kqemu_gs = kqemu_global_init(max_locked_pages); + if (!kqemu_gs) + return -ENOMEM; + + if (major > 0) { + ret = register_chrdev(major, "kqemu", &kqemu_fops); + if (ret < 0) { + kqemu_global_delete(kqemu_gs); + printk("kqemu: could not get major %d\n", major); + return ret; + } + } else { + ret = misc_register (&kqemu_dev); + if (ret < 0) { + kqemu_global_delete(kqemu_gs); + printk("kqemu: could not create device\n"); + return ret; + } + } + printk("KQEMU installed, max_locked_mem=%dkB.\n", + max_locked_pages * 4); + return 0; +} + +void cleanup_module(void) +{ + if (major > 0) + unregister_chrdev(major, "kqemu"); + else + misc_deregister (&kqemu_dev); + if (kqemu_gs) { + kqemu_global_delete(kqemu_gs); + kqemu_gs = NULL; + } +} + +MODULE_LICENSE("Proprietary"); +module_init(init_module); +module_exit(cleanup_module); diff -urN linux-2.6.16/drivers/char/qemu/kqemu.h linux-2.6.16-qemu/drivers/char/qemu/kqemu.h --- linux-2.6.16/drivers/char/qemu/kqemu.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.16-qemu/drivers/char/qemu/kqemu.h 2006-08-06 16:55:22.000000000 +0200 @@ -0,0 +1,132 @@ +/* + * KQEMU header + * + * Copyright (c) 2004-2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef KQEMU_H +#define KQEMU_H + +#define KQEMU_VERSION 0x010300 + +struct kqemu_segment_cache { + uint32_t selector; + unsigned long base; + uint32_t limit; + uint32_t flags; +}; + +struct kqemu_cpu_state { +#ifdef __x86_64__ + unsigned long regs[16]; +#else + unsigned long regs[8]; +#endif + unsigned long eip; + unsigned long eflags; + + uint32_t dummy0, dummy1, dumm2, dummy3, dummy4; + + struct kqemu_segment_cache segs[6]; /* selector values */ + struct kqemu_segment_cache ldt; + struct kqemu_segment_cache tr; + struct kqemu_segment_cache gdt; /* only base and limit are used */ + struct kqemu_segment_cache idt; /* only base and limit are used */ + + unsigned long cr0; + unsigned long dummy5; + unsigned long cr2; + unsigned long cr3; + unsigned long cr4; + uint32_t a20_mask; + + /* sysenter registers */ + uint32_t sysenter_cs; + uint32_t sysenter_esp; + uint32_t sysenter_eip; + uint64_t efer __attribute__((aligned(8))); + uint64_t star; +#ifdef __x86_64__ + unsigned long lstar; + unsigned long cstar; + unsigned long fmask; + unsigned long kernelgsbase; +#endif + uint64_t tsc_offset; + + unsigned long dr0; + unsigned long dr1; + unsigned long dr2; + unsigned long dr3; + unsigned long dr6; + unsigned long dr7; + + uint8_t cpl; + uint8_t user_only; + + uint32_t error_code; /* error_code when exiting with an exception */ + unsigned long next_eip; /* next eip value when exiting with an interrupt */ + unsigned int nb_pages_to_flush; /* number of pages to flush, + KQEMU_FLUSH_ALL means full flush */ +#define KQEMU_MAX_PAGES_TO_FLUSH 512 +#define KQEMU_FLUSH_ALL (KQEMU_MAX_PAGES_TO_FLUSH + 1) + + long retval; + + /* number of ram_dirty entries to update */ + unsigned int nb_ram_pages_to_update; +#define KQEMU_MAX_RAM_PAGES_TO_UPDATE 512 +#define KQEMU_RAM_PAGES_UPDATE_ALL (KQEMU_MAX_RAM_PAGES_TO_UPDATE + 1) + +#define KQEMU_MAX_MODIFIED_RAM_PAGES 512 + unsigned int nb_modified_ram_pages; +}; + +struct kqemu_init { + uint8_t *ram_base; /* must be page aligned */ + unsigned long ram_size; /* must be multiple of 4 KB */ + uint8_t *ram_dirty; /* must be page aligned */ + uint32_t **phys_to_ram_map; /* must be page aligned */ + unsigned long *pages_to_flush; /* must be page aligned */ + unsigned long *ram_pages_to_update; /* must be page aligned */ + unsigned long *modified_ram_pages; /* must be page aligned */ +}; + +#define KQEMU_RET_ABORT (-1) +#define KQEMU_RET_EXCEPTION 0x0000 /* 8 low order bit are the exception */ +#define KQEMU_RET_INT 0x0100 /* 8 low order bit are the interrupt */ +#define KQEMU_RET_SOFTMMU 0x0200 /* emulation needed (I/O or + unsupported INSN) */ +#define KQEMU_RET_INTR 0x0201 /* interrupted by a signal */ +#define KQEMU_RET_SYSCALL 0x0300 /* syscall insn */ + +#ifdef _WIN32 +#define KQEMU_EXEC CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define KQEMU_INIT CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define KQEMU_GET_VERSION CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_READ_ACCESS) +#define KQEMU_MODIFY_RAM_PAGES CTL_CODE(FILE_DEVICE_UNKNOWN, 4, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#else +#define KQEMU_EXEC _IOWR('q', 1, struct kqemu_cpu_state) +#define KQEMU_INIT _IOW('q', 2, struct kqemu_init) +#define KQEMU_GET_VERSION _IOR('q', 3, int) +#define KQEMU_MODIFY_RAM_PAGES _IOW('q', 4, int) +#endif + +#endif /* KQEMU_H */
signature.asc
Description: OpenPGP digital signature
_______________________________________________ Qemu-devel mailing list Qemu-devel@nongnu.org http://lists.nongnu.org/mailman/listinfo/qemu-devel