From d4a8cf56d66921bc254d88c4854c227c37e29eaa Mon Sep 17 00:00:00 2001 From: Kevin Lawton Date: Fri, 3 Jan 2003 20:02:54 +0000 Subject: [PATCH] Cleaned up host-linux.c. Moved more logic into monitor-host, which is host neutral. --- bochs/plex86/kernel/host-linux.c | 737 +++++++++++++------------- bochs/plex86/kernel/host-null.c | 4 +- bochs/plex86/kernel/include/monitor.h | 18 +- bochs/plex86/kernel/monitor-host.c | 97 +++- 4 files changed, 464 insertions(+), 392 deletions(-) diff --git a/bochs/plex86/kernel/host-linux.c b/bochs/plex86/kernel/host-linux.c index 8a69d02a5..587c445f6 100644 --- a/bochs/plex86/kernel/host-linux.c +++ b/bochs/plex86/kernel/host-linux.c @@ -48,7 +48,7 @@ /************************************************************************/ -/* Compatibility macros for older kernels */ +/* Compatibility macros & convenience functions for older kernels */ /************************************************************************/ #ifndef EXPORT_NO_SYMBOLS @@ -66,27 +66,30 @@ #endif #if LINUX_VERSION_CODE < VERSION_CODE(2,1,0) -static inline unsigned long copy_from_user(void *to, const void *from, unsigned long n) + static inline unsigned long +copy_from_user(void *to, const void *from, unsigned long n) { - int i; - if ((i = verify_area(VERIFY_READ, from, n)) != 0) - return i; - memcpy_fromfs(to, from, n); - return 0; + int i; + if ( (i = verify_area(VERIFY_READ, from, n)) != 0 ) + return i; + memcpy_fromfs(to, from, n); + return 0; } -static inline unsigned long copy_to_user(void *to, const void *from, unsigned long n) + static inline unsigned long +copy_to_user(void *to, const void *from, unsigned long n) { - int i; - if ((i = verify_area(VERIFY_WRITE, to, n)) != 0) - return i; - memcpy_tofs(to, from, n); - return 0; + int i; + if ( (i = verify_area(VERIFY_WRITE, to, n)) != 0 ) + return i; + memcpy_tofs(to, from, n); + return 0; } #endif #if LINUX_VERSION_CODE >= VERSION_CODE(2,1,18) && !defined(THIS_MODULE) /* Starting with version 2.1.18, the __this_module symbol is present, - but the THIS_MODULE #define was introduced much later ... */ + * but the THIS_MODULE #define was introduced much later ... + */ #define THIS_MODULE (&__this_module) #endif @@ -103,7 +106,7 @@ MODULE_PARM(plex_major, "i"); MODULE_PARM_DESC(plex_major, "major number (default " __MODULE_STRING(PLEX86_MAJOR) ")"); #endif -/* The kernel segment base */ +/* The kernel segment base. */ #if LINUX_VERSION_CODE < VERSION_CODE(2,1,0) # define KERNEL_OFFSET 0xc0000000 #else @@ -111,26 +114,28 @@ MODULE_PARM_DESC(plex_major, "major number (default " __MODULE_STRING(PLEX86_MAJ #endif -/* File operations */ -static int plex86_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +/* File operations. */ +static int plex86_ioctl(struct inode *, struct file *, unsigned int, + unsigned long); static int plex86_open(struct inode *, struct file *); #if LINUX_VERSION_CODE >= VERSION_CODE(2,1,31) - static int plex86_release(struct inode *, struct file *); +static int plex86_release(struct inode *, struct file *); #else - static void plex86_release(struct inode *, struct file *); +static void plex86_release(struct inode *, struct file *); #endif #if LINUX_VERSION_CODE >= VERSION_CODE(2,1,0) - static int plex86_mmap(struct file * file, struct vm_area_struct * vma); +static int plex86_mmap(struct file * file, struct vm_area_struct * vma); #else - static int plex86_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma); +static int plex86_mmap(struct inode * inode, struct file * file, + struct vm_area_struct * vma); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9) -/* New License scheme */ +/* New License scheme. */ #ifdef MODULE_LICENSE -MODULE_LICENSE("GPL"); /* Close enough. */ +MODULE_LICENSE("GPL"); /* Close enough. Keeps kernel from complaining. */ #endif #endif @@ -140,12 +145,12 @@ MODULE_LICENSE("GPL"); /* Close enough. */ /* Structures / Variables */ /************************************************************************/ -static int retrieve_vm_pages(Bit32u *page, int max_pages, void *addr, unsigned size); -static unsigned retrieve_phy_pages(Bit32u *page, int max_pages, void *addr, unsigned size); +static int retrieve_vm_pages(Bit32u *page, int max_pages, void *addr, + unsigned size); +static unsigned retrieve_phy_pages(Bit32u *page, int max_pages, void *addr, + unsigned size); static int retrieve_monitor_pages(void); -monitor_pages_t monitor_pages; -extern unsigned intRedirCount[]; static struct file_operations plex86_fops = { @@ -173,14 +178,14 @@ int plex86_read_procmem(char *, char **, off_t, int, int); #if LINUX_VERSION_CODE < VERSION_CODE(2,3,25) static struct proc_dir_entry plex86_proc_entry = { - 0, /* dynamic inode */ - 6, "driver/plex86", /* len, name */ - S_IFREG | S_IRUGO, /* mode */ - 1, 0, 0, - 0, - NULL, - &plex86_read_procmem, /* read function */ -}; + 0, /* dynamic inode */ + 6, "driver/plex86", /* len, name */ + S_IFREG | S_IRUGO, /* mode */ + 1, 0, 0, + 0, + NULL, + &plex86_read_procmem, /* read function */ + }; #endif @@ -191,95 +196,95 @@ static struct proc_dir_entry plex86_proc_entry = { int init_module(void) { - int err; + int err; - /* clear uninitialised structures */ - memset(&monitor_pages, 0, sizeof(monitor_pages)); + /* Clear uninitialised structures. */ + memset(&monitor_pages, 0, sizeof(monitor_pages)); - /* register the device with the kernel */ - err = register_chrdev(plex_major, "plex86", &plex86_fops); - if (err < 0) { - printk(KERN_WARNING "plex86: can't get major %d\n", plex_major); - return(err); - } - /* If this was a dynamic allocation, save the major for - * the release code - */ - if(!plex_major) - plex_major = err; + /* Register the device with the kernel. */ + err = register_chrdev(plex_major, "plex86", &plex86_fops); + if (err < 0) { + printk(KERN_WARNING "plex86: can't get major %d\n", plex_major); + return(err); + } + /* If this was a dynamic allocation, save the major for + * the release code + */ + if(!plex_major) + plex_major = err; - /* register the /proc entry */ + /* Register the /proc entry. */ #ifdef CONFIG_PROC_FS #if LINUX_VERSION_CODE >= VERSION_CODE(2,3,25) - if (!create_proc_info_entry("driver/plex86", 0, NULL, plex86_read_procmem)) - printk(KERN_ERR "plex86: registering /proc/driver/plex86 failed\n"); + if (!create_proc_info_entry("driver/plex86", 0, NULL, plex86_read_procmem)) + printk(KERN_ERR "plex86: registering /proc/driver/plex86 failed\n"); #else - proc_register_dynamic(&proc_root, &plex86_proc_entry); + proc_register_dynamic(&proc_root, &plex86_proc_entry); #endif #endif - /* register /dev/misc/plex86 with devfs */ + /* Register /dev/misc/plex86 with devfs. */ #ifdef CONFIG_DEVFS_FS - my_devfs_entry = devfs_register(NULL, "misc/plex86", - DEVFS_FL_DEFAULT, - plex_major, 0 /* minor mode*/, - S_IFCHR | 0666, &plex86_fops, - NULL /* "info" */); - if (!my_devfs_entry) - printk(KERN_ERR "plex86: registering misc/plex86 devfs entry failed\n"); + my_devfs_entry = devfs_register(NULL, "misc/plex86", + DEVFS_FL_DEFAULT, + plex_major, 0 /* minor mode*/, + S_IFCHR | 0666, &plex86_fops, + NULL /* "info" */); + if (!my_devfs_entry) + printk(KERN_ERR "plex86: registering misc/plex86 devfs entry failed\n"); #endif - /* retrieve the monitor physical pages */ - if (!retrieve_monitor_pages()) { - printk(KERN_ERR "plex86: retrieve_monitor_pages returned error\n"); - err = -EINVAL; - goto fail_retrieve_pages; - } + /* Retrieve the monitor physical pages. */ + if ( !retrieve_monitor_pages() ) { + printk(KERN_ERR "plex86: retrieve_monitor_pages returned error\n"); + err = -EINVAL; + goto fail_retrieve_pages; + } - /* Kernel independent code to be run when kernel module is loaded. */ - if ( !genericModuleInit() ) { - printk(KERN_ERR "plex86: genericModuleInit returned error\n"); - err = -EINVAL; - goto fail_cpu_capabilities; - } + /* Kernel independent code to be run when kernel module is loaded. */ + if ( !genericModuleInit() ) { + printk(KERN_ERR "plex86: genericModuleInit returned error\n"); + err = -EINVAL; + goto fail_cpu_capabilities; + } - /* success */ - EXPORT_NO_SYMBOLS; - return(0); + /* Success. */ + EXPORT_NO_SYMBOLS; + return(0); fail_cpu_capabilities: fail_retrieve_pages: - /* unregister /proc entry */ + /* Unregister /proc entry. */ #ifdef CONFIG_PROC_FS #if LINUX_VERSION_CODE >= VERSION_CODE(2,3,25) - remove_proc_entry("driver/plex86", NULL); + remove_proc_entry("driver/plex86", NULL); #else - proc_unregister(&proc_root, plex86_proc_entry.low_ino); + proc_unregister(&proc_root, plex86_proc_entry.low_ino); #endif #endif - /* unregister device */ - unregister_chrdev(plex_major, "plex86"); - return err; + /* Unregister device. */ + unregister_chrdev(plex_major, "plex86"); + return err; } -void + void cleanup_module(void) { - /* unregister device */ - unregister_chrdev(plex_major, "plex86"); + /* Unregister device. */ + unregister_chrdev(plex_major, "plex86"); - /* unregister /proc entry */ + /* Unregister /proc entry. */ #ifdef CONFIG_PROC_FS #if LINUX_VERSION_CODE >= VERSION_CODE(2,3,25) - remove_proc_entry("driver/plex86", NULL); + remove_proc_entry("driver/plex86", NULL); #else - proc_unregister(&proc_root, plex86_proc_entry.low_ino); + proc_unregister(&proc_root, plex86_proc_entry.low_ino); #endif #endif #ifdef CONFIG_DEVFS_FS - devfs_unregister(my_devfs_entry); + devfs_unregister(my_devfs_entry); #endif } @@ -298,7 +303,7 @@ plex86_open(struct inode *inode, struct file *filp) #endif /* Allocate a VM structure. */ - if ( (vm = vmalloc(sizeof(vm_t))) == NULL ) + if ( (vm = hostAllocZeroedMem(sizeof(vm_t))) == NULL ) return -ENOMEM; filp->private_data = vm; @@ -316,28 +321,27 @@ plex86_open(struct inode *inode, struct file *filp) #endif plex86_release(struct inode *inode, struct file *filp) { - vm_t *vm = (vm_t *)filp->private_data; - filp->private_data = NULL; + vm_t *vm = (vm_t *)filp->private_data; + filp->private_data = NULL; - /* Free the virtual memory. */ - hostUnreserveGuestPages( vm ); - unallocVmPages( vm ); + /* Free the virtual memory. */ + unreserveGuestPhyPages(vm); + unallocVmPages( vm ); - /* Free the VM structure. */ - memset( vm, 0, sizeof(*vm) ); - vfree( vm ); + /* Free the VM structure. */ + memset( vm, 0, sizeof(*vm) ); + vfree( vm ); #if LINUX_VERSION_CODE < VERSION_CODE(2,4,0) - MOD_DEC_USE_COUNT; + MOD_DEC_USE_COUNT; #endif #if LINUX_VERSION_CODE >= VERSION_CODE(2,1,31) - return(0); + return(0); #endif } - int plex86_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) @@ -359,116 +363,98 @@ plex86_ioctl(struct inode *inode, struct file *filp, } -int + int #if LINUX_VERSION_CODE >= VERSION_CODE(2,1,0) plex86_mmap(struct file * file, struct vm_area_struct * vma) #else plex86_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma) #endif { - vm_t *vm = (vm_t *)file->private_data; - int i, firstpage, nr_pages; - Bit32u *pagesArray; - unsigned stateMask; + vm_t *vm = (vm_t *)file->private_data; + int firstpage, pagesN; +#if LINUX_VERSION_CODE >= VERSION_CODE(2,1,0) + void *inode = NULL; /* Not used; for consistency of passing args. */ +#endif + int ret; - /* Must have memory allocated */ - if (!vm->pages.guest_n_pages) { - printk(KERN_WARNING "plex86: device not initialized\n"); - return -EACCES; - } - - /* Private mappings make no sense ... */ - if ( !(vma->vm_flags & VM_SHARED) ) { - printk(KERN_WARNING "plex86: private mapping\n"); - return -EINVAL; - } + /* Private mappings make no sense ... */ + if ( !(vma->vm_flags & VM_SHARED) ) { + printk(KERN_WARNING "plex86: private mapping\n"); + return -EINVAL; + } #if LINUX_VERSION_CODE < VERSION_CODE(2,3,25) - /* To simplify things, allow only page-aligned offsets */ - if ( vma->vm_offset & (PAGE_SIZE - 1) ) { - printk(KERN_WARNING "plex86: unaligned offset %08lx\n", vma->vm_offset); - return -EINVAL; - } + /* To simplify things, allow only page-aligned offsets */ + if ( vma->vm_offset & (PAGE_SIZE - 1) ) { + printk(KERN_WARNING "plex86: unaligned offset %08lx\n", vma->vm_offset); + return -EINVAL; + } #endif - /* Map all requested pages in ... */ #if LINUX_VERSION_CODE >= VERSION_CODE(2,3,25) - firstpage = vma->vm_pgoff; + firstpage = vma->vm_pgoff; #else - firstpage = vma->vm_offset >> PAGE_SHIFT; + firstpage = vma->vm_offset >> PAGE_SHIFT; #endif - nr_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + pagesN = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; - /* The memory map: - * guest physical memory (guest_n_pages) - * log_buffer (1) - * guest_cpu (1) - */ - if ( firstpage == 0 ) { - if (nr_pages != vm->pages.guest_n_pages) { - printk(KERN_WARNING "plex86: mmap of guest phy mem, " - "nr_pages of %u != guest_n_pages of %u\n", - nr_pages, vm->pages.guest_n_pages); - return -EINVAL; - } - /* printk(KERN_WARNING "plex86: found mmap of guest phy memory.\n"); */ - pagesArray = &vm->pages.guest[0]; - stateMask = VMStateMMapPhyMem; - } - else if ( firstpage == (vm->pages.guest_n_pages+0) ) { - if (nr_pages != 1) { - printk(KERN_WARNING "plex86: mmap of log_buffer, pages>1.\n"); - return -EINVAL; - } - /* printk(KERN_WARNING "plex86: found mmap of log_buffer.\n"); */ - pagesArray = &vm->pages.log_buffer[0]; - stateMask = VMStateMMapPrintBuffer; - } - else if ( firstpage == (vm->pages.guest_n_pages+1) ) { - if (nr_pages != 1) { - printk(KERN_WARNING "plex86: mmap of guest_cpu, pages>1.\n"); - return -EINVAL; - } - /* printk(KERN_WARNING "plex86: found mmap of guest_cpu.\n"); */ - pagesArray = &vm->pages.guest_cpu; - stateMask = VMStateMMapGuestCPU; - } - else { - printk(KERN_WARNING "plex86: mmap with firstpage of 0x%x.\n", firstpage); - return -EINVAL; - } + ret = genericMMap(vm, inode, file, vma, firstpage, pagesN); + return( - hostConvertPlex86Errno(ret) ); +} - /* Sanity check */ + + int +hostMMap(vm_t *vm, void *iV, void *fV, void *vmaV, + unsigned pagesN, Bit32u *pagesArray) +{ +#if LINUX_VERSION_CODE >= VERSION_CODE(2,1,0) + void *inode = NULL; +#else + struct inode * inode = (struct inode *) iV; +#endif + struct file * file = (struct file *) fV; + struct vm_area_struct * vma = (struct vm_area_struct *) vmaV; + unsigned i; + + UNUSED(file); + + /* Note: this function returns Plex86Errno style errors, since + * it reports to the hostOS-independent logic. + */ + + /* Sanity check. */ #if LINUX_VERSION_CODE >= VERSION_CODE(2,3,25) - if ( ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) > nr_pages ) { - printk(KERN_WARNING "plex86: mmap sanity checks failed.\n"); - return -EINVAL; - } + if ( ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) > pagesN ) { + printk(KERN_WARNING "plex86: mmap sanity checks failed.\n"); + return Plex86ErrnoEINVAL; + } #else - if ( (vma->vm_end - vma->vm_start) > (nr_pages << PAGE_SHIFT) ) { - printk(KERN_WARNING "plex86: mmap sanity checks failed.\n"); - return -EINVAL; - } + if ( (vma->vm_end - vma->vm_start) > (pagesN << PAGE_SHIFT) ) { + printk(KERN_WARNING "plex86: mmap sanity checks failed.\n"); + return Plex86ErrnoEINVAL; + } #endif - for ( i = 0; i < nr_pages; i++ ) { - if ( remap_page_range(vma->vm_start + (i << PAGE_SHIFT), - pagesArray[i] << 12, - PAGE_SIZE, - vma->vm_page_prot ) ) - /* xxx What about fixing partial remaps? */ - return -EAGAIN; - } + for (i = 0; i < pagesN; i++) { + if ( remap_page_range(vma->vm_start + (i << PAGE_SHIFT), + pagesArray[i] << 12, + PAGE_SIZE, + vma->vm_page_prot) ) + /* xxx What about fixing partial remaps? */ + return Plex86ErrnoEAGAIN; + } #if LINUX_VERSION_CODE < VERSION_CODE(2,1,0) - /* Enter our inode into the VMA; no need to change the default ops */ - vma->vm_inode = inode; - if (!inode->i_count) - inode->i_count++; + /* Enter our inode into the VMA; no need to change the default ops. */ + vma->vm_inode = inode; + if (!inode->i_count) + inode->i_count++; +#else + UNUSED(inode); #endif - vm->vmState |= stateMask; - return 0; + + return 0; /* OK. */ } @@ -480,235 +466,220 @@ plex86_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vm int plex86_read_procmem(char *buf, char **start, off_t offset, #if LINUX_VERSION_CODE >= VERSION_CODE(2,4,0) - int len + int len #else - int len, int unused + int len, int unused #endif - ) + ) { - unsigned i; - len = 0; - len += sprintf(buf, "monitor-->host interrupt reflection counts\n"); - for (i=0; i<256; i++) { + unsigned i; + len = 0; + len += sprintf(buf, "monitor-->host interrupt reflection counts\n"); + for (i=0; i<256; i++) { if (intRedirCount[i]) - len += sprintf(buf+len, " 0x%2x:%10u\n", i, intRedirCount[i]); + len += sprintf(buf+len, " 0x%2x:%10u\n", i, intRedirCount[i]); } - return(len); + return(len); } +#warning "Consolidate retrieve_XYZ() functions?" + int +retrieve_monitor_pages(void) +{ + /* + * Retrieve start address and size of this module. + * + * Note that with old kernels, we cannot access the module info (size), + * hence we rely on the fact that Linux lets at least one page of + * virtual address space unused after the end of the module. + */ +#ifdef THIS_MODULE + void *start_addr = THIS_MODULE; + unsigned size = THIS_MODULE->size; +#else + void *start_addr = &mod_use_count_; + unsigned size = 0x10000000; /* Actual size determined below */ +#endif + + int n_pages; + + n_pages = retrieve_vm_pages(monitor_pages.page, PLEX86_MAX_MONITOR_PAGES, + start_addr, size); + if (n_pages == 0) { + printk(KERN_ERR "plex86: retrieve_vm_pages returned error.\n"); + return( 0 ); /* Error. */ + } + printk(KERN_WARNING "plex86: %u monitor pages located\n", n_pages); + + monitor_pages.startOffset = (Bit32u)start_addr; + monitor_pages.startOffsetPageAligned = monitor_pages.startOffset & 0xfffff000; + monitor_pages.n_pages = n_pages; + return( n_pages ); +} + int retrieve_vm_pages(Bit32u *page, int max_pages, void *addr, unsigned size) { - /* - * Grrr. There doesn't seem to be an exported mechanism to retrieve - * the physical pages underlying a vmalloc()'ed area. We do it the - * hard way ... - */ - pageEntry_t *host_pgd; - Bit32u host_cr3; - Bit32u start_addr; - int n_pages; - int i; + /* + * Grrr. There doesn't seem to be an exported mechanism to retrieve + * the physical pages underlying a vmalloc()'ed area. We do it the + * hard way ... + */ + pageEntry_t *host_pgd; + Bit32u host_cr3; + Bit32u start_addr; + int n_pages; + int i; - start_addr = ((Bit32u)addr) & 0xfffff000; - n_pages = BytesToPages( (((Bit32u)addr) - start_addr) + size ); + start_addr = ((Bit32u)addr) & 0xfffff000; + n_pages = BytesToPages( (((Bit32u)addr) - start_addr) + size ); - if (!addr) { - printk(KERN_WARNING "plex86: retrieve_vm_pages: addr NULL!\n"); - return 0; - } - - if ( n_pages > max_pages ) { - printk(KERN_WARNING "plex86: retrieve_vm_pages: not enough pages!\n"); - printk(KERN_WARNING "plex86: npages(%u) > max_pages(%u)\n", - n_pages, max_pages); - return 0; - } - - asm volatile ("movl %%cr3, %0" : "=r" (host_cr3)); - host_pgd = (pageEntry_t *)(phys_to_virt(host_cr3 & ~0xfff)); - - for (i = 0; i < n_pages; i++) - { - Bit32u virt_addr = start_addr + i*PAGESIZE + KERNEL_OFFSET; - pageEntry_t *pde = host_pgd + (virt_addr >> 22); - pageEntry_t *pte = (pageEntry_t *)phys_to_virt(pde->fields.base << 12) - + ((virt_addr >> 12) & 0x3ff); - - /* If page isn't present, assume end of area */ - if ( !pde->fields.P || ! pte->fields.P ) - { - n_pages = i; - break; - } - - /* Abort if our page list is too small */ - if (i >= max_pages) - { - printk(KERN_WARNING "plex86: page list is too small!\n"); - printk(KERN_WARNING "plex86: n_pages=%u, max_pages=%u\n", - n_pages, max_pages); - return 0; - } - - page[i] = pte->fields.base; + if (!addr) { + printk(KERN_WARNING "plex86: retrieve_vm_pages: addr NULL!\n"); + return 0; } - return n_pages; -} + if ( n_pages > max_pages ) { + printk(KERN_WARNING "plex86: retrieve_vm_pages: not enough pages!\n"); + printk(KERN_WARNING "plex86: npages(%u) > max_pages(%u)\n", + n_pages, max_pages); + return 0; + } - int -retrieve_monitor_pages(void) -{ - /* - * Retrieve start address and size of this module. - * - * Note that with old kernels, we cannot access the module info (size), - * hence we rely on the fact that Linux lets at least one page of - * virtual address space unused after the end of the module. - */ -#ifdef THIS_MODULE - void *start_addr = THIS_MODULE; - unsigned size = THIS_MODULE->size; -#else - void *start_addr = &mod_use_count_; - unsigned size = 0x10000000; /* Actual size determined below */ -#endif + asm volatile ("movl %%cr3, %0" : "=r" (host_cr3)); + host_pgd = (pageEntry_t *)(phys_to_virt(host_cr3 & ~0xfff)); - int n_pages; + for (i = 0; i < n_pages; i++) { + Bit32u virt_addr = start_addr + i*PAGESIZE + KERNEL_OFFSET; + pageEntry_t *pde = host_pgd + (virt_addr >> 22); + pageEntry_t *pte = (pageEntry_t *)phys_to_virt(pde->fields.base << 12) + + ((virt_addr >> 12) & 0x3ff); - n_pages = retrieve_vm_pages(monitor_pages.page, PLEX86_MAX_MONITOR_PAGES, - start_addr, size); - if (n_pages == 0) { - printk(KERN_ERR "plex86: retrieve_vm_pages returned error.\n"); - return( 0 ); /* Error. */ + /* If page isn't present, assume end of area. */ + if ( !pde->fields.P || ! pte->fields.P ) { + n_pages = i; + break; + } + + /* Abort if our page list is too small */ + if (i >= max_pages) { + printk(KERN_WARNING "plex86: page list is too small!\n"); + printk(KERN_WARNING "plex86: n_pages=%u, max_pages=%u\n", + n_pages, max_pages); + return 0; } - printk(KERN_WARNING "plex86: %u monitor pages located\n", n_pages); - monitor_pages.startOffset = (Bit32u)start_addr; - monitor_pages.startOffsetPageAligned = - monitor_pages.startOffset & 0xfffff000; - monitor_pages.n_pages = n_pages; - return( n_pages ); + page[i] = pte->fields.base; + } + + return n_pages; } - void -hostReserveGuestPages(vm_t *vm) -{ - vm_pages_t *pg = &vm->pages; - unsigned p; - - /* - * As we want to map these pages to user space, we need to mark - * them as 'reserved' pages by setting the PG_reserved bit. - * - * This has the effect that: - * - remap_page_range accepts them as candidates for remapping - * - the swapper does *not* try to swap these pages out, even - * after they are mapped to user space - */ - -#if LINUX_VERSION_CODE >= VERSION_CODE(2,4,0) - for (p = 0; p < pg->guest_n_pages; p++) - set_bit(PG_reserved, &((mem_map + pg->guest[p])->flags)); - set_bit(PG_reserved, &((mem_map + pg->log_buffer[0])->flags)); - set_bit(PG_reserved, &((mem_map + pg->guest_cpu)->flags)); -#else - for (p = 0; p < pg->guest_n_pages; p++) - mem_map_reserve(pg->guest[p]); - mem_map_reserve(pg->log_buffer[0]); - mem_map_reserve(pg->guest_cpu); -#endif -} - - void -hostUnreserveGuestPages(vm_t *vm) -{ - vm_pages_t *pg = &vm->pages; - unsigned p; - - /* Remove the PG_reserved flags before returning the pages */ -#if LINUX_VERSION_CODE >= VERSION_CODE(2,4,0) - for (p = 0; p < pg->guest_n_pages; p++) - clear_bit(PG_reserved, &((mem_map + pg->guest[p])->flags)); - clear_bit(PG_reserved, &((mem_map + pg->log_buffer[0])->flags)); - clear_bit(PG_reserved, &((mem_map + pg->guest_cpu)->flags)); -#else - for (p = 0; p < pg->guest_n_pages; p++) - mem_map_unreserve(pg->guest[p]); - mem_map_unreserve(pg->log_buffer[0]); - mem_map_unreserve(pg->guest_cpu); -#endif -} - - unsigned retrieve_phy_pages(Bit32u *page, int max_pages, void *addr_v, unsigned size) { - /* - * Grrr. There doesn't seem to be an exported mechanism to retrieve - * the physical pages underlying a vmalloc()'ed area. We do it the - * hard way ... - */ - pageEntry_t *host_pgd; - Bit32u host_cr3; - /*Bit32u start_addr = (Bit32u)addr & ~(PAGESIZE-1); */ - /*int n_pages = ((Bit32u)addr + size - start_addr + PAGESIZE-1) >> 12; */ - int i; - Bit8u *addr; - unsigned n_pages; + /* + * Grrr. There doesn't seem to be an exported mechanism to retrieve + * the physical pages underlying a vmalloc()'ed area. We do it the + * hard way ... + */ + pageEntry_t *host_pgd; + Bit32u host_cr3; + /*Bit32u start_addr = (Bit32u)addr & ~(PAGESIZE-1); */ + /*int n_pages = ((Bit32u)addr + size - start_addr + PAGESIZE-1) >> 12; */ + int i; + Bit8u *addr; + unsigned n_pages; - addr = (Bit8u *) addr_v; - if ( ((Bit32u)addr) & 0xfff ) { - printk(KERN_ERR "plex86: retrieve_phy_pages: not aligned!\n"); + addr = (Bit8u *) addr_v; + if ( ((Bit32u)addr) & 0xfff ) { + printk(KERN_ERR "plex86: retrieve_phy_pages: not aligned!\n"); + return 0; + } + n_pages = BytesToPages(size); + if (!addr) { + printk(KERN_ERR "plex86: retrieve_phy_pages: addr NULL!\n"); + return 0; + } + + if ( n_pages > max_pages ) { + printk(KERN_ERR "plex86: retrieve_phy_pages: n=%u > max=%u\n", + n_pages, max_pages); + return 0; + } + + asm volatile ("movl %%cr3, %0" : "=r" (host_cr3)); + host_pgd = (pageEntry_t *)(phys_to_virt(host_cr3 & ~0xfff)); + + for (i = 0; i < n_pages; i++) { + Bit32u laddr; + pageEntry_t *pde; + pageEntry_t *pte; + + laddr = KERNEL_OFFSET + ((Bit32u) addr); + pde = host_pgd + (laddr >> 22); + pte = ((pageEntry_t *)phys_to_virt(pde->fields.base << 12)) + + ((laddr >> 12) & 0x3ff); + if ( !pde->fields.P ) { + printk(KERN_ERR "plex86: retrieve_phy_pages: " + "PDE.P==0: i=%u, n=%u laddr=0x%x\n", i, n_pages, laddr); return 0; } - n_pages = BytesToPages(size); - if (!addr) { - printk(KERN_ERR "plex86: retrieve_phy_pages: addr NULL!\n"); + if ( !pte->fields.P ) { + printk(KERN_ERR "plex86: retrieve_phy_pages: " + "PTE.P==0: i=%u, n=%u laddr=0x%x\n", i, n_pages, laddr); return 0; } - - if ( n_pages > max_pages ) { - printk(KERN_ERR "plex86: retrieve_phy_pages: n=%u > max=%u\n", - n_pages, max_pages); - return 0; - } - - asm volatile ("movl %%cr3, %0" : "=r" (host_cr3)); - host_pgd = (pageEntry_t *)(phys_to_virt(host_cr3 & ~0xfff)); - - for (i = 0; i < n_pages; i++) { - Bit32u laddr; - pageEntry_t *pde; - pageEntry_t *pte; - - laddr = KERNEL_OFFSET + ((Bit32u) addr); - pde = host_pgd + (laddr >> 22); - pte = ((pageEntry_t *)phys_to_virt(pde->fields.base << 12)) - + ((laddr >> 12) & 0x3ff); - if ( !pde->fields.P ) { - printk(KERN_ERR "plex86: retrieve_phy_pages: PDE.P==0: i=%u, n=%u laddr=0x%x\n", - i, n_pages, laddr); - return 0; - } - if ( !pte->fields.P ) { - printk(KERN_ERR "plex86: retrieve_phy_pages: PTE.P==0: i=%u, n=%u laddr=0x%x\n", - i, n_pages, laddr); - return 0; - } - page[i] = pte->fields.base; - addr += 4096; - } - return(n_pages); + page[i] = pte->fields.base; + addr += 4096; + } + return(n_pages); } -/************************************************************************/ -/* The requisite host-specific functions. */ -/************************************************************************/ +/************************************************************************ + * The requisite host-specific functions. An implementation of each of + * these functions needs to be offered for each host-XYZ.c file. + ************************************************************************/ + + void +hostReservePhyPages(vm_t *vm, Bit32u *hostPhyPages, unsigned nPages) +{ + unsigned p; + + /* + * As we want to map these pages to user space, we need to mark + * them as 'reserved' pages by setting the PG_reserved bit. + * + * This has the effect that: + * - remap_page_range accepts them as candidates for remapping + * - the swapper does *not* try to swap these pages out, even + * after they are mapped to user space + */ + + for (p = 0; p < nPages; p++) +#if LINUX_VERSION_CODE >= VERSION_CODE(2,4,0) + set_bit(PG_reserved, &((mem_map + hostPhyPages[p])->flags)); +#else + mem_map_reserve(hostPhyPages[p]); +#endif +} + + void +hostUnreservePhyPages(vm_t *vm, Bit32u *hostPhyPages, unsigned nPages) +{ + unsigned p; + + /* Remove the PG_reserved flags before returning the pages. */ + for (p = 0; p < nPages; p++) +#if LINUX_VERSION_CODE >= VERSION_CODE(2,4,0) + clear_bit(PG_reserved, &((mem_map + hostPhyPages[p])->flags)); +#else + mem_map_unreserve(hostPhyPages[p]); +#endif +} unsigned hostIdle(void) @@ -800,6 +771,8 @@ hostConvertPlex86Errno(unsigned ret) case Plex86ErrnoENOMEM: return(ENOMEM); case Plex86ErrnoEFAULT: return(EFAULT); case Plex86ErrnoEINVAL: return(EINVAL); + case Plex86ErrnoEACCES: return(EACCES); + case Plex86ErrnoEAGAIN: return(EAGAIN); default: printk(KERN_ERR "plex86: ioctlAllocVPhys: case %u\n", ret); return(EINVAL); diff --git a/bochs/plex86/kernel/host-null.c b/bochs/plex86/kernel/host-null.c index dfb56d369..eabbb879b 100644 --- a/bochs/plex86/kernel/host-null.c +++ b/bochs/plex86/kernel/host-null.c @@ -56,12 +56,12 @@ main(int argc, char *argv[]) } void -hostReserveGuestPages(vm_t *vm) +hostReservePhyPages(vm_t *vm, Bit32u *hostPhyPages, unsigned nPages) { } void -hostUnreserveGuestPages(vm_t *vm) +hostUnreservePhyPages(vm_t *vm, Bit32u *hostPhyPages, unsigned nPages) { } diff --git a/bochs/plex86/kernel/include/monitor.h b/bochs/plex86/kernel/include/monitor.h index e2e1dd98a..31183f87e 100644 --- a/bochs/plex86/kernel/include/monitor.h +++ b/bochs/plex86/kernel/include/monitor.h @@ -36,6 +36,11 @@ #include "eflags.h" #include "guest_context.h" +#ifndef UNUSED +# define UNUSED(x) ((void)(x)) +#endif + + /* Method1: push event info (CPU pushes error code before) */ typedef struct { @@ -481,6 +486,7 @@ typedef struct { extern monitor_pages_t monitor_pages; extern cpuid_info_t cpuid_info; +extern unsigned intRedirCount[]; @@ -566,6 +572,8 @@ vm_rdtsc(void) { #define Plex86ErrnoENOMEM 2 #define Plex86ErrnoEFAULT 3 #define Plex86ErrnoEINVAL 4 +#define Plex86ErrnoEACCES 5 +#define Plex86ErrnoEAGAIN 6 #define vm_save_flags(x) \ asm volatile("pushfl ; popl %0": "=g" (x): :"memory") @@ -588,6 +596,10 @@ int ioctlGeneric(vm_t *vm, void *inode, void *filp, int ioctlExecute(vm_t *vm, plex86IoctlExecute_t *executeMsg); unsigned ioctlAllocVPhys(vm_t *vm, unsigned long arg); void copyGuestStateToUserSpace(vm_t *vm); +void unreserveGuestPhyPages(vm_t *vm); +void reserveGuestPhyPages(vm_t *vm); +int genericMMap(vm_t *vm, void *inode, void *file, void *vma, + unsigned firstPage, unsigned pagesN); /* These are the functions that the host-OS-specific file of the * plex86 device driver must define. @@ -602,13 +614,15 @@ unsigned hostGetAllocedMemPhyPages(Bit32u *page, int max_pages, void *ptr, Bit32u hostGetAllocedPagePhyPage(void *ptr); void hostPrint(char *fmt, ...); Bit32u hostKernelOffset(void); -void hostReserveGuestPages(vm_t *vm); -void hostUnreserveGuestPages(vm_t *vm); +void hostReservePhyPages(vm_t *vm, Bit32u *hostPhyPages, unsigned nPages); +void hostUnreservePhyPages(vm_t *vm, Bit32u *hostPhyPages, unsigned nPages); int hostConvertPlex86Errno(unsigned ret); unsigned hostMMapCheck(void *i, void *f); void hostModuleCountReset(vm_t *vm, void *inode, void *filp); unsigned long hostCopyFromUser(void *to, void *from, unsigned long len); unsigned long hostCopyToUser(void *to, void *from, unsigned long len); +int hostMMap(vm_t *vm, void *iV, void *fV, void *vmaV, + unsigned pagesN, Bit32u *pagesArray); #endif /* HOST Space */ diff --git a/bochs/plex86/kernel/monitor-host.c b/bochs/plex86/kernel/monitor-host.c index 88ef3423b..8bd3c51d4 100644 --- a/bochs/plex86/kernel/monitor-host.c +++ b/bochs/plex86/kernel/monitor-host.c @@ -25,14 +25,15 @@ #define IN_HOST_SPACE #include "monitor.h" +monitor_pages_t monitor_pages; /* Instrumentation of how many hardware interrupts were redirected * to the host, while the VM was running. */ +#warning "Check for SMP issues on these globals" unsigned intRedirCount[256]; - - cpuid_info_t hostCpuIDInfo; + static int initIDTSlot(vm_t *vm, unsigned vec, int type); static void mapMonPages(vm_t *vm, Bit32u *, unsigned, Bit32u *, page_t *, unsigned user, unsigned writable, char *name); @@ -744,7 +745,8 @@ ioctlGeneric(vm_t *vm, void *inode, void *filp, */ vm->vmState &= ~VMStateMMapAll; - hostUnreserveGuestPages(vm); +#warning "Add check before calling unreserveGuestPhyPages()" + unreserveGuestPhyPages(vm); unallocVmPages(vm); /* Reset state to only FD opened. */ @@ -769,6 +771,7 @@ ioctlGeneric(vm_t *vm, void *inode, void *filp, return ret; } +#warning "PLEX86_RESET should only conditionally compiled for debugging." /* * For debugging, when the module gets hosed, this is a way * to reset the in-use count, so we can rmmod it. @@ -1262,11 +1265,11 @@ hostPrint("plex86: vm_t size is %u\n", sizeof(vm_t)); } /* Mark guest pages as reserved (for mmap()). */ - hostReserveGuestPages( vm ); + reserveGuestPhyPages(vm); /* Initialize the guests physical memory. */ if ( initGuestPhyMem(vm) ) { - hostUnreserveGuestPages(vm); + unreserveGuestPhyPages(vm); unallocVmPages(vm); return -Plex86ErrnoEFAULT; } @@ -1274,7 +1277,7 @@ hostPrint("plex86: vm_t size is %u\n", sizeof(vm_t)); /* Initialize the monitor. */ if ( !initMonitor(vm) || !mapMonitor(vm) ) { - hostUnreserveGuestPages(vm); + unreserveGuestPhyPages(vm); unallocVmPages(vm); return -Plex86ErrnoEFAULT; } @@ -1688,4 +1691,86 @@ initShadowPaging(vm_t *vm) xxxpanic(vm, "monPagingRemap: BadUsage4PDir\n"); } #endif +} + + void +reserveGuestPhyPages(vm_t *vm) +{ + /* Mark guest pages as reserved (for mmap()). */ + hostReservePhyPages(vm, vm->pages.guest, vm->pages.guest_n_pages); + hostReservePhyPages(vm, vm->pages.log_buffer, LOG_BUFF_PAGES); + hostReservePhyPages(vm, &vm->pages.guest_cpu, 1); +} + + void +unreserveGuestPhyPages(vm_t *vm) +{ + hostUnreservePhyPages(vm, vm->pages.guest, vm->pages.guest_n_pages); + hostUnreservePhyPages(vm, vm->pages.log_buffer, LOG_BUFF_PAGES); + hostUnreservePhyPages(vm, &vm->pages.guest_cpu, 1); +} + + int +genericMMap(vm_t *vm, void *inode, void *file, void *vma, unsigned firstPage, + unsigned pagesN) +{ + unsigned stateMask; + Bit32u *pagesArray; + int ret; + + /* The memory map: + * guest physical memory (guest_n_pages) + * log_buffer (1) + * guest_cpu (1) + */ + + /* Must have memory allocated. */ + if (!vm->pages.guest_n_pages) { + hostPrint("plex86: genericMMap: device not initialized\n"); + return Plex86ErrnoEACCES; + } + + if ( firstPage == 0 ) { + if (pagesN != vm->pages.guest_n_pages) { + hostPrint("plex86: mmap of guest phy mem, " + "pagesN of %u != guest_n_pages of %u\n", + pagesN, vm->pages.guest_n_pages); + return Plex86ErrnoEINVAL; + } + /* hostPrint("plex86: found mmap of guest phy memory.\n"); */ + pagesArray = &vm->pages.guest[0]; + stateMask = VMStateMMapPhyMem; + } + else if ( firstPage == (vm->pages.guest_n_pages+0) ) { + if (pagesN != 1) { + hostPrint("plex86: mmap of log_buffer, pages>1.\n"); + return Plex86ErrnoEINVAL; + } + /* hostPrint("plex86: found mmap of log_buffer.\n"); */ + pagesArray = &vm->pages.log_buffer[0]; + stateMask = VMStateMMapPrintBuffer; + } + else if ( firstPage == (vm->pages.guest_n_pages+1) ) { + if (pagesN != 1) { + hostPrint("plex86: mmap of guest_cpu, pages>1.\n"); + return Plex86ErrnoEINVAL; + } + /* hostPrint("plex86: found mmap of guest_cpu.\n"); */ + pagesArray = &vm->pages.guest_cpu; + stateMask = VMStateMMapGuestCPU; + } + else { + hostPrint("plex86: mmap with firstPage of 0x%x.\n", firstPage); + return Plex86ErrnoEINVAL; + } + + /* Call the hostOS-specific mmap code. */ + ret = hostMMap(vm, inode, file, vma, pagesN, pagesArray); + if (ret != 0) { + /* Host-specific mmap code returned an error. Return that. */ + return( ret ); + } + + vm->vmState |= stateMask; + return 0; /* OK. */ }