123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- /*
- Remember: mmap, like most fops, does not work with debugfs as of 4.9! https://patchwork.kernel.org/patch/9252557/
- Adapted from:
- https://coherentmusings.wordpress.com/2014/06/10/implementing-mmap-for-transferring-data-from-user-space-to-kernel-space/
- Related:
- - https://stackoverflow.com/questions/10760479/mmap-kernel-buffer-to-user-space/10770582#10770582
- - https://stackoverflow.com/questions/1623008/allocating-memory-for-user-space-from-kernel-thread
- - https://stackoverflow.com/questions/6967933/mmap-mapping-in-user-space-a-kernel-buffer-allocated-with-kmalloc
- - https://github.com/jeremytrimble/ezdma
- - https://github.com/simonjhall/dma
- - https://github.com/ikwzm/udmabuf
- */
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/kernel.h> /* min */
- #include <linux/mm.h>
- #include <linux/module.h>
- #include <linux/proc_fs.h>
- #include <linux/uaccess.h> /* copy_from_user, copy_to_user */
- #include <linux/slab.h>
- 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. */
- int (*fault)(struct vm_fault *vmf);
- static int 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;
- }
- /* Aftr 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;
- int ret;
- pr_info("read\n");
- info = filp->private_data;
- ret = min(len, (size_t)BUFFER_SIZE);
- if (copy_to_user(buf, info->data, ret)) {
- ret = -EFAULT;
- }
- 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 file_operations fops = {
- .mmap = mmap,
- .open = open,
- .release = release,
- .read = read,
- .write = write,
- };
- static int myinit(void)
- {
- proc_create(filename, 0, NULL, &fops);
- return 0;
- }
- static void myexit(void)
- {
- remove_proc_entry(filename, NULL);
- }
- module_init(myinit)
- module_exit(myexit)
- MODULE_LICENSE("GPL");
|