This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / xen / privcmd / privcmd.c
diff --git a/drivers/xen/privcmd/privcmd.c b/drivers/xen/privcmd/privcmd.c
new file mode 100644 (file)
index 0000000..0b78cc7
--- /dev/null
@@ -0,0 +1,232 @@
+/******************************************************************************
+ * privcmd.c
+ * 
+ * Interface to privileged domain-0 commands.
+ * 
+ * Copyright (c) 2002-2004, K A Fraser, B Dragovic
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/swap.h>
+#include <linux/smp_lock.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+#include <linux/seq_file.h>
+
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/tlb.h>
+#include <asm-xen/linux-public/privcmd.h>
+#include <asm-xen/xen-public/dom0_ops.h>
+#include <asm-xen/xen_proc.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define pud_t pgd_t
+#define pud_offset(d, va) d
+#endif
+
+static struct proc_dir_entry *privcmd_intf;
+
+static int privcmd_ioctl(struct inode *inode, struct file *file,
+                         unsigned int cmd, unsigned long data)
+{
+    int ret = -ENOSYS;
+
+    switch ( cmd )
+    {
+    case IOCTL_PRIVCMD_HYPERCALL:
+    {
+        privcmd_hypercall_t hypercall;
+  
+        if ( copy_from_user(&hypercall, (void *)data, sizeof(hypercall)) )
+            return -EFAULT;
+
+#if defined(__i386__)
+        __asm__ __volatile__ (
+            "pushl %%ebx; pushl %%ecx; pushl %%edx; pushl %%esi; pushl %%edi; "
+            "movl  4(%%eax),%%ebx ;"
+            "movl  8(%%eax),%%ecx ;"
+            "movl 12(%%eax),%%edx ;"
+            "movl 16(%%eax),%%esi ;"
+            "movl 20(%%eax),%%edi ;"
+            "movl   (%%eax),%%eax ;"
+            TRAP_INSTR "; "
+            "popl %%edi; popl %%esi; popl %%edx; popl %%ecx; popl %%ebx"
+            : "=a" (ret) : "0" (&hypercall) : "memory" );
+#elif defined (__x86_64__)
+       __asm__ __volatile__ (
+           "movq   %5,%%r10; movq %6,%%r8;" TRAP_INSTR
+           : "=a" (ret)
+           : "a" ((unsigned long)hypercall.op), 
+             "D" ((unsigned long)hypercall.arg[0]), 
+             "S" ((unsigned long)hypercall.arg[1]),
+             "d" ((unsigned long)hypercall.arg[2]), 
+             "g" ((unsigned long)hypercall.arg[3]),
+             "g" ((unsigned long)hypercall.arg[4])
+           : "r11","rcx","r8","r10","memory");
+#endif
+    }
+    break;
+
+    case IOCTL_PRIVCMD_INITDOMAIN_EVTCHN:
+    {
+        extern int initdom_ctrlif_domcontroller_port;
+        ret = initdom_ctrlif_domcontroller_port;
+    }
+    break;
+    
+#if defined(CONFIG_XEN_PRIVILEGED_GUEST)
+    case IOCTL_PRIVCMD_MMAP:
+    {
+#define PRIVCMD_MMAP_SZ 32
+        privcmd_mmap_t mmapcmd;
+        privcmd_mmap_entry_t msg[PRIVCMD_MMAP_SZ], *p;
+        int i, rc;
+
+        if ( copy_from_user(&mmapcmd, (void *)data, sizeof(mmapcmd)) )
+            return -EFAULT;
+
+        p = mmapcmd.entry;
+
+        for (i=0; i<mmapcmd.num; i+=PRIVCMD_MMAP_SZ, p+=PRIVCMD_MMAP_SZ)
+        {
+            int j, n = ((mmapcmd.num-i)>PRIVCMD_MMAP_SZ)?
+                PRIVCMD_MMAP_SZ:(mmapcmd.num-i);
+
+
+            if ( copy_from_user(&msg, p, n*sizeof(privcmd_mmap_entry_t)) )
+                return -EFAULT;
+     
+            for ( j = 0; j < n; j++ )
+            {
+                struct vm_area_struct *vma = 
+                    find_vma( current->mm, msg[j].va );
+
+                if ( !vma )
+                    return -EINVAL;
+
+                if ( msg[j].va > PAGE_OFFSET )
+                    return -EINVAL;
+
+                if ( (msg[j].va + (msg[j].npages<<PAGE_SHIFT)) > vma->vm_end )
+                    return -EINVAL;
+
+                if ( (rc = direct_remap_area_pages(vma->vm_mm, 
+                                                   msg[j].va&PAGE_MASK, 
+                                                   msg[j].mfn<<PAGE_SHIFT, 
+                                                   msg[j].npages<<PAGE_SHIFT, 
+                                                   vma->vm_page_prot,
+                                                   mmapcmd.dom)) < 0 )
+                    return rc;
+            }
+        }
+        ret = 0;
+    }
+    break;
+
+    case IOCTL_PRIVCMD_MMAPBATCH:
+    {
+        mmu_update_t u;
+        privcmd_mmapbatch_t m;
+        struct vm_area_struct *vma = NULL;
+        unsigned long *p, addr;
+        unsigned long mfn;
+        int i;
+
+        if ( copy_from_user(&m, (void *)data, sizeof(m)) )
+        { ret = -EFAULT; goto batch_err; }
+
+        vma = find_vma( current->mm, m.addr );
+
+        if ( !vma )
+        { ret = -EINVAL; goto batch_err; }
+
+        if ( m.addr > PAGE_OFFSET )
+        { ret = -EFAULT; goto batch_err; }
+
+        if ( (m.addr + (m.num<<PAGE_SHIFT)) > vma->vm_end )
+        { ret = -EFAULT; goto batch_err; }
+
+        p = m.arr;
+        addr = m.addr;
+        for ( i = 0; i < m.num; i++, addr += PAGE_SIZE, p++ )
+        {
+            if ( get_user(mfn, p) )
+                return -EFAULT;
+
+            u.val = (mfn << PAGE_SHIFT) | pgprot_val(vma->vm_page_prot);
+
+            __direct_remap_area_pages(vma->vm_mm,
+                                      addr, 
+                                      PAGE_SIZE, 
+                                      &u);
+
+            if ( unlikely(HYPERVISOR_mmu_update(&u, 1, NULL, m.dom) < 0) )
+                put_user(0xF0000000 | mfn, p);
+        }
+
+        ret = 0;
+        break;
+
+    batch_err:
+        printk("batch_err ret=%d vma=%p addr=%lx num=%d arr=%p %lx-%lx\n", 
+               ret, vma, m.addr, m.num, m.arr,
+               vma ? vma->vm_start : 0, vma ? vma->vm_end : 0);
+        break;
+    }
+    break;
+#endif
+
+    case IOCTL_PRIVCMD_GET_MACH2PHYS_START_MFN:
+    {
+        unsigned long m2pv = (unsigned long)machine_to_phys_mapping;
+        pgd_t *pgd = pgd_offset_k(m2pv);
+        pud_t *pud = pud_offset(pgd, m2pv);
+        pmd_t *pmd = pmd_offset(pud, m2pv);
+        unsigned long m2p_start_mfn = (*(unsigned long *)pmd) >> PAGE_SHIFT; 
+        ret = put_user(m2p_start_mfn, (unsigned long *)data) ? -EFAULT: 0;
+    }
+    break;
+
+    default:
+        ret = -EINVAL;
+        break;
+    }
+    return ret;
+}
+
+static int privcmd_mmap(struct file * file, struct vm_area_struct * vma)
+{
+    /* DONTCOPY is essential for Xen as copy_page_range is broken. */
+    vma->vm_flags |= VM_RESERVED | VM_IO | VM_DONTCOPY;
+
+    return 0;
+}
+
+static struct file_operations privcmd_file_ops = {
+    ioctl : privcmd_ioctl,
+    mmap:   privcmd_mmap
+};
+
+
+static int __init privcmd_init(void)
+{
+    if ( !(xen_start_info.flags & SIF_PRIVILEGED) )
+        return 0;
+
+    privcmd_intf = create_xen_proc_entry("privcmd", 0400);
+    if ( privcmd_intf != NULL )
+        privcmd_intf->proc_fops = &privcmd_file_ops;
+
+    return 0;
+}
+
+__initcall(privcmd_init);