in privcmd.c [720:833]
static long privcmd_ioctl_mmap_resource(struct file *file,
struct privcmd_mmap_resource __user *udata)
{
struct privcmd_data *data = file->private_data;
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
struct privcmd_mmap_resource kdata;
xen_pfn_t *pfns = NULL;
struct xen_mem_acquire_resource xdata = { };
int rc;
if (copy_from_user(&kdata, udata, sizeof(kdata)))
return -EFAULT;
/* If restriction is in place, check the domid matches */
if (data->domid != DOMID_INVALID && data->domid != kdata.dom)
return -EPERM;
/* Both fields must be set or unset */
if (!!kdata.addr != !!kdata.num)
return -EINVAL;
xdata.domid = kdata.dom;
xdata.type = kdata.type;
xdata.id = kdata.id;
if (!kdata.addr && !kdata.num) {
/* Query the size of the resource. */
rc = HYPERVISOR_memory_op(XENMEM_acquire_resource, &xdata);
if (rc)
return rc;
return __put_user(xdata.nr_frames, &udata->num);
}
mmap_write_lock(mm);
vma = find_vma(mm, kdata.addr);
if (!vma || vma->vm_ops != &privcmd_vm_ops) {
rc = -EINVAL;
goto out;
}
pfns = kcalloc(kdata.num, sizeof(*pfns), GFP_KERNEL);
if (!pfns) {
rc = -ENOMEM;
goto out;
}
if (IS_ENABLED(CONFIG_XEN_AUTO_XLATE) &&
xen_feature(XENFEAT_auto_translated_physmap)) {
unsigned int nr = DIV_ROUND_UP(kdata.num, XEN_PFN_PER_PAGE);
struct page **pages;
unsigned int i;
rc = alloc_empty_pages(vma, nr);
if (rc < 0)
goto out;
pages = vma->vm_private_data;
for (i = 0; i < kdata.num; i++) {
xen_pfn_t pfn =
page_to_xen_pfn(pages[i / XEN_PFN_PER_PAGE]);
pfns[i] = pfn + (i % XEN_PFN_PER_PAGE);
}
} else
vma->vm_private_data = PRIV_VMA_LOCKED;
xdata.frame = kdata.idx;
xdata.nr_frames = kdata.num;
set_xen_guest_handle(xdata.frame_list, pfns);
xen_preemptible_hcall_begin();
rc = HYPERVISOR_memory_op(XENMEM_acquire_resource, &xdata);
xen_preemptible_hcall_end();
if (rc)
goto out;
if (IS_ENABLED(CONFIG_XEN_AUTO_XLATE) &&
xen_feature(XENFEAT_auto_translated_physmap)) {
rc = xen_remap_vma_range(vma, kdata.addr, kdata.num << PAGE_SHIFT);
} else {
unsigned int domid =
(xdata.flags & XENMEM_rsrc_acq_caller_owned) ?
DOMID_SELF : kdata.dom;
int num, *errs = (int *)pfns;
BUILD_BUG_ON(sizeof(*errs) > sizeof(*pfns));
num = xen_remap_domain_mfn_array(vma,
kdata.addr & PAGE_MASK,
pfns, kdata.num, errs,
vma->vm_page_prot,
domid);
if (num < 0)
rc = num;
else if (num != kdata.num) {
unsigned int i;
for (i = 0; i < num; i++) {
rc = errs[i];
if (rc < 0)
break;
}
} else
rc = 0;
}
out:
mmap_write_unlock(mm);
kfree(pfns);
return rc;
}