/* $Id: kmod-freebsd.c,v 1.3 2005/03/23 17:53:24 pb Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define __KERNEL__ #include "kqemu.h" MALLOC_DECLARE(M_KQEMU); MALLOC_DEFINE(M_KQEMU, "kqemu", "kqemu buffers"); #define USER_BASE 0x1000 /* quick and dirty convert a physical address to a virtual address */ static vm_offset_t pa_to_va(vm_paddr_t pa, vm_offset_t beg, vm_offset_t end) { struct vmspace *vm = curproc->p_vmspace; vm_offset_t va; pmap_t pmap; pmap = vm_map_pmap(&vm->vm_map); for (va = beg; va != end; va += PAGE_SIZE) if (pa == pmap_extract(pmap, va)) return va; printf("pa_to_va failed: pa=%08x\n", pa); return -1; } /* lock the page at virtual address 'user_addr' and return its physical page index. Return -1 if error */ unsigned long CDECL kqemu_lock_user_page(unsigned long user_addr) { struct vmspace *vm = curproc->p_vmspace; vm_offset_t va = user_addr; vm_paddr_t pa = 0; int ret; pmap_t pmap; ret = vm_map_wire(&vm->vm_map, va, va+PAGE_SIZE, VM_MAP_WIRE_USER); if (ret != KERN_SUCCESS) { printf("kqemu_lock_user_page(%08lx) failed, ret=%d\n", user_addr, ret); return -1; } pmap = vm_map_pmap(&vm->vm_map); pa = pmap_extract(pmap, va); // printf("kqemu_lock_user_page(%08lx) va=%08x pa=%08x\n", user_addr, va, pa); return pa >> PAGE_SHIFT; } void CDECL kqemu_unlock_user_page(unsigned long page_index) { struct vmspace *vm = curproc->p_vmspace; vm_offset_t va; int ret; // printf("kqemu_unlock_user_page(%08lx)\n", page_index); va = pa_to_va(page_index << PAGE_SHIFT, USER_BASE, KERNBASE); ret = vm_map_unwire(&vm->vm_map, va, va+PAGE_SIZE, VM_MAP_WIRE_USER); if (ret != KERN_SUCCESS) { printf("kqemu_unlock_user_page(%08lx) failed, ret=%d\n", page_index, ret); } } /* * Allocate a new page. The page must be mapped in the kernel space. * Return the page_index or -1 if error. */ unsigned long CDECL kqemu_alloc_zeroed_page(void) { pmap_t pmap; vm_offset_t va; vm_paddr_t pa; va = kmem_alloc(kernel_map, PAGE_SIZE); if (va == 0) { printf("kqemu_alloc_zeroed_page: NULL\n"); return -1; } pmap = vm_map_pmap(kernel_map); pa = pmap_extract(pmap, va); // printf("kqemu_alloc_zeroed_page: %08x\n", pa); return pa >> PAGE_SHIFT; } void CDECL kqemu_free_page(unsigned long page_index) { printf("kqemu_free_page(%08lx)\n", page_index); } /* return kernel address of the physical page page_index */ void *CDECL kqemu_page_kaddr(unsigned long page_index) { vm_offset_t va; va = pa_to_va(page_index << PAGE_SHIFT, KERNBASE, 0); if (va != -1) { printf("kqemu_page_kaddr(%08lx)=%08x\n", page_index, va); return (void *)va; } printf("kqemu_page_kaddr(%08lx)=NOT FOUND\n", page_index); return NULL; } /* contraint: each page of the vmalloced area must be in the first 4 GB of physical memory */ void * CDECL kqemu_vmalloc(unsigned int size) { struct vmspace *vm = curproc->p_vmspace; vm_offset_t va = USER_BASE; int rv; if (size % PAGE_SIZE != 0) { printf("kqemu_vmalloc(%d) not a multiple of page size\n", size); return NULL; } rv = vm_map_find(&vm->vm_map, NULL, 0, &va, size, 1, VM_PROT_ALL, VM_PROT_ALL, 0); if (rv != KERN_SUCCESS) { printf("kqemu_vmalloc(%d) failed rv=%d\n", size, rv); return NULL; } printf("kqemu_vmalloc(%d): %08x\n", size, va); return (void *)va; } void CDECL kqemu_vfree(void *ptr) { printf("kqemu_vfree(%p)\n", ptr); } /* return the physical page index for a given virtual page */ unsigned long CDECL kqemu_vmalloc_to_phys(const void *vaddr) { struct vmspace *vm = curproc->p_vmspace; vm_paddr_t pa; pmap_t pmap; pmap = vm_map_pmap(&vm->vm_map); pa = pmap_extract(pmap, (vm_offset_t)vaddr); if (pa == 0) { printf("kqemu_vmalloc_to_phys(%p)->error\n", vaddr); return -1; } printf("kqemu_vmalloc_to_phys(%p)->%08x\n", vaddr, pa); return pa >> PAGE_SHIFT; } /* return TRUE if a signal is pending (i.e. the guest must stop execution) */ int CDECL kqemu_schedule(void) { // printf("kqemu_schedule\n"); mtx_lock_spin(&sched_lock); mi_switch(SW_VOL, NULL); mtx_unlock_spin(&sched_lock); return SIGPENDING(curthread); } 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); } struct kqemu_instance { // struct semaphore sem; struct kqemu_state *state; }; static d_close_t kqemu_close; static d_open_t kqemu_open; static d_ioctl_t kqemu_ioctl; static struct cdevsw kqemu_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = kqemu_open, .d_ioctl = kqemu_ioctl, .d_close = kqemu_close, .d_name = "kqemu" }; /* For use with make_dev(9)/destroy_dev(9). */ static struct cdev *kqemu_dev; /* ARGSUSED */ static int kqemu_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td) { struct kqemu_instance *ks; ks = malloc(sizeof(struct kqemu_instance), M_KQEMU, M_WAITOK); if (ks == NULL) { printf("malloc failed\n"); return ENOMEM; } ks->state = NULL; dev->si_drv1 = ks; return 0; } /* ARGSUSED */ static int kqemu_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags __unused, struct thread *td) { int error = 0; int ret; struct kqemu_instance *ks = dev->si_drv1; struct kqemu_state *s = ks->state; switch(cmd) { case KQEMU_INIT: { struct kqemu_init d1, *d = &d1; if (s != NULL) { error = EIO; break; } d1 = *(struct kqemu_init *)addr; printf("ram_base=%p ram_size=%ld\n", d1.ram_base, d1.ram_size); s = kqemu_init(d, 16000); if (s == NULL) { error = ENOMEM; break; } ks->state = s; break; } case KQEMU_EXEC: { struct kqemu_cpu_state *ctx; if (s == NULL) { error = EIO; break; } ctx = kqemu_get_cpu_state(s); *ctx = *(struct kqemu_cpu_state *)addr; DROP_GIANT(); ret = kqemu_exec(s); PICKUP_GIANT(); td->td_retval[0] = ret; *(struct kqemu_cpu_state *)addr = *ctx; break; } case KQEMU_GET_VERSION: *(int *)addr = KQEMU_VERSION; break; default: error = EINVAL; } return error; } /* ARGSUSED */ static int kqemu_close(struct cdev *dev __unused, int flags, int fmt __unused, struct thread *td) { return 0; } /* ARGSUSED */ static int kqemu_modevent(module_t mod __unused, int type, void *data __unused) { int error = 0; switch (type) { case MOD_LOAD: printf("kqemu version 0x%08x\n", KQEMU_VERSION); kqemu_dev = make_dev(&kqemu_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, "kqemu"); break; case MOD_UNLOAD: destroy_dev(kqemu_dev); break; case MOD_SHUTDOWN: break; default: error = EOPNOTSUPP; break; } return (error); } DEV_MODULE(kqemu, kqemu_modevent, NULL); MODULE_VERSION(kqemu, 1);