summaryrefslogtreecommitdiffstats
path: root/drivers/xen/privcmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/xen/privcmd.c')
-rw-r--r--drivers/xen/privcmd.c133
1 files changed, 133 insertions, 0 deletions
diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c
index 0a778d30d333..8ae0349d9f0a 100644
--- a/drivers/xen/privcmd.c
+++ b/drivers/xen/privcmd.c
@@ -33,6 +33,7 @@
33#include <xen/xen.h> 33#include <xen/xen.h>
34#include <xen/privcmd.h> 34#include <xen/privcmd.h>
35#include <xen/interface/xen.h> 35#include <xen/interface/xen.h>
36#include <xen/interface/memory.h>
36#include <xen/interface/hvm/dm_op.h> 37#include <xen/interface/hvm/dm_op.h>
37#include <xen/features.h> 38#include <xen/features.h>
38#include <xen/page.h> 39#include <xen/page.h>
@@ -722,6 +723,134 @@ static long privcmd_ioctl_restrict(struct file *file, void __user *udata)
722 return 0; 723 return 0;
723} 724}
724 725
726struct remap_pfn {
727 struct mm_struct *mm;
728 struct page **pages;
729 pgprot_t prot;
730 unsigned long i;
731};
732
733static int remap_pfn_fn(pte_t *ptep, pgtable_t token, unsigned long addr,
734 void *data)
735{
736 struct remap_pfn *r = data;
737 struct page *page = r->pages[r->i];
738 pte_t pte = pte_mkspecial(pfn_pte(page_to_pfn(page), r->prot));
739
740 set_pte_at(r->mm, addr, ptep, pte);
741 r->i++;
742
743 return 0;
744}
745
746static long privcmd_ioctl_mmap_resource(struct file *file, void __user *udata)
747{
748 struct privcmd_data *data = file->private_data;
749 struct mm_struct *mm = current->mm;
750 struct vm_area_struct *vma;
751 struct privcmd_mmap_resource kdata;
752 xen_pfn_t *pfns = NULL;
753 struct xen_mem_acquire_resource xdata;
754 int rc;
755
756 if (copy_from_user(&kdata, udata, sizeof(kdata)))
757 return -EFAULT;
758
759 /* If restriction is in place, check the domid matches */
760 if (data->domid != DOMID_INVALID && data->domid != kdata.dom)
761 return -EPERM;
762
763 down_write(&mm->mmap_sem);
764
765 vma = find_vma(mm, kdata.addr);
766 if (!vma || vma->vm_ops != &privcmd_vm_ops) {
767 rc = -EINVAL;
768 goto out;
769 }
770
771 pfns = kcalloc(kdata.num, sizeof(*pfns), GFP_KERNEL);
772 if (!pfns) {
773 rc = -ENOMEM;
774 goto out;
775 }
776
777 if (xen_feature(XENFEAT_auto_translated_physmap)) {
778 unsigned int nr = DIV_ROUND_UP(kdata.num, XEN_PFN_PER_PAGE);
779 struct page **pages;
780 unsigned int i;
781
782 rc = alloc_empty_pages(vma, nr);
783 if (rc < 0)
784 goto out;
785
786 pages = vma->vm_private_data;
787 for (i = 0; i < kdata.num; i++) {
788 xen_pfn_t pfn =
789 page_to_xen_pfn(pages[i / XEN_PFN_PER_PAGE]);
790
791 pfns[i] = pfn + (i % XEN_PFN_PER_PAGE);
792 }
793 } else
794 vma->vm_private_data = PRIV_VMA_LOCKED;
795
796 memset(&xdata, 0, sizeof(xdata));
797 xdata.domid = kdata.dom;
798 xdata.type = kdata.type;
799 xdata.id = kdata.id;
800 xdata.frame = kdata.idx;
801 xdata.nr_frames = kdata.num;
802 set_xen_guest_handle(xdata.frame_list, pfns);
803
804 xen_preemptible_hcall_begin();
805 rc = HYPERVISOR_memory_op(XENMEM_acquire_resource, &xdata);
806 xen_preemptible_hcall_end();
807
808 if (rc)
809 goto out;
810
811 if (xen_feature(XENFEAT_auto_translated_physmap)) {
812 struct remap_pfn r = {
813 .mm = vma->vm_mm,
814 .pages = vma->vm_private_data,
815 .prot = vma->vm_page_prot,
816 };
817
818 rc = apply_to_page_range(r.mm, kdata.addr,
819 kdata.num << PAGE_SHIFT,
820 remap_pfn_fn, &r);
821 } else {
822 unsigned int domid =
823 (xdata.flags & XENMEM_rsrc_acq_caller_owned) ?
824 DOMID_SELF : kdata.dom;
825 int num;
826
827 num = xen_remap_domain_mfn_array(vma,
828 kdata.addr & PAGE_MASK,
829 pfns, kdata.num, (int *)pfns,
830 vma->vm_page_prot,
831 domid,
832 vma->vm_private_data);
833 if (num < 0)
834 rc = num;
835 else if (num != kdata.num) {
836 unsigned int i;
837
838 for (i = 0; i < num; i++) {
839 rc = pfns[i];
840 if (rc < 0)
841 break;
842 }
843 } else
844 rc = 0;
845 }
846
847out:
848 up_write(&mm->mmap_sem);
849 kfree(pfns);
850
851 return rc;
852}
853
725static long privcmd_ioctl(struct file *file, 854static long privcmd_ioctl(struct file *file,
726 unsigned int cmd, unsigned long data) 855 unsigned int cmd, unsigned long data)
727{ 856{
@@ -753,6 +882,10 @@ static long privcmd_ioctl(struct file *file,
753 ret = privcmd_ioctl_restrict(file, udata); 882 ret = privcmd_ioctl_restrict(file, udata);
754 break; 883 break;
755 884
885 case IOCTL_PRIVCMD_MMAP_RESOURCE:
886 ret = privcmd_ioctl_mmap_resource(file, udata);
887 break;
888
756 default: 889 default:
757 break; 890 break;
758 } 891 }