Import kqemu, submitted in the past, slightly tweaked by me

The QEMU Accelerator (KQEMU) is a driver allowing a user application to
run x86 code in a Virtual Machine (VM). The code can be either user or
kernel code, in 64, 32 or 16 bit protected mode. KQEMU is very similar
in essence to the VM86 Linux syscall call, but it adds some new concepts
to improve memory handling.

KQEMU is ported on many host OSes (currently Linux, Windows, FreeBSD,
Solaris). It can execute code from many guest OSes (e.g. Linux, Windows
2000/XP) even if the host CPU does not support hardware virtualization.
This commit is contained in:
todd 2008-01-19 23:55:19 +00:00
parent 9a1552a179
commit b0b7750cb0
10 changed files with 485 additions and 0 deletions

33
emulators/kqemu/Makefile Normal file
View File

@ -0,0 +1,33 @@
ONLY_FOR_ARCHS= i386 amd64
COMMENT= QEMU accelerator module
DISTNAME= kqemu-1.3.0pre11
CATEGORIES= emulators
HOMEPAGE= http://fabrice.bellard.free.fr/qemu/
MAINTAINER= Todd T. Fries <todd@OpenBSD.org>
# GPL
PERMIT_PACKAGE_CDROM= Yes
PERMIT_PACKAGE_FTP= Yes
PERMIT_DISTFILES_CDROM= Yes
PERMIT_DISTFILES_FTP= Yes
MASTER_SITES= ${HOMEPAGE}
BUILD_DEPENDS= ::devel/gmake
CONFIGURE_STYLE= gnu
MAKE_FILE= Makefile.openbsd
PREFIX= ${LOCALBASE}/lib/kqemu
post-extract:
@cp ${FILESDIR}/* ${WRKBUILD}
do-install:
${INSTALL_DATA_DIR} ${PREFIX}
${INSTALL_DATA} ${WRKBUILD}/kqemu.o ${PREFIX}
.include <bsd.port.mk>

5
emulators/kqemu/distinfo Normal file
View File

@ -0,0 +1,5 @@
MD5 (kqemu-1.3.0pre11.tar.gz) = 970521874ef8b1ba4598925ace5936c3
RMD160 (kqemu-1.3.0pre11.tar.gz) = 3d42d2e6f3ae94362c5a29c462f7fb839687a563
SHA1 (kqemu-1.3.0pre11.tar.gz) = 780d48b99715e6b2671864ad5050f1c9506fcb71
SHA256 (kqemu-1.3.0pre11.tar.gz) = 541aef5797e5c6c6a76e354c17c6513ca21fe3372ec79493a32f7e51ba785b0f
SIZE (kqemu-1.3.0pre11.tar.gz) = 161478

View File

@ -0,0 +1,23 @@
NOMAN = noman
.if ${MACHINE_ARCH} == "i386"
CFLAGS += -Wall -DI386
COMMON = kqemu-mod-i386.o
.elif ${MACHINE_ARCH} == "amd64"
CFLAGS += -Wall -DAMD64
COMMON = kqemu-mod-x86_64.o
.endif
SRCS = kqemu-openbsd.c
OBJS = ${COMMON}
LKM = kqemu
COMBINED = kqemu.o
.include <bsd.lkm.mk>
${COMMON}:
gmake -C common all
clean:
rm -f $(COMMON)
gmake -C common clean

View File

@ -0,0 +1,327 @@
/*
* Copyright (c) 2007 Enache Adrian <3n4ch3@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/exec.h>
#include <sys/fcntl.h>
#include <sys/ioccom.h>
#include <sys/lkm.h>
#include <sys/malloc.h>
#include <sys/signalvar.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include "kqemu-kernel.h"
#define KQEMU_DEV "/dev/kqemu"
#define KQEMU_MODE 0660
#define KQEMU_GID 597
#ifdef AMD64
#define want_resched (curcpu()->ci_want_resched)
#define vtophys(VA) ((*vtopte(VA) & PG_FRAME) | ((unsigned)(VA) & ~PG_FRAME))
#endif
struct kqemu_page* CDECL
kqemu_alloc_zeroed_page(unsigned long *ppage_index)
{
vaddr_t va;
paddr_t pa;
if(!(va = uvm_km_zalloc(kernel_map, PAGE_SIZE)))
return 0;
pmap_extract(pmap_kernel(), va, &pa);
*ppage_index = pa >> PAGE_SHIFT;
return (struct kqemu_page*)va;
}
void CDECL
kqemu_free_page(struct kqemu_page *page)
{
uvm_km_free(kernel_map, (vaddr_t)page, PAGE_SIZE);
}
void* CDECL
kqemu_io_map(unsigned long page_index, unsigned int size)
{
return 0; /*XXX*/
}
void CDECL
kqemu_io_unmap(void *ptr, unsigned int size)
{
/*XXX*/
}
struct kqemu_user_page *CDECL
kqemu_lock_user_page(unsigned long *ppage_index, unsigned long user_addr)
{
vaddr_t va = (vaddr_t)user_addr;
vm_map_t map = &curproc->p_vmspace->vm_map;
paddr_t pa;
if(uvm_map_pageable(map, va, va + PAGE_SIZE, FALSE, 0))
return 0;
pmap_extract(vm_map_pmap(map), va, &pa);
*ppage_index = pa >> PAGE_SHIFT;
return (struct kqemu_user_page*)va;
}
void CDECL
kqemu_log(const char *fmt, ...)
{
va_list va;
printf("kqemu: ");
va_start(va, fmt); vprintf(fmt, va); va_end(va);
}
void* CDECL
kqemu_page_kaddr(struct kqemu_page *page)
{
return page; /*XXX*/
}
int CDECL
kqemu_schedule(void)
{
if(want_resched)
yield();
return CURSIG(curproc);
}
void CDECL
kqemu_unlock_user_page(struct kqemu_user_page *page)
{
vaddr_t va = (vaddr_t)page;
vm_map_t map = &curproc->p_vmspace->vm_map;
if(uvm_map_pageable(map, va, va + PAGE_SIZE, TRUE, 0))
printf("kqemu: failed to unwire page at 0x%08lx\n", va);
}
void CDECL kqemu_vfree(void *ptr)
{
free(ptr, M_TEMP);
}
void* CDECL
kqemu_vmalloc(unsigned int size)
{
return malloc(size, M_TEMP, M_WAITOK);
}
unsigned long CDECL
kqemu_vmalloc_to_phys(const void *vaddr)
{
paddr_t pa = vtophys((vaddr_t)vaddr);
if(!pa)
return -1;
return pa >> PAGE_SHIFT;
}
/* /dev/kqemu device operations */
#define QEMU_MAGIC 0x554d4551
struct kqemu_instance {
int magic;
struct kqemu_state *state;
};
static struct kqemu_global_state *kqemu_gs;
static int
kqemuopen(dev_t dev, int flag, int devtype, struct proc* p)
{
struct kqemu_instance *ks;
if ((flag & (FREAD|FWRITE)) == FREAD)
return EPERM;
if(p->p_emuldata)
return EBUSY;
if(!(ks = malloc(sizeof *ks, M_EMULDATA, M_WAITOK)))
return ENOMEM;
ks->magic = QEMU_MAGIC;
ks->state = 0;
p->p_emuldata = ks;
return 0;
}
static int
kqemuclose(dev_t dev, int flag, int devtype, struct proc* p)
{
struct kqemu_instance *ks = p->p_emuldata;
if(!ks || ks->magic != QEMU_MAGIC){
printf("kqemu: the kqemu instance was lost\n");
return EIO;
}
if(ks->state) kqemu_delete(ks->state);
free(ks, M_EMULDATA);
p->p_emuldata = 0;
return 0;
}
static int
kqemuioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
struct kqemu_cpu_state *ctx;
struct kqemu_instance *ks = p->p_emuldata;
if(!ks || ks->magic != QEMU_MAGIC){
printf("kqemu: the kqemu instance was lost\n");
return EIO;
}
switch(cmd){
case KQEMU_INIT:
if(ks->state) return EIO;
ks->state = kqemu_init((struct kqemu_init*)data, kqemu_gs);
if(!ks->state) return ENOMEM;
break;
case KQEMU_EXEC:
if(!ks->state) return EIO;
ctx = kqemu_get_cpu_state(ks->state);
*ctx = *(struct kqemu_cpu_state*)data;
KERNEL_PROC_UNLOCK(p);
kqemu_exec(ks->state);
KERNEL_PROC_LOCK(p);
*(struct kqemu_cpu_state*)data = *ctx;
break;
case KQEMU_GET_VERSION:
*(int*)data = KQEMU_VERSION;
break;
default:
return ENOTTY;
}
return 0;
}
static struct cdevsw kqemu_cdevsw = {
kqemuopen,
kqemuclose,
(dev_type_read((*)))enodev,
(dev_type_write((*)))enodev,
kqemuioctl,
(dev_type_stop((*)))enodev,
0,
seltrue,
(dev_type_mmap((*)))enodev,
};
MOD_DEV("kqemu", LM_DT_CHAR, -1, &kqemu_cdevsw);
int lkmexists(struct lkm_table *);
static int
kqemu_mknod(const char *path, dev_t dev)
{
struct nameidata nd;
struct vattr vattr;
int error;
NDINIT(&nd, CREATE, LOCKPARENT, UIO_SYSSPACE, path, curproc);
if ((error = namei(&nd)) != 0)
return (error);
if (nd.ni_vp != NULL) {
VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
if (nd.ni_dvp == nd.ni_vp)
vrele(nd.ni_dvp);
else
vput(nd.ni_dvp);
vrele(nd.ni_vp);
return (EEXIST);
}
VATTR_NULL(&vattr);
vattr.va_rdev = dev;
vattr.va_mode = KQEMU_MODE;
vattr.va_type = VCHR;
return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr));
}
static int
kqemu_chgrp(const char *path)
{
struct nameidata nd;
struct vattr vattr;
int error;
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, curproc);
if ((error = namei(&nd)) != 0) {
return (error);
}
vn_lock(nd.ni_vp, LK_EXCLUSIVE | LK_RETRY, curproc);
VATTR_NULL(&vattr);
vattr.va_gid = KQEMU_GID;
error = VOP_SETATTR(nd.ni_vp, &vattr, curproc->p_ucred, curproc);
vput(nd.ni_vp);
return (error);
}
static int
kqemu_unlink(const char *path)
{
struct nameidata nd;
int error;
NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE,
path, curproc);
if ((error = namei(&nd)) != 0)
return (error);
uvm_vnp_uncache(nd.ni_vp);
return (VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd));
}
int
kqemu_lkmentry(struct lkm_table *lkmtp, int cmd, int ver)
{
int error = 0;
int max_locked_pages = physmem / 2;
if (ver != LKM_VERSION)
return (EINVAL);
if (cmd == LKM_E_LOAD)
lkmtp->private.lkm_any = (struct lkm_any *)&_module;
if ((error = lkmdispatch(lkmtp, cmd)) == 0) {
switch(cmd){
case LKM_E_LOAD:
if(!(kqemu_gs = kqemu_global_init(max_locked_pages)))
return ENOMEM;
printf("kqemu: kqemu version 0x%08x loaded,"
" max locked mem=%dkB\n",
KQEMU_VERSION, max_locked_pages << (PAGE_SHIFT - 10));
kqemu_unlink(KQEMU_DEV);
kqemu_mknod(KQEMU_DEV, makedev(_module.lkm_offset, 0));
kqemu_chgrp(KQEMU_DEV);
break;
case LKM_E_UNLOAD:
/* kqemu_global_delete() should return an
error if kqemu_gs->nb_kqemu_states > 0.
then this could be changed to:
if(kqemu_global_delete(kqemu_gs)) return EBUSY; */
kqemu_global_delete(kqemu_gs);
kqemu_unlink(KQEMU_DEV);
break;
}
}
return (error);
}

