cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

privcmd-buf.c (4149B)


      1// SPDX-License-Identifier: GPL-2.0 OR MIT
      2
      3/******************************************************************************
      4 * privcmd-buf.c
      5 *
      6 * Mmap of hypercall buffers.
      7 *
      8 * Copyright (c) 2018 Juergen Gross
      9 */
     10
     11#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
     12
     13#include <linux/kernel.h>
     14#include <linux/module.h>
     15#include <linux/list.h>
     16#include <linux/miscdevice.h>
     17#include <linux/mm.h>
     18#include <linux/slab.h>
     19
     20#include "privcmd.h"
     21
     22MODULE_LICENSE("GPL");
     23
     24struct privcmd_buf_private {
     25	struct mutex lock;
     26	struct list_head list;
     27};
     28
     29struct privcmd_buf_vma_private {
     30	struct privcmd_buf_private *file_priv;
     31	struct list_head list;
     32	unsigned int users;
     33	unsigned int n_pages;
     34	struct page *pages[];
     35};
     36
     37static int privcmd_buf_open(struct inode *ino, struct file *file)
     38{
     39	struct privcmd_buf_private *file_priv;
     40
     41	file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
     42	if (!file_priv)
     43		return -ENOMEM;
     44
     45	mutex_init(&file_priv->lock);
     46	INIT_LIST_HEAD(&file_priv->list);
     47
     48	file->private_data = file_priv;
     49
     50	return 0;
     51}
     52
     53static void privcmd_buf_vmapriv_free(struct privcmd_buf_vma_private *vma_priv)
     54{
     55	unsigned int i;
     56
     57	list_del(&vma_priv->list);
     58
     59	for (i = 0; i < vma_priv->n_pages; i++)
     60		__free_page(vma_priv->pages[i]);
     61
     62	kfree(vma_priv);
     63}
     64
     65static int privcmd_buf_release(struct inode *ino, struct file *file)
     66{
     67	struct privcmd_buf_private *file_priv = file->private_data;
     68	struct privcmd_buf_vma_private *vma_priv;
     69
     70	mutex_lock(&file_priv->lock);
     71
     72	while (!list_empty(&file_priv->list)) {
     73		vma_priv = list_first_entry(&file_priv->list,
     74					    struct privcmd_buf_vma_private,
     75					    list);
     76		privcmd_buf_vmapriv_free(vma_priv);
     77	}
     78
     79	mutex_unlock(&file_priv->lock);
     80
     81	kfree(file_priv);
     82
     83	return 0;
     84}
     85
     86static void privcmd_buf_vma_open(struct vm_area_struct *vma)
     87{
     88	struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
     89
     90	if (!vma_priv)
     91		return;
     92
     93	mutex_lock(&vma_priv->file_priv->lock);
     94	vma_priv->users++;
     95	mutex_unlock(&vma_priv->file_priv->lock);
     96}
     97
     98static void privcmd_buf_vma_close(struct vm_area_struct *vma)
     99{
    100	struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
    101	struct privcmd_buf_private *file_priv;
    102
    103	if (!vma_priv)
    104		return;
    105
    106	file_priv = vma_priv->file_priv;
    107
    108	mutex_lock(&file_priv->lock);
    109
    110	vma_priv->users--;
    111	if (!vma_priv->users)
    112		privcmd_buf_vmapriv_free(vma_priv);
    113
    114	mutex_unlock(&file_priv->lock);
    115}
    116
    117static vm_fault_t privcmd_buf_vma_fault(struct vm_fault *vmf)
    118{
    119	pr_debug("fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",
    120		 vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end,
    121		 vmf->pgoff, (void *)vmf->address);
    122
    123	return VM_FAULT_SIGBUS;
    124}
    125
    126static const struct vm_operations_struct privcmd_buf_vm_ops = {
    127	.open = privcmd_buf_vma_open,
    128	.close = privcmd_buf_vma_close,
    129	.fault = privcmd_buf_vma_fault,
    130};
    131
    132static int privcmd_buf_mmap(struct file *file, struct vm_area_struct *vma)
    133{
    134	struct privcmd_buf_private *file_priv = file->private_data;
    135	struct privcmd_buf_vma_private *vma_priv;
    136	unsigned long count = vma_pages(vma);
    137	unsigned int i;
    138	int ret = 0;
    139
    140	if (!(vma->vm_flags & VM_SHARED))
    141		return -EINVAL;
    142
    143	vma_priv = kzalloc(struct_size(vma_priv, pages, count), GFP_KERNEL);
    144	if (!vma_priv)
    145		return -ENOMEM;
    146
    147	for (i = 0; i < count; i++) {
    148		vma_priv->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
    149		if (!vma_priv->pages[i])
    150			break;
    151		vma_priv->n_pages++;
    152	}
    153
    154	mutex_lock(&file_priv->lock);
    155
    156	vma_priv->file_priv = file_priv;
    157	vma_priv->users = 1;
    158
    159	vma->vm_flags |= VM_IO | VM_DONTEXPAND;
    160	vma->vm_ops = &privcmd_buf_vm_ops;
    161	vma->vm_private_data = vma_priv;
    162
    163	list_add(&vma_priv->list, &file_priv->list);
    164
    165	if (vma_priv->n_pages != count)
    166		ret = -ENOMEM;
    167	else
    168		ret = vm_map_pages_zero(vma, vma_priv->pages,
    169						vma_priv->n_pages);
    170
    171	if (ret)
    172		privcmd_buf_vmapriv_free(vma_priv);
    173
    174	mutex_unlock(&file_priv->lock);
    175
    176	return ret;
    177}
    178
    179const struct file_operations xen_privcmdbuf_fops = {
    180	.owner = THIS_MODULE,
    181	.open = privcmd_buf_open,
    182	.release = privcmd_buf_release,
    183	.mmap = privcmd_buf_mmap,
    184};
    185EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops);
    186
    187struct miscdevice xen_privcmdbuf_dev = {
    188	.minor = MISC_DYNAMIC_MINOR,
    189	.name = "xen/hypercall",
    190	.fops = &xen_privcmdbuf_fops,
    191};