/* https://cirosantilli.com/linux-kernel-module-cheat#mmap */ #include /* virt_to_phys */ #include #include #include /* min */ #include #include #include #include /* copy_from_user, copy_to_user */ #include static const char *filename = "lkmc_mmap"; enum { BUFFER_SIZE = 4 }; struct mmap_info { char *data; }; /* After unmap. */ static void vm_close(struct vm_area_struct *vma) { pr_info("vm_close\n"); } /* First page access. */ static vm_fault_t vm_fault(struct vm_fault *vmf) { struct page *page; struct mmap_info *info; pr_info("vm_fault\n"); info = (struct mmap_info *)vmf->vma->vm_private_data; if (info->data) { page = virt_to_page(info->data); get_page(page); vmf->page = page; } return 0; } /* After mmap. TODO vs mmap, when can this happen at a different time than mmap? */ static void vm_open(struct vm_area_struct *vma) { pr_info("vm_open\n"); } static struct vm_operations_struct vm_ops = { .close = vm_close, .fault = vm_fault, .open = vm_open, }; static int mmap(struct file *filp, struct vm_area_struct *vma) { pr_info("mmap\n"); vma->vm_ops = &vm_ops; vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; vma->vm_private_data = filp->private_data; vm_open(vma); return 0; } static int open(struct inode *inode, struct file *filp) { struct mmap_info *info; pr_info("open\n"); info = kmalloc(sizeof(struct mmap_info), GFP_KERNEL); pr_info("virt_to_phys = 0x%llx\n", (unsigned long long)virt_to_phys((void *)info)); info->data = (char *)get_zeroed_page(GFP_KERNEL); memcpy(info->data, "asdf", BUFFER_SIZE); filp->private_data = info; return 0; } static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off) { struct mmap_info *info; ssize_t ret; pr_info("read\n"); if ((size_t)BUFFER_SIZE <= *off) { ret = 0; } else { info = filp->private_data; ret = min(len, (size_t)BUFFER_SIZE - (size_t)*off); if (copy_to_user(buf, info->data + *off, ret)) { ret = -EFAULT; } else { *off += ret; } } return ret; } static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff_t *off) { struct mmap_info *info; pr_info("write\n"); info = filp->private_data; if (copy_from_user(info->data, buf, min(len, (size_t)BUFFER_SIZE))) { return -EFAULT; } else { return len; } } static int release(struct inode *inode, struct file *filp) { struct mmap_info *info; pr_info("release\n"); info = filp->private_data; free_page((unsigned long)info->data); kfree(info); filp->private_data = NULL; return 0; } static const struct proc_ops pops = { .proc_mmap = mmap, .proc_open = open, .proc_release = release, .proc_read = read, .proc_write = write, }; static int myinit(void) { proc_create(filename, 0, NULL, &pops); return 0; } static void myexit(void) { remove_proc_entry(filename, NULL); } module_init(myinit) module_exit(myexit) MODULE_LICENSE("GPL");