/* * FreeBSD kernel wrapper for KQEMU * Copyright (c) 2005 Antony T Curtis * * Based upon the Linux wrapper by Fabrice Bellard */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define __KERNEL__ #include "kqemu.h" static MALLOC_DEFINE(M_KQEMU, "KQEMU", "KQEMU Resources"); /* lock the page at virtual address 'user_addr' and return its page index. Return -1 if error */ unsigned long CDECL kqemu_lock_user_page(unsigned long user_addr) { vm_map_t map = &curproc->p_vmspace->vm_map; vm_offset_t user_page = (vm_offset_t) (user_addr >> PAGE_SHIFT); vm_offset_t user_start = (vm_offset_t) (user_page << PAGE_SHIFT); int rv; rv = vm_map_pageable(map, user_start, user_start + PAGE_SIZE - 1, 0); if (rv != 0) return -1; return (long) user_page; } void CDECL kqemu_unlock_user_page(unsigned long page_index) { vm_map_t map = &curproc->p_vmspace->vm_map; vm_offset_t user_start = (vm_offset_t) (page_index << PAGE_SHIFT); vm_map_pageable(map, user_start, user_start + PAGE_SIZE - 1, 1); } unsigned long CDECL kqemu_alloc_zeroed_page(void) { void *addr = contigmalloc(PAGE_SIZE, M_KQEMU, M_WAITOK, 0, ~0ul, PAGE_SIZE, 0); if (!addr) return -1; memset(addr, 0, PAGE_SIZE); return ((long) addr) >> PAGE_SHIFT; } void CDECL kqemu_free_page(unsigned long page_index) { contigfree((void *)(page_index << PAGE_SHIFT), PAGE_SIZE, M_KQEMU); } void * CDECL kqemu_page_kaddr(unsigned long page_index) { return (void *)(page_index << PAGE_SHIFT); } /* contraint: each page of the vmalloced area must be in the first 4 GB of physical memory */ void * CDECL kqemu_vmalloc(unsigned int size) { return malloc(size, M_KQEMU, M_WAITOK); } void CDECL kqemu_vfree(void *ptr) { return free(ptr, M_KQEMU); } unsigned long CDECL kqemu_vmalloc_to_phys(const void *vaddr) { vm_map_t map = &curproc->p_vmspace->vm_map; vm_map_entry_t page; if (!vm_map_lookup_entry(map, (vm_offset_t) vaddr, &page)) return -1; return ((vm_offset_t) vaddr) >> PAGE_SHIFT; } static int curpriority_cmp(struct proc *p) { int c_class, p_class; c_class = RTP_PRIO_BASE(curproc->p_rtprio.type); p_class = RTP_PRIO_BASE(p->p_rtprio.type); if (p_class != c_class) return (p_class - c_class); if (p_class == RTP_PRIO_NORMAL) return (((int)p->p_priority - (int)curpriority) / PPQ); return ((int)p->p_rtprio.prio - (int)curproc->p_rtprio.prio); } /* return TRUE if a signal is pending (i.e. the guest must stop execution) */ int CDECL kqemu_schedule(void) { struct proc *p = curproc; if (curpriority_cmp(p) > 0) { int s = splhigh(); p->p_priority = MAXPRI; setrunqueue(p); p->p_stats->p_ru.ru_nvcsw++; mi_switch(); splx(s); } return issignal(curproc) != 0; } static 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); printf("kqemu: %s", log_buf); va_end(ap); } /*********************************************************/ #define KQEMU_MAX_INSTANCES 4 struct kqemu_instance { struct kqemu_state *state; }; static int kqemu_ref_count = 0; static int max_locked_pages; static dev_t kqemu_dev; static d_open_t kqemu_open; static d_close_t kqemu_close; static d_ioctl_t kqemu_ioctl; static struct cdevsw kqemu_cdevsw = { /* open */ kqemu_open, /* close */ kqemu_close, /* read */ noread, /* write */ nowrite, /* ioctl */ kqemu_ioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "kqemu", /* maj */ KQEMU_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; int kqemu_open(dev, flags, fmt, p) dev_t dev; int flags, fmt; struct proc *p; { struct kqemu_instance *ks; if (dev->si_drv1 || kqemu_ref_count >= KQEMU_MAX_INSTANCES) return(EBUSY); if ((flags & (FREAD|FWRITE)) == FREAD) return(EPERM); ks = (struct kqemu_instance *) malloc(sizeof(*ks), M_KQEMU, M_WAITOK); if (ks == NULL) return(ENOMEM); memset(ks, 0, sizeof *ks); dev->si_drv1 = ks; kqemu_ref_count++; kqemu_log("opened by pid=%d\n", p->p_pid); return(0); } int kqemu_close(dev, flags, fmt, p) dev_t dev; int flags, fmt; struct proc *p; { struct kqemu_instance *ks = (struct kqemu_instance *) dev->si_drv1; if (ks->state) { kqemu_delete(ks->state); ks->state = NULL; } free(ks, M_KQEMU); dev->si_drv1 = NULL; kqemu_ref_count--; kqemu_log("closed by pid=%d\n", p->p_pid); return(0); } int kqemu_ioctl(dev, cmd, cmdarg, flags, p) dev_t dev; unsigned long cmd; caddr_t cmdarg; int flags; struct proc *p; { struct kqemu_instance *ks = (struct kqemu_instance *) dev->si_drv1; struct kqemu_state *s = ks->state; long ret; int error; switch (cmd) { case KQEMU_INIT: { struct kqemu_init d; if (s) return(EIO); if ((error = copyin(*(caddr_t *)cmdarg, &d, sizeof(d)))) return(error); if (!(s = kqemu_init(&d, max_locked_pages))) return(ENOMEM); ks->state = s; ret = 0; break; } case KQEMU_EXEC: { struct kqemu_cpu_state *ctx; if (!s) return(EIO); ctx = kqemu_get_cpu_state(s); if ((error = copyin(*(caddr_t *)cmdarg, ctx, sizeof(*ctx)))) return(error); ret = kqemu_exec(s); if ((error = copyout(ctx, *(caddr_t *)cmdarg, sizeof(*ctx)))) return(error); break; } case KQEMU_GET_VERSION: { *(int *)cmdarg = KQEMU_VERSION; ret = 0; break; } default: return(ENXIO); } return(ret); } static int init_module(void) { int rc; printf("QEMU Accelerator Module version %d.%d.%d, Copyright (c) 2005 Fabrice Bellard\n" "FreeBSD wrapper port, Copyright (c) 2005 Antony T Curtis\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); max_locked_pages = physmem / (2 * KQEMU_MAX_INSTANCES); if (max_locked_pages > 32768) max_locked_pages = 32768; if ((rc = cdevsw_add(&kqemu_cdevsw))) { kqemu_log("error registering cdevsw, rc=%d\n", rc); return(ENOENT); } kqemu_dev = make_dev(&kqemu_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "kqemu"); kqemu_log("KQEMU installed, max_instances=%d max_locked_mem=%dkB.\n", KQEMU_MAX_INSTANCES, max_locked_pages * 4); kqemu_ref_count = 0; return 0; } static void cleanup_module(void) { int rc; destroy_dev(kqemu_dev); if ((rc = cdevsw_remove(&kqemu_cdevsw))) kqemu_log("error unregistering, rc=%d\n", rc); } static int kqemu_modevent(module_t mod, int type, void *data) { int err = 0; switch (type) { case MOD_LOAD: err = init_module(); break; case MOD_UNLOAD: if (kqemu_ref_count > 0) { err = EBUSY; break; } /* fall through */ case MOD_SHUTDOWN: cleanup_module(); break; default: err = EINVAL; break; } return(err); } static moduledata_t kqemu_mod = { "kqemu_driver", kqemu_modevent, NULL }; DECLARE_MODULE(kqemu, kqemu_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);