// SPDX-License-Identifier: GPL-2.0 #include #include #include #include #include #include #include #include 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); }