// SPDX-License-Identifier: GPL-2.0 OR MIT /****************************************************************************** * privcmd-buf.c * * Mmap of hypercall buffers. * * Copyright (c) 2018 Juergen Gross */ #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt #include <linux/kernel.h>#include <linux/module.h>#include <linux/list.h>#include <linux/miscdevice.h>#include <linux/mm.h>#include <linux/slab.h> #include "privcmd.h" MODULE_DESCRIPTION("Xen Mmap of hypercall buffers");MODULE_LICENSE("GPL"); struct privcmd_buf_private { struct mutex lock; struct list_head list;}; struct privcmd_buf_vma_private { struct privcmd_buf_private *file_priv; struct list_head list; unsigned int users; unsigned int n_pages; struct page *pages[];}; static int privcmd_buf_open(struct inode *ino, struct file *file){ struct privcmd_buf_private *file_priv; file_priv = kzalloc_obj(*file_priv); if (!file_priv) return -ENOMEM; mutex_init(&file_priv->lock); INIT_LIST_HEAD(&file_priv->list); file->private_data = file_priv; return 0;} static void privcmd_buf_vmapriv_free(struct privcmd_buf_vma_private *vma_priv){ unsigned int i; list_del(&vma_priv->list); for (i = 0; i < vma_priv->n_pages; i++) __free_page(vma_priv->pages[i]); kfree(vma_priv);} static int privcmd_buf_release(struct inode *ino, struct file *file){ struct privcmd_buf_private *file_priv = file->private_data; struct privcmd_buf_vma_private *vma_priv; mutex_lock(&file_priv->lock); while (!list_empty(&file_priv->list)) { vma_priv = list_first_entry(&file_priv->list, struct privcmd_buf_vma_private, list); privcmd_buf_vmapriv_free(vma_priv); } mutex_unlock(&file_priv->lock); kfree(file_priv); return 0;} static void privcmd_buf_vma_open(struct vm_area_struct *vma){ struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data; if (!vma_priv) return; mutex_lock(&vma_priv->file_priv->lock); vma_priv->users++; mutex_unlock(&vma_priv->file_priv->lock);} static void privcmd_buf_vma_close(struct vm_area_struct *vma){ struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data; struct privcmd_buf_private *file_priv; if (!vma_priv) return; file_priv = vma_priv->file_priv; mutex_lock(&file_priv->lock); vma_priv->users--; if (!vma_priv->users) privcmd_buf_vmapriv_free(vma_priv); mutex_unlock(&file_priv->lock);} static vm_fault_t privcmd_buf_vma_fault(struct vm_fault *vmf){ pr_debug("fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n", vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end, vmf->pgoff, (void *)vmf->address); return VM_FAULT_SIGBUS;} static const struct vm_operations_struct privcmd_buf_vm_ops = { .open = privcmd_buf_vma_open, .close = privcmd_buf_vma_close, .fault = privcmd_buf_vma_fault,}; static int privcmd_buf_mmap(struct file *file, struct vm_area_struct *vma){ struct privcmd_buf_private *file_priv = file->private_data; struct privcmd_buf_vma_private *vma_priv; unsigned long count = vma_pages(vma); unsigned int i; int ret = 0; if (!(vma->vm_flags & VM_SHARED)) return -EINVAL; vma_priv = kzalloc_flex(*vma_priv, pages, count); if (!vma_priv) return -ENOMEM; for (i = 0; i < count; i++) { vma_priv->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); if (!vma_priv->pages[i]) break; vma_priv->n_pages++; } mutex_lock(&file_priv->lock); vma_priv->file_priv = file_priv; vma_priv->users = 1; vm_flags_set(vma, VM_IO | VM_DONTEXPAND); vma->vm_ops = &privcmd_buf_vm_ops; vma->vm_private_data = vma_priv; list_add(&vma_priv->list, &file_priv->list); if (vma_priv->n_pages != count) ret = -ENOMEM; else ret = vm_map_pages_zero(vma, vma_priv->pages, vma_priv->n_pages); if (ret) privcmd_buf_vmapriv_free(vma_priv); mutex_unlock(&file_priv->lock); return ret;} const struct file_operations xen_privcmdbuf_fops = { .owner = THIS_MODULE, .open = privcmd_buf_open, .release = privcmd_buf_release, .mmap = privcmd_buf_mmap,};EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops); struct miscdevice xen_privcmdbuf_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "xen/hypercall", .fops = &xen_privcmdbuf_fops,};
// SPDX-License-Identifier: GPL-2.0 OR MIT
/******************************************************************************
* privcmd-buf.c
*
* Mmap of hypercall buffers.
*
* Copyright (c) 2018 Juergen Gross
*/
#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include "privcmd.h"
MODULE_DESCRIPTION("Xen Mmap of hypercall buffers");
MODULE_LICENSE("GPL");
struct privcmd_buf_private {
struct mutex lock;
struct list_head list;
};
struct privcmd_buf_vma_private {
struct privcmd_buf_private *file_priv;
struct list_head list;
unsigned int users;
unsigned int n_pages;
struct page *pages[];
};
static int privcmd_buf_open(struct inode *ino, struct file *file)
{
struct privcmd_buf_private *file_priv;
file_priv = kzalloc_obj(*file_priv);
if (!file_priv)
return -ENOMEM;
mutex_init(&file_priv->lock);
INIT_LIST_HEAD(&file_priv->list);
file->private_data = file_priv;
return 0;
}
static void privcmd_buf_vmapriv_free(struct privcmd_buf_vma_private *vma_priv)
{
unsigned int i;
list_del(&vma_priv->list);
for (i = 0; i < vma_priv->n_pages; i++)
__free_page(vma_priv->pages[i]);
kfree(vma_priv);
}
static int privcmd_buf_release(struct inode *ino, struct file *file)
{
struct privcmd_buf_private *file_priv = file->private_data;
struct privcmd_buf_vma_private *vma_priv;
mutex_lock(&file_priv->lock);
while (!list_empty(&file_priv->list)) {
vma_priv = list_first_entry(&file_priv->list,
struct privcmd_buf_vma_private,
list);
privcmd_buf_vmapriv_free(vma_priv);
}
mutex_unlock(&file_priv->lock);
kfree(file_priv);
return 0;
}
static void privcmd_buf_vma_open(struct vm_area_struct *vma)
{
struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
if (!vma_priv)
return;
mutex_lock(&vma_priv->file_priv->lock);
vma_priv->users++;
mutex_unlock(&vma_priv->file_priv->lock);
}
static void privcmd_buf_vma_close(struct vm_area_struct *vma)
{
struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
struct privcmd_buf_private *file_priv;
if (!vma_priv)
return;
file_priv = vma_priv->file_priv;
mutex_lock(&file_priv->lock);
vma_priv->users--;
if (!vma_priv->users)
privcmd_buf_vmapriv_free(vma_priv);
mutex_unlock(&file_priv->lock);
}
static vm_fault_t privcmd_buf_vma_fault(struct vm_fault *vmf)
{
pr_debug("fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",
vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end,
vmf->pgoff, (void *)vmf->address);
return VM_FAULT_SIGBUS;
}
static const struct vm_operations_struct privcmd_buf_vm_ops = {
.open = privcmd_buf_vma_open,
.close = privcmd_buf_vma_close,
.fault = privcmd_buf_vma_fault,
};
static int privcmd_buf_mmap(struct file *file, struct vm_area_struct *vma)
{
struct privcmd_buf_private *file_priv = file->private_data;
struct privcmd_buf_vma_private *vma_priv;
unsigned long count = vma_pages(vma);
unsigned int i;
int ret = 0;
if (!(vma->vm_flags & VM_SHARED))
return -EINVAL;
vma_priv = kzalloc_flex(*vma_priv, pages, count);
if (!vma_priv)
return -ENOMEM;
for (i = 0; i < count; i++) {
vma_priv->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!vma_priv->pages[i])
break;
vma_priv->n_pages++;
}
mutex_lock(&file_priv->lock);
vma_priv->file_priv = file_priv;
vma_priv->users = 1;
vm_flags_set(vma, VM_IO | VM_DONTEXPAND);
vma->vm_ops = &privcmd_buf_vm_ops;
vma->vm_private_data = vma_priv;
list_add(&vma_priv->list, &file_priv->list);
if (vma_priv->n_pages != count)
ret = -ENOMEM;
else
ret = vm_map_pages_zero(vma, vma_priv->pages,
vma_priv->n_pages);
if (ret)
privcmd_buf_vmapriv_free(vma_priv);
mutex_unlock(&file_priv->lock);
return ret;
}
const struct file_operations xen_privcmdbuf_fops = {
.owner = THIS_MODULE,
.open = privcmd_buf_open,
.release = privcmd_buf_release,
.mmap = privcmd_buf_mmap,
};
EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops);
struct miscdevice xen_privcmdbuf_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "xen/hypercall",
.fops = &xen_privcmdbuf_fops,
};