123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- /* https://cirosantilli.com/linux-kernel-module-cheat#mmap */
- #include <asm-generic/io.h> /* virt_to_phys */
- #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. */
- 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");
|