184 lines
3.7 KiB
C
184 lines
3.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <linux/module.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/genalloc.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/kvm_host.h>
|
|
#include <asm/kvm_host.h>
|
|
|
|
static bool addr_in_pool(struct gen_pool *pool,
|
|
unsigned long start, size_t size)
|
|
{
|
|
bool found = false;
|
|
unsigned long end = start + size - 1;
|
|
struct gen_pool_chunk *chunk;
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(chunk, &(pool)->chunks, next_chunk) {
|
|
if (start >= chunk->start_addr && start <= chunk->end_addr) {
|
|
if (end <= chunk->end_addr) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
rcu_read_unlock();
|
|
return found;
|
|
}
|
|
|
|
static int vmem_vm_insert_page(struct vm_area_struct *vma)
|
|
{
|
|
unsigned long addr, uaddr;
|
|
struct page *vmem_page;
|
|
struct vmem_info *info;
|
|
size_t size;
|
|
int ret;
|
|
|
|
info = vma->vm_private_data;
|
|
addr = info->start;
|
|
size = info->size;
|
|
uaddr = vma->vm_start;
|
|
|
|
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_MIXEDMAP;
|
|
vmem_page = pfn_to_page(addr >> PAGE_SHIFT);
|
|
do {
|
|
ret = vm_insert_page(vma, uaddr, vmem_page);
|
|
if (ret < 0) {
|
|
pr_info("vm_insert_page failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
vmem_page++;
|
|
uaddr += PAGE_SIZE;
|
|
size -= PAGE_SIZE;
|
|
} while (size > 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void vmem_vm_open(struct vm_area_struct *vma)
|
|
{
|
|
struct vmem_info *info = vma->vm_private_data;
|
|
|
|
atomic_inc(&info->refcnt);
|
|
}
|
|
|
|
static void vmem_vm_close(struct vm_area_struct *vma)
|
|
{
|
|
unsigned long addr;
|
|
size_t size;
|
|
struct vmem_info *info;
|
|
|
|
info = vma->vm_private_data;
|
|
addr = info->start;
|
|
size = info->size;
|
|
|
|
if (atomic_dec_and_test(&info->refcnt)) {
|
|
if (sw64_kvm_pool && addr_in_pool(sw64_kvm_pool, addr, size)) {
|
|
pr_info("gen pool free addr: %#lx, size: %#lx\n",
|
|
addr, size);
|
|
gen_pool_free(sw64_kvm_pool, addr, size);
|
|
}
|
|
kfree(info);
|
|
}
|
|
}
|
|
|
|
const struct vm_operations_struct vmem_vm_ops = {
|
|
.open = vmem_vm_open,
|
|
.close = vmem_vm_close,
|
|
};
|
|
EXPORT_SYMBOL_GPL(vmem_vm_ops);
|
|
|
|
static int vmem_open(struct inode *inode, struct file *flip)
|
|
{
|
|
flip->private_data = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static loff_t vmem_llseek(struct file *filp, loff_t offset, int whence)
|
|
{
|
|
loff_t newpos = 256UL << 30;
|
|
return newpos;
|
|
}
|
|
|
|
static int vmem_release(struct inode *inode, struct file *flip)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int vmem_mmap(struct file *flip, struct vm_area_struct *vma)
|
|
{
|
|
unsigned long addr;
|
|
static struct vmem_info *info;
|
|
size_t size = vma->vm_end - vma->vm_start;
|
|
int ret;
|
|
|
|
if (!(vma->vm_flags & VM_SHARED)) {
|
|
pr_err("%s: mapping must be shared\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!sw64_kvm_pool)
|
|
return -ENOMEM;
|
|
|
|
if (flip->private_data == NULL) {
|
|
addr = gen_pool_alloc(sw64_kvm_pool, size);
|
|
if (!addr)
|
|
return -ENOMEM;
|
|
|
|
info = kzalloc(sizeof(struct vmem_info), GFP_KERNEL);
|
|
pr_info("guest phys addr=%#lx, size=%#lx\n", addr, size);
|
|
info->start = addr;
|
|
info->size = size;
|
|
flip->private_data = (void *)info;
|
|
} else {
|
|
info = flip->private_data;
|
|
addr = info->start;
|
|
}
|
|
|
|
vma->vm_private_data = (void *)info;
|
|
vma->vm_ops = &vmem_vm_ops;
|
|
vma->vm_ops->open(vma);
|
|
|
|
/*to do if size bigger than vm_mem_size*/
|
|
pr_info("sw64_vmem: vm_start=%#lx, size= %#lx\n", vma->vm_start, size);
|
|
|
|
vmem_vm_insert_page(vma);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations vmem_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = vmem_open,
|
|
.llseek = vmem_llseek,
|
|
.release = vmem_release,
|
|
.mmap = vmem_mmap,
|
|
};
|
|
|
|
static struct miscdevice vmem_dev = {
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
.name = "sw64_vmem",
|
|
.fops = &vmem_fops,
|
|
};
|
|
|
|
static int __init vmem_init(void)
|
|
{
|
|
int err;
|
|
|
|
err = misc_register(&vmem_dev);
|
|
if (err != 0) {
|
|
pr_err("Could not register sw64_vmem device\n");
|
|
return err;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void vmem_exit(void)
|
|
{
|
|
misc_deregister(&vmem_dev);
|
|
}
|