From 46bf3f700edfcf4fc96b65488cd0c3d34bf12c0d Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Sun, 30 Jul 2017 11:23:31 +0100 Subject: [PATCH] mmap example works --- kernel_module/mmap.c | 196 ++++++++---------------------------- kernel_module/user/Makefile | 2 +- kernel_module/user/mmap.c | 62 ++++++++++-- rootfs_overlay/mmap.sh | 3 +- 4 files changed, 97 insertions(+), 166 deletions(-) diff --git a/kernel_module/mmap.c b/kernel_module/mmap.c index 7c34e07..68325dd 100644 --- a/kernel_module/mmap.c +++ b/kernel_module/mmap.c @@ -1,43 +1,35 @@ /* -Remember: mmap does not work with debugfs as of 4.9! -*/ +Remember: mmap, like most fops, does not work with debugfs as of 4.9! https://patchwork.kernel.org/patch/9252557/ -#if 0 - -/* Adapted from: https://coherentmusings.wordpress.com/2014/06/10/implementing-mmap-for-transferring-data-from-user-space-to-kernel-space/ */ +#include /* copy_from_user */ #include #include #include -#include +#include /* min */ #include #include +#include #include -#ifndef VM_RESERVED -# define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP) -#endif +static const char *filename = "lkmc_mmap"; -static struct dentry *debugfs_file; +enum { BUFFER_SIZE = 4 }; -struct mmap_info -{ +struct mmap_info { char *data; - int reference; }; +/* After unmap. */ static void vm_close(struct vm_area_struct *vma) { - struct mmap_info *info; - pr_info("vm_close\n"); - info = (struct mmap_info *)vma->vm_private_data; - info->reference--; } +/* First page access. */ static int vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct page *page; @@ -53,13 +45,10 @@ static int vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 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) { - struct mmap_info *info; - pr_info("vm_open\n"); - info = (struct mmap_info *)vma->vm_private_data; - info->reference++; } static struct vm_operations_struct vm_ops = @@ -73,7 +62,7 @@ static int mmap(struct file *filp, struct vm_area_struct *vma) { pr_info("mmap\n"); vma->vm_ops = &vm_ops; - vma->vm_flags |= VM_RESERVED; + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; vma->vm_private_data = filp->private_data; vm_open(vma); return 0; @@ -86,11 +75,38 @@ static int open(struct inode *inode, struct file *filp) pr_info("open\n"); info = kmalloc(sizeof(struct mmap_info), GFP_KERNEL); info->data = (char *)get_zeroed_page(GFP_KERNEL); - memcpy(info->data, "abc", 4); + 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; @@ -107,138 +123,8 @@ static const struct file_operations fops = { .mmap = mmap, .open = open, .release = release, -}; - -static int myinit(void) -{ - debugfs_file = debugfs_create_file("lkmc_mmap", S_IRWXU, NULL, NULL, &fops); - return 0; -} - -static void myexit(void) -{ - debugfs_remove(debugfs_file); -} - -module_init(myinit); -module_exit(myexit); -MODULE_LICENSE("GPL"); - - -/*minimized debugfs*/ - - -#include -#include -#include -#include -#include -#include - -static struct dentry *debugfs_file; - -static int mmap(struct file *filp, struct vm_area_struct *vma) -{ - pr_info("mmap\n"); - if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) { - return -EAGAIN; - } - return 0; -} - -struct file_operations fops = -{ - .owner = THIS_MODULE, - .open = nonseekable_open, - .mmap = mmap -}; - -static int myinit(void) -{ - debugfs_file = debugfs_create_file("lkmc_mmap", S_IRWXU, NULL, NULL, &fops); - return 0; -} - -static void myexit(void) -{ - debugfs_remove(debugfs_file); -} - -module_init(myinit); -module_exit(myexit); -MODULE_LICENSE("GPL"); - -/*working chardev*/ - -#include -#include -#include -#include -#include -#include - -#define NAME "lkmc_mmap" - -static int major; - -static int mmap(struct file *filp, struct vm_area_struct *vma) -{ - pr_info("mmap\n"); - if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) { - return -EAGAIN; - } - return 0; -} - -struct file_operations fops = -{ - .owner = THIS_MODULE, - .open = nonseekable_open, - .mmap = mmap -}; - -static int myinit(void) -{ - major = register_chrdev(0, NAME, &fops); - return 0; -} - -static void myexit(void) -{ - unregister_chrdev(major, NAME); -} - -module_init(myinit) -module_exit(myexit) -MODULE_LICENSE("GPL"); - -#endif - -/* proc attempt */ - -#include -#include -#include -#include -#include -#include -#include - -static const char *filename = "lkmc_procfs"; - -static int mmap(struct file *filp, struct vm_area_struct *vma) -{ - pr_info("mmap\n"); - return 0; -} - -struct file_operations fops = -{ - .owner = THIS_MODULE, - .open = nonseekable_open, - .mmap = mmap + .read = read, + .write = write, }; static int myinit(void) diff --git a/kernel_module/user/Makefile b/kernel_module/user/Makefile index f5570ef..71296d6 100644 --- a/kernel_module/user/Makefile +++ b/kernel_module/user/Makefile @@ -1,6 +1,6 @@ .PHONY: clean -CC ?= gcc +CC ?= gcc -ggdb3 -O0 -std=c99 -Wall -Werror -Wextra IN_EXT ?= .c OUT_EXT ?= .out diff --git a/kernel_module/user/mmap.c b/kernel_module/user/mmap.c index b864479..fe3979f 100644 --- a/kernel_module/user/mmap.c +++ b/kernel_module/user/mmap.c @@ -1,36 +1,80 @@ +#include #include #include #include #include #include -#include +#include /* sysconf */ -#define PAGE_SIZE 4096 +enum { BUFFER_SIZE = 4 }; int main(int argc, char **argv) { int fd; - char *address = NULL; + long page_size; + char *address1, *address2; + char buf[BUFFER_SIZE]; if (argc < 2) { printf("Usage: %s \n", argv[0]); return EXIT_FAILURE; } + page_size = sysconf(_SC_PAGE_SIZE); printf("open pathname = %s\n", argv[1]); fd = open(argv[1], O_RDWR | O_SYNC); if (fd < 0) { perror("open"); - return EXIT_FAILURE; + assert(0); } printf("fd = %d\n", fd); - address = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (address == MAP_FAILED) { + + /* mmap twice for double fun. */ + puts("mmap 1"); + address1 = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (address1 == MAP_FAILED) { + perror("mmap"); + assert(0); + } + puts("mmap 2"); + address2 = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (address2 == MAP_FAILED) { perror("mmap"); return EXIT_FAILURE; } - printf("%s\n", address); - memcpy(address + 11 , "qwer", 6); - printf("%s\n", address); + assert(address1 != address2); + + /* Read and modify memory. */ + puts("access 1"); + assert(!strcmp(address1, "asdf")); + /* vm_fault */ + puts("access 2"); + assert(!strcmp(address2, "asdf")); + /* vm_fault */ + strcpy(address1, "qwer"); + /* Also modified. So both virtual addresses point to the same physical address. */ + assert(!strcmp(address2, "qwer")); + + /* Check that modifications made from userland are also visible from the kernel. */ + read(fd, buf, BUFFER_SIZE); + assert(!memcmp(buf, "qwer", BUFFER_SIZE)); + + /* Modify the data from the kernel, and check that the change is visible from userland. */ + write(fd, "zxcv", 4); + assert(!strcmp(address1, "zxcv")); + assert(!strcmp(address2, "zxcv")); + + /* Cleanup. */ + puts("munmap 1"); + if (munmap(address1, page_size)) { + perror("munmap"); + assert(0); + } + puts("munmap 2"); + if (munmap(address2, page_size)) { + perror("munmap"); + assert(0); + } + puts("close"); close(fd); return EXIT_SUCCESS; } diff --git a/rootfs_overlay/mmap.sh b/rootfs_overlay/mmap.sh index 500413c..ead7c69 100755 --- a/rootfs_overlay/mmap.sh +++ b/rootfs_overlay/mmap.sh @@ -1,4 +1,5 @@ #!/bin/sh +set -ex insmod /mmap.ko -/mmap.out /sys/kernel/debug/lkmc_mmap +/mmap.out /proc/lkmc_mmap rmmod /mmap.ko