Skip to content

Commit 3ad0876

Browse files
Paul Durrantjgross1
authored andcommitted
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>
1 parent 4bf2cc9 commit 3ad0876

File tree

7 files changed

+291
-21
lines changed

7 files changed

+291
-21
lines changed

arch/arm/xen/enlighten.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,17 @@ int xen_unmap_domain_gfn_range(struct vm_area_struct *vma,
8989
}
9090
EXPORT_SYMBOL_GPL(xen_unmap_domain_gfn_range);
9191

92+
/* Not used by XENFEAT_auto_translated guests. */
93+
int 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+
}
101+
EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_array);
102+
92103
static void xen_read_wallclock(struct timespec64 *ts)
93104
{
94105
u32 version;

arch/x86/xen/mmu.c

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -65,37 +65,44 @@ static void xen_flush_tlb_all(void)
6565
#define REMAP_BATCH_SIZE 16
6666

6767
struct remap_data {
68-
xen_pfn_t *mfn;
68+
xen_pfn_t *pfn;
6969
bool contiguous;
70+
bool no_translate;
7071
pgprot_t prot;
7172
struct mmu_update *mmu_update;
7273
};
7374

74-
static int remap_area_mfn_pte_fn(pte_t *ptep, pgtable_t token,
75+
static int remap_area_pfn_pte_fn(pte_t *ptep, pgtable_t token,
7576
unsigned long addr, void *data)
7677
{
7778
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));
7980

80-
/* If we have a contiguous range, just update the mfn itself,
81-
else update pointer to be "next mfn". */
81+
/*
82+
* If we have a contiguous range, just update the pfn itself,
83+
* else update pointer to be "next pfn".
84+
*/
8285
if (rmd->contiguous)
83-
(*rmd->mfn)++;
86+
(*rmd->pfn)++;
8487
else
85-
rmd->mfn++;
88+
rmd->pfn++;
8689

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;
8894
rmd->mmu_update->val = pte_val_ma(pte);
8995
rmd->mmu_update++;
9096

9197
return 0;
9298
}
9399

94-
static int do_remap_gfn(struct vm_area_struct *vma,
100+
static int do_remap_pfn(struct vm_area_struct *vma,
95101
unsigned long addr,
96-
xen_pfn_t *gfn, int nr,
102+
xen_pfn_t *pfn, int nr,
97103
int *err_ptr, pgprot_t prot,
98-
unsigned domid,
104+
unsigned int domid,
105+
bool no_translate,
99106
struct page **pages)
100107
{
101108
int err = 0;
@@ -106,11 +113,14 @@ static int do_remap_gfn(struct vm_area_struct *vma,
106113

107114
BUG_ON(!((vma->vm_flags & (VM_PFNMAP | VM_IO)) == (VM_PFNMAP | VM_IO)));
108115

109-
rmd.mfn = gfn;
116+
rmd.pfn = pfn;
110117
rmd.prot = prot;
111-
/* We use the err_ptr to indicate if there we are doing a contiguous
112-
* mapping or a discontigious mapping. */
118+
/*
119+
* We use the err_ptr to indicate if there we are doing a contiguous
120+
* mapping or a discontigious mapping.
121+
*/
113122
rmd.contiguous = !err_ptr;
123+
rmd.no_translate = no_translate;
114124

115125
while (nr) {
116126
int index = 0;
@@ -121,7 +131,7 @@ static int do_remap_gfn(struct vm_area_struct *vma,
121131

122132
rmd.mmu_update = mmu_update;
123133
err = apply_to_page_range(vma->vm_mm, addr, range,
124-
remap_area_mfn_pte_fn, &rmd);
134+
remap_area_pfn_pte_fn, &rmd);
125135
if (err)
126136
goto out;
127137

@@ -175,7 +185,8 @@ int xen_remap_domain_gfn_range(struct vm_area_struct *vma,
175185
if (xen_feature(XENFEAT_auto_translated_physmap))
176186
return -EOPNOTSUPP;
177187

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);
179190
}
180191
EXPORT_SYMBOL_GPL(xen_remap_domain_gfn_range);
181192

@@ -194,10 +205,25 @@ int xen_remap_domain_gfn_array(struct vm_area_struct *vma,
194205
* cause of "wrong memory was mapped in".
195206
*/
196207
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);
198210
}
199211
EXPORT_SYMBOL_GPL(xen_remap_domain_gfn_array);
200212

213+
int 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+
}
225+
EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_array);
226+
201227
/* Returns: 0 success */
202228
int xen_unmap_domain_gfn_range(struct vm_area_struct *vma,
203229
int nr, struct page **pages)

drivers/xen/privcmd.c

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <xen/xen.h>
3434
#include <xen/privcmd.h>
3535
#include <xen/interface/xen.h>
36+
#include <xen/interface/memory.h>
3637
#include <xen/interface/hvm/dm_op.h>
3738
#include <xen/features.h>
3839
#include <xen/page.h>
@@ -722,6 +723,134 @@ static long privcmd_ioctl_restrict(struct file *file, void __user *udata)
722723
return 0;
723724
}
724725

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+
725854
static long privcmd_ioctl(struct file *file,
726855
unsigned int cmd, unsigned long data)
727856
{
@@ -753,6 +882,10 @@ static long privcmd_ioctl(struct file *file,
753882
ret = privcmd_ioctl_restrict(file, udata);
754883
break;
755884

885+
case IOCTL_PRIVCMD_MMAP_RESOURCE:
886+
ret = privcmd_ioctl_mmap_resource(file, udata);
887+
break;
888+
756889
default:
757890
break;
758891
}

include/uapi/xen/privcmd.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ struct privcmd_dm_op {
8989
const struct privcmd_dm_op_buf __user *ubufs;
9090
};
9191

92+
struct privcmd_mmap_resource {
93+
domid_t dom;
94+
__u32 type;
95+
__u32 id;
96+
__u32 idx;
97+
__u64 num;
98+
__u64 addr;
99+
};
100+
92101
/*
93102
* @cmd: IOCTL_PRIVCMD_HYPERCALL
94103
* @arg: &privcmd_hypercall_t
@@ -114,5 +123,7 @@ struct privcmd_dm_op {
114123
_IOC(_IOC_NONE, 'P', 5, sizeof(struct privcmd_dm_op))
115124
#define IOCTL_PRIVCMD_RESTRICT \
116125
_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))
117128

118129
#endif /* __LINUX_PUBLIC_PRIVCMD_H__ */

include/xen/interface/memory.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,4 +265,70 @@ struct xen_remove_from_physmap {
265265
};
266266
DEFINE_GUEST_HANDLE_STRUCT(xen_remove_from_physmap);
267267

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
273+
struct 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+
};
332+
DEFINE_GUEST_HANDLE_STRUCT(xen_mem_acquire_resource);
333+
268334
#endif /* __XEN_PUBLIC_MEMORY_H__ */

0 commit comments

Comments
 (0)