aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Durrant <paul.durrant@citrix.com>2018-05-09 09:16:12 -0400
committerJuergen Gross <jgross@suse.com>2018-05-14 09:25:37 -0400
commit3ad0876554cafa368f574d4d408468510543e9ff (patch)
tree7fade80924630151265270e68355d3f68b029bad
parent4bf2cc9645599382e34b7d0cbe5a13d0de98194e (diff)
xen/privcmd: add IOCTL_PRIVCMD_MMAP_RESOURCE
My recent Xen patch series introduces a new HYPERVISOR_memory_op to support direct priv-mapping of certain guest resources (such as ioreq pages, used by emulators) by a tools domain, rather than having to access such resources via the guest P2M. This patch adds the necessary infrastructure to the privcmd driver and Xen MMU code to support direct resource mapping. NOTE: The adjustment in the MMU code is partially cosmetic. Xen will now allow a PV tools domain to map guest pages either by GFN or MFN, thus the term 'mfn' has been swapped for 'pfn' in the lower layers of the remap code. Signed-off-by: Paul Durrant <paul.durrant@citrix.com> Reviewed-by: Boris Ostrovsky <boris.ostrovsky@oracle.com> Signed-off-by: Juergen Gross <jgross@suse.com>
-rw-r--r--arch/arm/xen/enlighten.c11
-rw-r--r--arch/x86/xen/mmu.c60
-rw-r--r--drivers/xen/privcmd.c133
-rw-r--r--include/uapi/xen/privcmd.h11
-rw-r--r--include/xen/interface/memory.h66
-rw-r--r--include/xen/interface/xen.h7
-rw-r--r--include/xen/xen-ops.h24
7 files changed, 291 insertions, 21 deletions
diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c
index ba7f4c8f5c3e..8073625371f5 100644
--- a/arch/arm/xen/enlighten.c
+++ b/arch/arm/xen/enlighten.c
@@ -89,6 +89,17 @@ int xen_unmap_domain_gfn_range(struct vm_area_struct *vma,
89} 89}
90EXPORT_SYMBOL_GPL(xen_unmap_domain_gfn_range); 90EXPORT_SYMBOL_GPL(xen_unmap_domain_gfn_range);
91 91
92/* Not used by XENFEAT_auto_translated guests. */
93int xen_remap_domain_mfn_array(struct vm_area_struct *vma,
94 unsigned long addr,
95 xen_pfn_t *mfn, int nr,
96 int *err_ptr, pgprot_t prot,
97 unsigned int domid, struct page **pages)
98{
99 return -ENOSYS;
100}
101EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_array);
102
92static void xen_read_wallclock(struct timespec64 *ts) 103static void xen_read_wallclock(struct timespec64 *ts)
93{ 104{
94 u32 version; 105 u32 version;
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index d33e7dbe3129..af2960cb7a3e 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -65,37 +65,44 @@ static void xen_flush_tlb_all(void)
65#define REMAP_BATCH_SIZE 16 65#define REMAP_BATCH_SIZE 16
66 66
67struct remap_data { 67struct remap_data {
68 xen_pfn_t *mfn; 68 xen_pfn_t *pfn;
69 bool contiguous; 69 bool contiguous;
70 bool no_translate;
70 pgprot_t prot; 71 pgprot_t prot;
71 struct mmu_update *mmu_update; 72 struct mmu_update *mmu_update;
72}; 73};
73 74
74static int remap_area_mfn_pte_fn(pte_t *ptep, pgtable_t token, 75static int remap_area_pfn_pte_fn(pte_t *ptep, pgtable_t token,
75 unsigned long addr, void *data) 76 unsigned long addr, void *data)
76{ 77{
77 struct remap_data *rmd = data; 78 struct remap_data *rmd = data;
78 pte_t pte = pte_mkspecial(mfn_pte(*rmd->mfn, rmd->prot)); 79 pte_t pte = pte_mkspecial(mfn_pte(*rmd->pfn, rmd->prot));
79 80
80 /* If we have a contiguous range, just update the mfn itself, 81 /*
81 else update pointer to be "next mfn". */ 82 * If we have a contiguous range, just update the pfn itself,
83 * else update pointer to be "next pfn".
84 */
82 if (rmd->contiguous) 85 if (rmd->contiguous)
83 (*rmd->mfn)++; 86 (*rmd->pfn)++;
84 else 87 else
85 rmd->mfn++; 88 rmd->pfn++;
86 89
87 rmd->mmu_update->ptr = virt_to_machine(ptep).maddr | MMU_NORMAL_PT_UPDATE; 90 rmd->mmu_update->ptr = virt_to_machine(ptep).maddr;
91 rmd->mmu_update->ptr |= rmd->no_translate ?
92 MMU_PT_UPDATE_NO_TRANSLATE :
93 MMU_NORMAL_PT_UPDATE;
88 rmd->mmu_update->val = pte_val_ma(pte); 94 rmd->mmu_update->val = pte_val_ma(pte);
89 rmd->mmu_update++; 95 rmd->mmu_update++;
90 96
91 return 0; 97 return 0;
92} 98}
93 99
94static int do_remap_gfn(struct vm_area_struct *vma, 100static int do_remap_pfn(struct vm_area_struct *vma,
95 unsigned long addr, 101 unsigned long addr,
96 xen_pfn_t *gfn, int nr, 102 xen_pfn_t *pfn, int nr,
97 int *err_ptr, pgprot_t prot, 103 int *err_ptr, pgprot_t prot,
98 unsigned domid, 104 unsigned int domid,
105 bool no_translate,
99 struct page **pages) 106 struct page **pages)
100{ 107{
101 int err = 0; 108 int err = 0;
@@ -106,11 +113,14 @@ static int do_remap_gfn(struct vm_area_struct *vma,
106 113
107 BUG_ON(!((vma->vm_flags & (VM_PFNMAP | VM_IO)) == (VM_PFNMAP | VM_IO))); 114 BUG_ON(!((vma->vm_flags & (VM_PFNMAP | VM_IO)) == (VM_PFNMAP | VM_IO)));
108 115
109 rmd.mfn = gfn; 116 rmd.pfn = pfn;
110 rmd.prot = prot; 117 rmd.prot = prot;
111 /* We use the err_ptr to indicate if there we are doing a contiguous 118 /*
112 * mapping or a discontigious mapping. */ 119 * We use the err_ptr to indicate if there we are doing a contiguous
120 * mapping or a discontigious mapping.
121 */
113 rmd.contiguous = !err_ptr; 122 rmd.contiguous = !err_ptr;
123 rmd.no_translate = no_translate;
114 124
115 while (nr) { 125 while (nr) {
116 int index = 0; 126 int index = 0;
@@ -121,7 +131,7 @@ static int do_remap_gfn(struct vm_area_struct *vma,
121 131
122 rmd.mmu_update = mmu_update; 132 rmd.mmu_update = mmu_update;
123 err = apply_to_page_range(vma->vm_mm, addr, range, 133 err = apply_to_page_range(vma->vm_mm, addr, range,
124 remap_area_mfn_pte_fn, &rmd); 134 remap_area_pfn_pte_fn, &rmd);
125 if (err) 135 if (err)
126 goto out; 136 goto out;
127 137
@@ -175,7 +185,8 @@ int xen_remap_domain_gfn_range(struct vm_area_struct *vma,
175 if (xen_feature(XENFEAT_auto_translated_physmap)) 185 if (xen_feature(XENFEAT_auto_translated_physmap))
176 return -EOPNOTSUPP; 186 return -EOPNOTSUPP;
177 187
178 return do_remap_gfn(vma, addr, &gfn, nr, NULL, prot, domid, pages); 188 return do_remap_pfn(vma, addr, &gfn, nr, NULL, prot, domid, false,
189 pages);
179} 190}
180EXPORT_SYMBOL_GPL(xen_remap_domain_gfn_range); 191EXPORT_SYMBOL_GPL(xen_remap_domain_gfn_range);
181 192
@@ -194,10 +205,25 @@ int xen_remap_domain_gfn_array(struct vm_area_struct *vma,
194 * cause of "wrong memory was mapped in". 205 * cause of "wrong memory was mapped in".
195 */ 206 */
196 BUG_ON(err_ptr == NULL); 207 BUG_ON(err_ptr == NULL);
197 return do_remap_gfn(vma, addr, gfn, nr, err_ptr, prot, domid, pages); 208 return do_remap_pfn(vma, addr, gfn, nr, err_ptr, prot, domid,
209 false, pages);
198} 210}
199EXPORT_SYMBOL_GPL(xen_remap_domain_gfn_array); 211EXPORT_SYMBOL_GPL(xen_remap_domain_gfn_array);
200 212
213int xen_remap_domain_mfn_array(struct vm_area_struct *vma,
214 unsigned long addr,
215 xen_pfn_t *mfn, int nr,
216 int *err_ptr, pgprot_t prot,
217 unsigned int domid, struct page **pages)
218{
219 if (xen_feature(XENFEAT_auto_translated_physmap))
220 return -EOPNOTSUPP;
221
222 return do_remap_pfn(vma, addr, mfn, nr, err_ptr, prot, domid,
223 true, pages);
224}
225EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_array);
226
201/* Returns: 0 success */ 227/* Returns: 0 success */
202int xen_unmap_domain_gfn_range(struct vm_area_struct *vma, 228int xen_unmap_domain_gfn_range(struct vm_area_struct *vma,
203 int nr, struct page **pages) 229 int nr, struct page **pages)
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 }
diff --git a/include/uapi/xen/privcmd.h b/include/uapi/xen/privcmd.h
index 39d3e7b8e993..d2029556083e 100644
--- a/include/uapi/xen/privcmd.h
+++ b/include/uapi/xen/privcmd.h
@@ -89,6 +89,15 @@ struct privcmd_dm_op {
89 const struct privcmd_dm_op_buf __user *ubufs; 89 const struct privcmd_dm_op_buf __user *ubufs;
90}; 90};
91 91
92struct privcmd_mmap_resource {
93 domid_t dom;
94 __u32 type;
95 __u32 id;
96 __u32 idx;
97 __u64 num;
98 __u64 addr;
99};
100
92/* 101/*
93 * @cmd: IOCTL_PRIVCMD_HYPERCALL 102 * @cmd: IOCTL_PRIVCMD_HYPERCALL
94 * @arg: &privcmd_hypercall_t 103 * @arg: &privcmd_hypercall_t
@@ -114,5 +123,7 @@ struct privcmd_dm_op {
114 _IOC(_IOC_NONE, 'P', 5, sizeof(struct privcmd_dm_op)) 123 _IOC(_IOC_NONE, 'P', 5, sizeof(struct privcmd_dm_op))
115#define IOCTL_PRIVCMD_RESTRICT \ 124#define IOCTL_PRIVCMD_RESTRICT \
116 _IOC(_IOC_NONE, 'P', 6, sizeof(domid_t)) 125 _IOC(_IOC_NONE, 'P', 6, sizeof(domid_t))
126#define IOCTL_PRIVCMD_MMAP_RESOURCE \
127 _IOC(_IOC_NONE, 'P', 7, sizeof(struct privcmd_mmap_resource))
117 128
118#endif /* __LINUX_PUBLIC_PRIVCMD_H__ */ 129#endif /* __LINUX_PUBLIC_PRIVCMD_H__ */
diff --git a/include/xen/interface/memory.h b/include/xen/interface/memory.h
index 583dd93b3016..4c5751c26f87 100644
--- a/include/xen/interface/memory.h
+++ b/include/xen/interface/memory.h
@@ -265,4 +265,70 @@ struct xen_remove_from_physmap {
265}; 265};
266DEFINE_GUEST_HANDLE_STRUCT(xen_remove_from_physmap); 266DEFINE_GUEST_HANDLE_STRUCT(xen_remove_from_physmap);
267 267
268/*
269 * Get the pages for a particular guest resource, so that they can be
270 * mapped directly by a tools domain.
271 */
272#define XENMEM_acquire_resource 28
273struct xen_mem_acquire_resource {
274 /* IN - The domain whose resource is to be mapped */
275 domid_t domid;
276 /* IN - the type of resource */
277 uint16_t type;
278
279#define XENMEM_resource_ioreq_server 0
280#define XENMEM_resource_grant_table 1
281
282 /*
283 * IN - a type-specific resource identifier, which must be zero
284 * unless stated otherwise.
285 *
286 * type == XENMEM_resource_ioreq_server -> id == ioreq server id
287 * type == XENMEM_resource_grant_table -> id defined below
288 */
289 uint32_t id;
290
291#define XENMEM_resource_grant_table_id_shared 0
292#define XENMEM_resource_grant_table_id_status 1
293
294 /* IN/OUT - As an IN parameter number of frames of the resource
295 * to be mapped. However, if the specified value is 0 and
296 * frame_list is NULL then this field will be set to the
297 * maximum value supported by the implementation on return.
298 */
299 uint32_t nr_frames;
300 /*
301 * OUT - Must be zero on entry. On return this may contain a bitwise
302 * OR of the following values.
303 */
304 uint32_t flags;
305
306 /* The resource pages have been assigned to the calling domain */
307#define _XENMEM_rsrc_acq_caller_owned 0
308#define XENMEM_rsrc_acq_caller_owned (1u << _XENMEM_rsrc_acq_caller_owned)
309
310 /*
311 * IN - the index of the initial frame to be mapped. This parameter
312 * is ignored if nr_frames is 0.
313 */
314 uint64_t frame;
315
316#define XENMEM_resource_ioreq_server_frame_bufioreq 0
317#define XENMEM_resource_ioreq_server_frame_ioreq(n) (1 + (n))
318
319 /*
320 * IN/OUT - If the tools domain is PV then, upon return, frame_list
321 * will be populated with the MFNs of the resource.
322 * If the tools domain is HVM then it is expected that, on
323 * entry, frame_list will be populated with a list of GFNs
324 * that will be mapped to the MFNs of the resource.
325 * If -EIO is returned then the frame_list has only been
326 * partially mapped and it is up to the caller to unmap all
327 * the GFNs.
328 * This parameter may be NULL if nr_frames is 0.
329 */
330 GUEST_HANDLE(xen_pfn_t) frame_list;
331};
332DEFINE_GUEST_HANDLE_STRUCT(xen_mem_acquire_resource);
333
268#endif /* __XEN_PUBLIC_MEMORY_H__ */ 334#endif /* __XEN_PUBLIC_MEMORY_H__ */
diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h
index 4f4830ef8f93..8bfb242f433e 100644
--- a/include/xen/interface/xen.h
+++ b/include/xen/interface/xen.h
@@ -265,9 +265,10 @@
265 * 265 *
266 * PAT (bit 7 on) --> PWT (bit 3 on) and clear bit 7. 266 * PAT (bit 7 on) --> PWT (bit 3 on) and clear bit 7.
267 */ 267 */
268#define MMU_NORMAL_PT_UPDATE 0 /* checked '*ptr = val'. ptr is MA. */ 268#define MMU_NORMAL_PT_UPDATE 0 /* checked '*ptr = val'. ptr is MA. */
269#define MMU_MACHPHYS_UPDATE 1 /* ptr = MA of frame to modify entry for */ 269#define MMU_MACHPHYS_UPDATE 1 /* ptr = MA of frame to modify entry for */
270#define MMU_PT_UPDATE_PRESERVE_AD 2 /* atomically: *ptr = val | (*ptr&(A|D)) */ 270#define MMU_PT_UPDATE_PRESERVE_AD 2 /* atomically: *ptr = val | (*ptr&(A|D)) */
271#define MMU_PT_UPDATE_NO_TRANSLATE 3 /* checked '*ptr = val'. ptr is MA. */
271 272
272/* 273/*
273 * MMU EXTENDED OPERATIONS 274 * MMU EXTENDED OPERATIONS
diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h
index fd23e42c6024..fd18c974a619 100644
--- a/include/xen/xen-ops.h
+++ b/include/xen/xen-ops.h
@@ -63,7 +63,7 @@ static inline void xen_destroy_contiguous_region(phys_addr_t pstart,
63struct vm_area_struct; 63struct vm_area_struct;
64 64
65/* 65/*
66 * xen_remap_domain_gfn_array() - map an array of foreign frames 66 * xen_remap_domain_gfn_array() - map an array of foreign frames by gfn
67 * @vma: VMA to map the pages into 67 * @vma: VMA to map the pages into
68 * @addr: Address at which to map the pages 68 * @addr: Address at which to map the pages
69 * @gfn: Array of GFNs to map 69 * @gfn: Array of GFNs to map
@@ -86,6 +86,28 @@ int xen_remap_domain_gfn_array(struct vm_area_struct *vma,
86 unsigned domid, 86 unsigned domid,
87 struct page **pages); 87 struct page **pages);
88 88
89/*
90 * xen_remap_domain_mfn_array() - map an array of foreign frames by mfn
91 * @vma: VMA to map the pages into
92 * @addr: Address at which to map the pages
93 * @mfn: Array of MFNs to map
94 * @nr: Number entries in the MFN array
95 * @err_ptr: Returns per-MFN error status.
96 * @prot: page protection mask
97 * @domid: Domain owning the pages
98 * @pages: Array of pages if this domain has an auto-translated physmap
99 *
100 * @mfn and @err_ptr may point to the same buffer, the MFNs will be
101 * overwritten by the error codes after they are mapped.
102 *
103 * Returns the number of successfully mapped frames, or a -ve error
104 * code.
105 */
106int xen_remap_domain_mfn_array(struct vm_area_struct *vma,
107 unsigned long addr, xen_pfn_t *mfn, int nr,
108 int *err_ptr, pgprot_t prot,
109 unsigned int domid, struct page **pages);
110
89/* xen_remap_domain_gfn_range() - map a range of foreign frames 111/* xen_remap_domain_gfn_range() - map a range of foreign frames
90 * @vma: VMA to map the pages into 112 * @vma: VMA to map the pages into
91 * @addr: Address at which to map the pages 113 * @addr: Address at which to map the pages