0768d01522
new experimental stripped-down version of plex86, which is now a user-code-only VM. I ripped out all the fancy stuff in plex86, such that under that right conditions, user-code (protection level 3) can run at near native speeds inside the plex86 VM. The general idea is that bochs emulates all the initial real-mode code, and guest kernel code (protection level 0). When it senses the right conditions (like the context switches to user-code), a shim is called to execute the guest inside the plex86 VM. All guest-generated faults/exceptions are then forwarded back to bochs to be handled in the emulator. Actually, I'm not yet adding the mods to the bochs code (other than the shim code which is in a separate file), until I hear that we're back in a more development mode with bochs after the 2.0 release. The plex86 subdirectory is really a separate project. It's just more convenient to co-develop it with bochs for now. Both projects are currently LGPL, but each should be taken to be a separate project, and have their own license file. Plex86 (it's only a kernel driver now) could ultimately be used with other projects, as it's modular. I talked with Bryce, and we both agreed it's OK to keep plex86 as a subdir in bochs for now.
855 lines
23 KiB
C
855 lines
23 KiB
C
/*
|
|
* plex86: run multiple x86 operating systems concurrently
|
|
* Copyright (C) 1999-2003 Kevin P. Lawton
|
|
*
|
|
* host-linux.c: Linux specific VM host driver functionality
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "plex86.h"
|
|
#define IN_HOST_SPACE
|
|
#include "monitor.h"
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/module.h>
|
|
#include <linux/types.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/wrapper.h>
|
|
#include <linux/version.h>
|
|
#include <asm/irq.h>
|
|
|
|
|
|
#ifndef VERSION_CODE
|
|
# define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) )
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= VERSION_CODE(2,1,0)
|
|
# include <asm/uaccess.h>
|
|
#endif
|
|
|
|
#include <asm/io.h>
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* Compatibility macros for older kernels */
|
|
/************************************************************************/
|
|
|
|
#ifndef EXPORT_NO_SYMBOLS
|
|
# define EXPORT_NO_SYMBOLS register_symtab(NULL)
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= VERSION_CODE(2,1,29)
|
|
# define proc_register_dynamic proc_register
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE < VERSION_CODE(2,2,0)
|
|
#define NEED_RESCHED need_resched
|
|
#else
|
|
#define NEED_RESCHED current->need_resched
|
|
#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)
|
|
{
|
|
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)
|
|
{
|
|
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 ... */
|
|
#define THIS_MODULE (&__this_module)
|
|
#endif
|
|
|
|
|
|
/************************************************************************/
|
|
/* Declarations */
|
|
/************************************************************************/
|
|
|
|
/* Use dynamic major number allocation. (Set non-zero for static allocation) */
|
|
#define PLEX86_MAJOR 0
|
|
static int plex_major = PLEX86_MAJOR;
|
|
#if LINUX_VERSION_CODE >= VERSION_CODE(2,1,18)
|
|
MODULE_PARM(plex_major, "i");
|
|
MODULE_PARM_DESC(plex_major, "major number (default " __MODULE_STRING(PLEX86_MAJOR) ")");
|
|
#endif
|
|
|
|
/* The kernel segment base */
|
|
#if LINUX_VERSION_CODE < VERSION_CODE(2,1,0)
|
|
# define KERNEL_OFFSET 0xc0000000
|
|
#else
|
|
# define KERNEL_OFFSET 0x00000000
|
|
#endif
|
|
|
|
|
|
/* 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 *);
|
|
#else
|
|
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);
|
|
#else
|
|
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 */
|
|
#ifdef MODULE_LICENSE
|
|
MODULE_LICENSE("GPL"); /* Close enough. */
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* 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_monitor_pages(void);
|
|
|
|
monitor_pages_t monitor_pages;
|
|
extern unsigned intRedirCount[];
|
|
|
|
|
|
static struct file_operations plex86_fops = {
|
|
#if LINUX_VERSION_CODE >= VERSION_CODE(2,4,0)
|
|
owner: THIS_MODULE,
|
|
#endif
|
|
mmap: plex86_mmap,
|
|
ioctl: plex86_ioctl,
|
|
open: plex86_open,
|
|
release: plex86_release,
|
|
};
|
|
|
|
|
|
#ifdef CONFIG_DEVFS_FS
|
|
#include <linux/devfs_fs_kernel.h>
|
|
devfs_handle_t my_devfs_entry;
|
|
#endif
|
|
|
|
/* For the /proc/driver/plex86 entry. */
|
|
#if LINUX_VERSION_CODE >= VERSION_CODE(2,4,0) /* XXX - How far back? */
|
|
int plex86_read_procmem(char *, char **, off_t, int);
|
|
#else
|
|
int plex86_read_procmem(char *, char **, off_t, int, int);
|
|
#endif
|
|
|
|
#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 */
|
|
};
|
|
#endif
|
|
|
|
|
|
/************************************************************************/
|
|
/* Main kernel module code */
|
|
/************************************************************************/
|
|
|
|
int
|
|
init_module(void)
|
|
{
|
|
int err;
|
|
|
|
/* 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 /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");
|
|
#else
|
|
proc_register_dynamic(&proc_root, &plex86_proc_entry);
|
|
#endif
|
|
#endif
|
|
|
|
/* 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");
|
|
#endif
|
|
|
|
/* retrieve the monitor physical pages */
|
|
if (!retrieve_monitor_pages()) {
|
|
printk(KERN_ERR "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 "genericModuleInit returned error\n");
|
|
err = -EINVAL;
|
|
goto fail_cpu_capabilities;
|
|
}
|
|
|
|
/* success */
|
|
EXPORT_NO_SYMBOLS;
|
|
return(0);
|
|
|
|
fail_cpu_capabilities:
|
|
fail_retrieve_pages:
|
|
/* unregister /proc entry */
|
|
#ifdef CONFIG_PROC_FS
|
|
#if LINUX_VERSION_CODE >= VERSION_CODE(2,3,25)
|
|
remove_proc_entry("driver/plex86", NULL);
|
|
#else
|
|
proc_unregister(&proc_root, plex86_proc_entry.low_ino);
|
|
#endif
|
|
#endif
|
|
|
|
/* unregister device */
|
|
unregister_chrdev(plex_major, "plex86");
|
|
return err;
|
|
}
|
|
|
|
void
|
|
cleanup_module(void)
|
|
{
|
|
/* unregister device */
|
|
unregister_chrdev(plex_major, "plex86");
|
|
|
|
/* unregister /proc entry */
|
|
#ifdef CONFIG_PROC_FS
|
|
#if LINUX_VERSION_CODE >= VERSION_CODE(2,3,25)
|
|
remove_proc_entry("driver/plex86", NULL);
|
|
#else
|
|
proc_unregister(&proc_root, plex86_proc_entry.low_ino);
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CONFIG_DEVFS_FS
|
|
devfs_unregister(my_devfs_entry);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* Open / Release a VM */
|
|
/************************************************************************/
|
|
|
|
int
|
|
plex86_open(struct inode *inode, struct file *filp)
|
|
{
|
|
vm_t *vm;
|
|
#if LINUX_VERSION_CODE < VERSION_CODE(2,4,0)
|
|
MOD_INC_USE_COUNT;
|
|
#endif
|
|
|
|
/* Allocate a VM structure. */
|
|
if ( (vm = vmalloc(sizeof(vm_t))) == NULL )
|
|
return -ENOMEM;
|
|
filp->private_data = vm;
|
|
|
|
/* Kernel independent device open code. */
|
|
genericDeviceOpen(vm);
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
#if LINUX_VERSION_CODE >= VERSION_CODE(2,1,31)
|
|
int
|
|
#else
|
|
void
|
|
#endif
|
|
plex86_release(struct inode *inode, struct file *filp)
|
|
{
|
|
vm_t *vm = (vm_t *)filp->private_data;
|
|
filp->private_data = NULL;
|
|
|
|
/* Free the virtual memory. */
|
|
unreserve_guest_pages( vm );
|
|
unallocVmPages( 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;
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= VERSION_CODE(2,1,31)
|
|
return(0);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
int
|
|
plex86_ioctl(struct inode *inode, struct file *filp,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
vm_t *vm = (vm_t *)filp->private_data;
|
|
int ret;
|
|
|
|
/* Call non host-specific ioctl() code which calls back to this
|
|
* module only when it needs host-specific features.
|
|
*/
|
|
ret = ioctlGeneric(vm, inode, filp, cmd, arg);
|
|
|
|
/* Convert from plex86 errno codes to host-specific errno codes. Not
|
|
* very exciting.
|
|
*/
|
|
if ( ret < 0 )
|
|
ret = - hostConvertPlex86Errno(- ret);
|
|
return( ret );
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
#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;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Map all requested pages in ... */
|
|
#if LINUX_VERSION_CODE >= VERSION_CODE(2,3,25)
|
|
firstpage = vma->vm_pgoff;
|
|
#else
|
|
firstpage = vma->vm_offset >> PAGE_SHIFT;
|
|
#endif
|
|
nr_pages = (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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
#else
|
|
if ( (vma->vm_end - vma->vm_start) > (nr_pages << PAGE_SHIFT) ) {
|
|
printk(KERN_WARNING "plex86: mmap sanity checks failed.\n");
|
|
return -EINVAL;
|
|
}
|
|
#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;
|
|
}
|
|
|
|
#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++;
|
|
#endif
|
|
vm->vmState |= stateMask;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* Status reporting: /proc code */
|
|
/************************************************************************/
|
|
|
|
int
|
|
plex86_read_procmem(char *buf, char **start, off_t offset,
|
|
#if LINUX_VERSION_CODE >= VERSION_CODE(2,4,0)
|
|
int len
|
|
#else
|
|
int len, int unused
|
|
#endif
|
|
)
|
|
{
|
|
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]);
|
|
}
|
|
return(len);
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
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 " 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 "n_pages=%u, max_pages=%u\n",
|
|
n_pages, max_pages);
|
|
return 0;
|
|
}
|
|
|
|
page[i] = pte->fields.base;
|
|
}
|
|
|
|
return n_pages;
|
|
}
|
|
|
|
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 "retrieve_vm_pages returned error.\n");
|
|
return( 0 ); /* Error. */
|
|
}
|
|
printk(KERN_WARNING "%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 );
|
|
}
|
|
|
|
void
|
|
reserve_guest_pages(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
|
|
unreserve_guest_pages(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;
|
|
|
|
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 "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 "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);
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* The requisite host-specific functions. */
|
|
/************************************************************************/
|
|
|
|
unsigned
|
|
hostIdle(void)
|
|
{
|
|
if (NEED_RESCHED)
|
|
schedule();
|
|
|
|
/* return !current_got_fatal_signal(); */
|
|
return( ! signal_pending(current) );
|
|
}
|
|
|
|
void *
|
|
hostAllocZeroedMem(unsigned long size)
|
|
{
|
|
void *ptr;
|
|
|
|
ptr = vmalloc(size);
|
|
if ( ((Bit32u) ptr) & 0x00000fff )
|
|
return( 0 ); /* Error. */
|
|
|
|
/* Zero pages. This also demand maps the pages in, which we need
|
|
* since we'll cycle through all the pages to get the physical
|
|
* address mappings.
|
|
*/
|
|
mon_memzero(ptr, size);
|
|
return( ptr );
|
|
}
|
|
|
|
void
|
|
hostFreeMem(void *ptr)
|
|
{
|
|
vfree(ptr);
|
|
}
|
|
|
|
void *
|
|
hostAllocZeroedPage(void)
|
|
{
|
|
return( (void *) get_zeroed_page(GFP_KERNEL) );
|
|
}
|
|
|
|
void
|
|
hostFreePage(void *ptr)
|
|
{
|
|
free_page( (Bit32u)ptr );
|
|
}
|
|
|
|
|
|
unsigned
|
|
hostGetAllocedMemPhyPages(Bit32u *page, int max_pages, void *ptr, unsigned size)
|
|
{
|
|
return( retrieve_phy_pages(page, max_pages, ptr, size) );
|
|
}
|
|
|
|
Bit32u
|
|
hostGetAllocedPagePhyPage(void *ptr)
|
|
{
|
|
if (!ptr) return 0;
|
|
/* return MAP_NR(ptr); */
|
|
return(__pa(ptr) >> PAGE_SHIFT);
|
|
}
|
|
|
|
void
|
|
hostPrint(char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
int ret;
|
|
unsigned char buffer[256];
|
|
|
|
va_start(args, fmt);
|
|
ret = mon_vsnprintf(buffer, 256, fmt, args);
|
|
if (ret == -1) {
|
|
printk(KERN_ERR "hostPrint: vsnprintf returns error.\n");
|
|
}
|
|
else {
|
|
printk(KERN_WARNING "%s\n", buffer);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
hostConvertPlex86Errno(unsigned ret)
|
|
{
|
|
switch (ret) {
|
|
case 0: return(0);
|
|
case Plex86ErrnoEBUSY: return(EBUSY);
|
|
case Plex86ErrnoENOMEM: return(ENOMEM);
|
|
case Plex86ErrnoEFAULT: return(EFAULT);
|
|
case Plex86ErrnoEINVAL: return(EINVAL);
|
|
default:
|
|
printk(KERN_ERR "ioctlAllocVPhys: case %u\n", ret);
|
|
return(EINVAL);
|
|
}
|
|
}
|
|
|
|
|
|
Bit32u
|
|
hostKernelOffset(void)
|
|
{
|
|
return( KERNEL_OFFSET );
|
|
}
|
|
|
|
unsigned
|
|
hostMMapCheck(void *i, void *f)
|
|
{
|
|
struct inode *inode;
|
|
struct file *filp;
|
|
|
|
inode = (struct inode *) i;
|
|
filp = (struct file *) f;
|
|
|
|
#if LINUX_VERSION_CODE >= VERSION_CODE(2,3,99)
|
|
/* Not sure when this changed. If you know, email us. */
|
|
if (inode->i_data.i_mmap != NULL)
|
|
#else
|
|
if (inode->i_mmap != NULL)
|
|
#endif
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
hostModuleCountReset(vm_t *vm, void *inode, void *filp)
|
|
{
|
|
#if LINUX_VERSION_CODE < VERSION_CODE(2,4,0)
|
|
while (MOD_IN_USE) {
|
|
MOD_DEC_USE_COUNT;
|
|
}
|
|
|
|
MOD_INC_USE_COUNT; /* bump back to 1 so release can decrement */
|
|
#endif
|
|
}
|
|
|
|
unsigned long
|
|
hostCopyFromUser(void *to, void *from, unsigned long len)
|
|
{
|
|
return( copy_from_user(to, from, len) );
|
|
}
|
|
|
|
unsigned long
|
|
hostCopyToUser(void *to, void *from, unsigned long len)
|
|
{
|
|
return( copy_to_user(to, from, len) );
|
|
}
|