diff options
Diffstat (limited to 'drivers/xen/privcmd.c')
-rw-r--r-- | drivers/xen/privcmd.c | 133 |
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 | ||
726 | struct remap_pfn { | ||
727 | struct mm_struct *mm; | ||
728 | struct page **pages; | ||
729 | pgprot_t prot; | ||
730 | unsigned long i; | ||
731 | }; | ||
732 | |||
733 | static 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 | |||
746 | static 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 | |||
847 | out: | ||
848 | up_write(&mm->mmap_sem); | ||
849 | kfree(pfns); | ||
850 | |||
851 | return rc; | ||
852 | } | ||
853 | |||
725 | static long privcmd_ioctl(struct file *file, | 854 | static 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 | } |