View File

@ -0,0 +1,18 @@
$OpenBSD: patch-common_Makefile,v 1.1.1.1 2008/01/19 23:55:19 todd Exp $
--- common/Makefile.orig Tue Feb 6 22:02:00 2007
+++ common/Makefile Mon Nov 12 23:59:17 2007
@@ -41,7 +41,13 @@ LD=ld
endif
DEFINES=-D__KERNEL__
-INCLUDES=-nostdinc -iwithprefix include -I. -I..
+
+ifdef CONFIG_OPENBSD
+ INCLUDES=-I. -I..
+else
+ INCLUDES=-nostdinc -iwithprefix include -I. -I..
+endif
+
TOOLS_CFLAGS=-Wall -O2 -Werror -g
COMMON_CFLAGS=-Wall -O2 -fomit-frame-pointer -fno-strict-aliasing -Werror
ifeq ($(ARCH), x86_64)

View File

@ -0,0 +1,34 @@
$OpenBSD: patch-common_monitor_c,v 1.1.1.1 2008/01/19 23:55:19 todd Exp $
--- common/monitor.c.orig Tue Feb 6 22:02:00 2007
+++ common/monitor.c Mon Nov 12 23:59:17 2007
@@ -990,7 +990,8 @@ static void *map_vaddr(struct kqemu_stat
e = &s->soft_tlb[(addr >> PAGE_SHIFT) & (SOFT_TLB_SIZE - 1)];
redo:
if (e->vaddr[(is_user << 1) + is_write] != (addr & PAGE_MASK)) {
- soft_tlb_fill(s, addr, is_write, is_user);
+ if(cpu_x86_handle_mmu_fault(s, addr, is_write, is_user, 1))
+ return NULL;
goto redo;
} else {
taddr = e->addend + addr;
@@ -1802,6 +1803,11 @@ static void update_dt_cache(struct kqemu
page_end = dt_end;
sel2 = sel + (page_end - dt_ptr);
ptr = map_vaddr(s, dt_ptr, 0, 0);
+ if(!ptr)
+ /* Open/NetBSD have a 'dynamic' GDT, but they load the gdt
+ register with LGDT only once and with a limit far beyond
+ the end of the memory actually mapped for the table */
+ goto skip_the_rest;
ram_addr = ram_ptr_to_ram_addr(s, ptr);
if (dt_changed ||
s->dt_ram_addr[dt_type][pindex] != ram_addr ||
@@ -1818,7 +1824,7 @@ static void update_dt_cache(struct kqemu
sel_end = (s->dt_limit[dt_type] + 1) & ~7;
if (sel < sel_end)
reset_dt_entries(s, dt_type, sel, sel_end);
-
+skip_the_rest:
s->dt_base[dt_type] = base;
s->dt_limit[dt_type] = limit;
}

View File

@ -0,0 +1,24 @@
$OpenBSD: patch-configure,v 1.1.1.1 2008/01/19 23:55:19 todd Exp $
--- configure.orig Tue Feb 6 22:02:00 2007
+++ configure Mon Nov 12 23:59:17 2007
@@ -112,6 +112,10 @@ oss="yes"
OpenBSD)
bsd="yes"
oss="yes"
+openbsd="yes"
+if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
+ kqemu="yes"
+fi
;;
Darwin)
bsd="yes"
@@ -344,6 +348,9 @@ if test $linux = "yes" ; then
if test $kbuild26 = "yes" ; then
echo "CONFIG_KBUILD26=yes" >> $config_mak
fi
+fi
+if test $openbsd = "yes"; then
+ echo "CONFIG_OPENBSD=yes" >> $config_mak
fi
echo "SRC_PATH=$source_path" >> $config_mak

View File

@ -0,0 +1,9 @@
The QEMU Accelerator (KQEMU) is a driver allowing a user application to
run x86 code in a Virtual Machine (VM). The code can be either user or
kernel code, in 64, 32 or 16 bit protected mode. KQEMU is very similar
in essence to the VM86 Linux syscall call, but it adds some new concepts
to improve memory handling.
KQEMU is ported on many host OSes (currently Linux, Windows, FreeBSD,
Solaris). It can execute code from many guest OSes (e.g. Linux, Windows
2000/XP) even if the host CPU does not support hardware virtualization.

View File

@ -0,0 +1,9 @@
If you want to load this kernel module at boot time, add the following
lines to /etc/rc.securelevel :
if [ -r /usr/local/lib/kqemu/kqemu.o ]; then
echo ' kqemu'; /sbin/modload /usr/local/lib/kqemu/kqemu.o
fi
If you want a user to be able to use this module, add them to the group
_kqemu.

View File

@ -0,0 +1,3 @@
@comment $OpenBSD: PLIST,v 1.1.1.1 2008/01/19 23:55:19 todd Exp $
@newgroup _kqemu:597
kqemu.o