diff --git a/tools/labs/skels/memory_mapping/kmmap/Kbuild b/tools/labs/skels/memory_mapping/kmmap/Kbuild new file mode 100644 index 00000000000000..3df7ecec2cab89 --- /dev/null +++ b/tools/labs/skels/memory_mapping/kmmap/Kbuild @@ -0,0 +1,3 @@ +ccflags-y = -Wno-unused-function -Wno-unused-label -Wno-unused-variable + +obj-m := kmmap.o diff --git a/tools/labs/skels/memory_mapping/kmmap/kmmap.c b/tools/labs/skels/memory_mapping/kmmap/kmmap.c new file mode 100644 index 00000000000000..04b5466cfac8b2 --- /dev/null +++ b/tools/labs/skels/memory_mapping/kmmap/kmmap.c @@ -0,0 +1,234 @@ +/* + * PSO - Memory Mapping Lab(#11) + * + * Exercise #1: memory mapping using kmalloc'd kernel areas + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../test/mmap-test.h" + +MODULE_DESCRIPTION("simple mmap driver"); +MODULE_AUTHOR("PSO"); +MODULE_LICENSE("Dual BSD/GPL"); + +#define MY_MAJOR 42 +/* how many pages do we actually kmalloc */ +#define NPAGES 16 + +/* character device basic structure */ +static struct cdev mmap_cdev; + +/* pointer to kmalloc'd area */ +static void *kmalloc_ptr; + +/* pointer to the kmalloc'd area, rounded up to a page boundary */ +static char *kmalloc_area; + +static int my_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int my_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int my_read(struct file *file, char __user *user_buffer, + size_t size, loff_t *offset) +{ + int ret; + /* TODO 2: check size doesn't exceed our mapped area size */ + if (size > (NPAGES + 1) * PAGE_SIZE) { + size = (NPAGES + 1) * PAGE_SIZE; + } + + /* TODO 2: copy from mapped area to user buffer */ + ret = copy_to_user(user_buffer, kmalloc_area, size); + if (ret) { + return ret; + } + + return size; +} + +static int my_write(struct file *file, const char __user *user_buffer, + size_t size, loff_t *offset) +{ + int ret; + /* TODO 2: check size doesn't exceed our mapped area size */ + if (size > (NPAGES + 1) * PAGE_SIZE) { + size = (NPAGES + 1) * PAGE_SIZE; + } + + /* TODO 2: copy from user buffer to mapped area */ + ret = copy_to_user(kmalloc_area, user_buffer, size); + if (ret) { + return ret; + } + + return size; +} + +static int my_mmap(struct file *filp, struct vm_area_struct *vma) +{ + int ret; + long length = vma->vm_end - vma->vm_start; + unsigned long pfn; + + /* do not map more than we can */ + if (length > NPAGES * PAGE_SIZE) + return -EIO; + + /* TODO 1: map the whole physically contiguous area in one piece */ + pfn = virt_to_phys((void *)kmalloc_area)>>PAGE_SHIFT; + ret = remap_pfn_range(vma, vma->vm_start, pfn, length, vma->vm_page_prot); + + if (ret < 0) { + pr_err("could not map the address area\n"); + return -EIO; + } + + return 0; +} + +static const struct file_operations mmap_fops = { + .owner = THIS_MODULE, + .open = my_open, + .release = my_release, + .mmap = my_mmap, + .read = my_read, + .write = my_write +}; + +static int my_seq_show(struct seq_file *seq, void *v) +{ + struct mm_struct *mm; + struct vm_area_struct *vma_iterator; + unsigned long total = 0; + + /* TODO 3: Get current process' mm_struct */ + mm = get_task_mm(current); + + /* TODO 3: Iterate through all memory mappings */ + vma_iterator = mm->mmap; + while (vma_iterator != NULL) { + total += (vma_iterator->vm_end - vma_iterator->vm_start); + + pr_info("%lx %lxn\n", vma_iterator->vm_start, vma_iterator->vm_end); + + vma_iterator = vma_iterator->vm_next; + } + + /* TODO 3: Release mm_struct */ + mmput(mm); + + /* TODO 3: write the total count to file */ + seq_printf(seq, "%lu", total); + return 0; +} + +static int my_seq_open(struct inode *inode, struct file *file) +{ + /* TODO 3: Register the display function */ + return single_open(file, my_seq_show, NULL); +} + +static const struct proc_ops my_proc_ops = { + .proc_open = my_seq_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +static int __init my_init(void) +{ + int ret = 0; + int i; + /* TODO 3: create a new entry in procfs */ + struct proc_dir_entry *entry; + + entry = proc_create(PROC_ENTRY_NAME, 0, NULL, &my_proc_ops); + if (!entry) { + return -ENOMEM; + } + + ret = register_chrdev_region(MKDEV(MY_MAJOR, 0), 1, "mymap"); + if (ret < 0) { + pr_err("could not register region\n"); + goto out_no_chrdev; + } + + /* TODO 1: allocate NPAGES+2 pages using kmalloc */ + kmalloc_ptr = kmalloc((NPAGES + 2) * PAGE_SIZE, GFP_USER); + + /* TODO 1: round kmalloc_ptr to nearest page start address */ + kmalloc_area = (char *)PAGE_ALIGN((int)kmalloc_ptr); + + /* TODO 1: mark pages as reserved */ + for(i = 0; i < (NPAGES + 2) * PAGE_SIZE; i += PAGE_SIZE) { + SetPageReserved(virt_to_page(((unsigned long)kmalloc_ptr) + i)); + } + + /* TODO 1: write data in each page */ + kmalloc_area[0] = 0xaa; + kmalloc_area[1] = 0xbb; + kmalloc_area[2] = 0xcc; + kmalloc_area[3] = 0xdd; + + /* Init device. */ + cdev_init(&mmap_cdev, &mmap_fops); + ret = cdev_add(&mmap_cdev, MKDEV(MY_MAJOR, 0), 1); + if (ret < 0) { + pr_err("could not add device\n"); + goto out_kfree; + } + + return 0; + +out_kfree: + kfree(kmalloc_ptr); +out_unreg: + unregister_chrdev_region(MKDEV(MY_MAJOR, 0), 1); +out_no_chrdev: + remove_proc_entry(PROC_ENTRY_NAME, NULL); +out: + return ret; +} + +static void __exit my_exit(void) +{ + int i; + + cdev_del(&mmap_cdev); + + /* TODO 1: clear reservation on pages and free mem. */ + for(i = 0; i < (NPAGES + 2) * PAGE_SIZE; i += PAGE_SIZE) { + ClearPageReserved(virt_to_page(((unsigned long)kmalloc_ptr) + i)); + } + kfree(kmalloc_ptr); + + unregister_chrdev_region(MKDEV(MY_MAJOR, 0), 1); + + /* TODO 3: remove proc entry */ + remove_proc_entry(PROC_ENTRY_NAME, NULL); +} + +module_init(my_init); +module_exit(my_exit); diff --git a/tools/labs/skels/memory_mapping/test/.gitignore b/tools/labs/skels/memory_mapping/test/.gitignore new file mode 100644 index 00000000000000..2a2fb151cce3b2 --- /dev/null +++ b/tools/labs/skels/memory_mapping/test/.gitignore @@ -0,0 +1 @@ +/mmap-test diff --git a/tools/labs/skels/memory_mapping/test/Makefile b/tools/labs/skels/memory_mapping/test/Makefile new file mode 100644 index 00000000000000..8639d2a7d45f53 --- /dev/null +++ b/tools/labs/skels/memory_mapping/test/Makefile @@ -0,0 +1,9 @@ +CFLAGS=-Wall -m32 +LDFLAGS=-static -m32 + +mmap-test: mmap-test.o + +.PHONY: clean + +clean: + -rm -f *~ *.o mmap-test diff --git a/tools/labs/skels/memory_mapping/test/mmap-test.c b/tools/labs/skels/memory_mapping/test/mmap-test.c new file mode 100644 index 00000000000000..a4aa5669149d1e --- /dev/null +++ b/tools/labs/skels/memory_mapping/test/mmap-test.c @@ -0,0 +1,173 @@ +/* + * PSO - Memory Mapping Lab (#11) + * + * Exercise #1, #2: memory mapping between user-space and kernel-space + * + * test case + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmap-test.h" + +#define NPAGES 16 +#define MMAP_DEV "/dev/mymmap" +#define PROC_ENTRY_PATH "/proc/" PROC_ENTRY_NAME + +void test_contents(unsigned char *addr, + unsigned char value1, unsigned char value2, + unsigned char value3, unsigned char value4) +{ + int i; + + for (i = 0; i < NPAGES * getpagesize(); i += getpagesize()) { + if (addr[i] != value1 || addr[i + 1] != value2 || + addr[i + 2] != value3 || addr[i + 3] != value4) + printf("0x%x 0x%x 0x%x 0x%x\n", addr[i], addr[i+1], + addr[i+2], addr[i+3]); + else + printf("matched\n"); + } +} + +int test_read_write(int fd, unsigned char *mmap_addr) +{ + unsigned char *local_addr; + int len = NPAGES * getpagesize(), i; + + printf("\nWrite test ...\n"); + /* alloc local memory */ + local_addr = malloc(len); + if (!local_addr) + return -1; + + /* init local memory */ + memset(local_addr, 0, len); + for (i = 0; i < NPAGES * getpagesize(); i += getpagesize()) { + local_addr[i] = 0xa0; + local_addr[i+1] = 0xb0; + local_addr[i+2] = 0xc0; + local_addr[i+3] = 0xd0; + } + + /* write to device */ + write(fd, local_addr, len); + + /* are these values in mapped memory? */ + test_contents(mmap_addr, 0xa0, 0xb0, 0xc0, 0xd0); + + printf("\nRead test ...\n"); + memset(local_addr, 0, len); + /* read from device */ + read(fd, local_addr, len); + /* are the values read correct? */ + test_contents(local_addr, 0xa0, 0xb0, 0xc0, 0xd0); + return 0; +} + +static int show_mem_usage(void) +{ + int fd, ret; + char buf[40]; + unsigned long mem_usage; + + fd = open(PROC_ENTRY_PATH, O_RDONLY); + if (fd < 0) { + perror("open " PROC_ENTRY_PATH); + ret = fd; + goto out; + } + + ret = read(fd, buf, sizeof buf); + if (ret < 0) + goto no_read; + + sscanf(buf, "%lu", &mem_usage); + buf[ret] = 0; + + printf("Memory usage: %lu\n", mem_usage); + + ret = mem_usage; +no_read: + close(fd); +out: + return ret; +} + +int main(int argc, const char **argv) +{ + int fd, test; + unsigned char *addr; + int len = NPAGES * getpagesize(); + int i; + unsigned long usage_before_mmap, usage_after_mmap; + + if (argc > 1) + test = atoi(argv[1]); + + assert(system("mknod " MMAP_DEV " c 42 0") == 0); + + fd = open(MMAP_DEV, O_RDWR | O_SYNC); + if (fd < 0) { + perror("open"); + assert(system("rm " MMAP_DEV) == 0); + exit(EXIT_FAILURE); + } + + addr = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + perror("mmap"); + assert(system("rm " MMAP_DEV) == 0); + exit(EXIT_FAILURE); + } + + for (i = 0; i < NPAGES * getpagesize(); i += getpagesize()) { + if (addr[i] != 0xaa || addr[i + 1] != 0xbb || + addr[i + 2] != 0xcc || addr[i + 3] != 0xdd) + printf("0x%x 0x%x 0x%x 0x%x\n", addr[i], addr[i+1], + addr[i+2], addr[i+3]); + else + printf("matched\n"); + } + + + if (test >= 2 && test_read_write(fd, addr)) { + perror("read/write test"); + assert(system("rm " MMAP_DEV) == 0); + exit(EXIT_FAILURE); + } + + if (test >= 3) { + usage_before_mmap = show_mem_usage(); + if (usage_before_mmap < 0) + printf("failed to show memory usage\n"); + + #define SIZE (10 * 1024 * 1024) + addr = mmap(NULL, SIZE, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (addr == MAP_FAILED) + perror("mmap_"); + + usage_after_mmap = show_mem_usage(); + if (usage_after_mmap < 0) + printf("failed to show memory usage\n"); + printf("mmaped :%lu MB\n", + (usage_after_mmap - usage_before_mmap) >> 20); + + sleep(30); + + munmap(addr, SIZE); + } + + close(fd); + + assert(system("rm " MMAP_DEV) == 0); + + return 0; +} diff --git a/tools/labs/skels/memory_mapping/test/mmap-test.h b/tools/labs/skels/memory_mapping/test/mmap-test.h new file mode 100644 index 00000000000000..8d98f57e319240 --- /dev/null +++ b/tools/labs/skels/memory_mapping/test/mmap-test.h @@ -0,0 +1,6 @@ +#ifndef __SO2MMAP_H__ +#define __SO2MMAP_H__ 1 + +#define PROC_ENTRY_NAME "my-proc-entry" + +#endif diff --git a/tools/labs/skels/memory_mapping/vmmap/Kbuild b/tools/labs/skels/memory_mapping/vmmap/Kbuild new file mode 100644 index 00000000000000..eaf763a4a9d505 --- /dev/null +++ b/tools/labs/skels/memory_mapping/vmmap/Kbuild @@ -0,0 +1,3 @@ +ccflags-y = -Wno-unused-function -Wno-unused-label -Wno-unused-variable + +obj-m := vmmap.o diff --git a/tools/labs/skels/memory_mapping/vmmap/vmmap.c b/tools/labs/skels/memory_mapping/vmmap/vmmap.c new file mode 100644 index 00000000000000..72e718d654dae9 --- /dev/null +++ b/tools/labs/skels/memory_mapping/vmmap/vmmap.c @@ -0,0 +1,203 @@ +/* + * PSO - Memory Mapping Lab(#11) + * + * Exercise #2: memory mapping using vmalloc'd kernel areas + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../test/mmap-test.h" + + +MODULE_DESCRIPTION("simple mmap driver"); +MODULE_AUTHOR("PSO"); +MODULE_LICENSE("Dual BSD/GPL"); + +#define MY_MAJOR 42 + +/* how many pages do we actually vmalloc */ +#define NPAGES 16 + +/* character device basic structure */ +static struct cdev mmap_cdev; + +/* pointer to the vmalloc'd area, rounded up to a page boundary */ +static char *vmalloc_area; + +static int my_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int my_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static ssize_t my_read(struct file *file, char __user *user_buffer, + size_t size, loff_t *offset) +{ + /* TODO 2: check size doesn't exceed our mapped area size */ + + /* TODO 2: copy from mapped area to user buffer */ + + return size; +} + +static ssize_t my_write(struct file *file, const char __user *user_buffer, + size_t size, loff_t *offset) +{ + /* TODO 2: check size doesn't exceed our mapped area size */ + + /* TODO 2: copy from user buffer to mapped area */ + + return size; +} + +static int my_mmap(struct file *filp, struct vm_area_struct *vma) +{ + int ret; + long length = vma->vm_end - vma->vm_start; + unsigned long start = vma->vm_start; + char *vmalloc_area_ptr = vmalloc_area; + unsigned long pfn; + + if (length > NPAGES * PAGE_SIZE) + return -EIO; + + /* TODO 1: map pages individually */ + while (length > 0) { + pfn = vmalloc_to_pfn(vmalloc_area_ptr); + + ret = remap_pfn_range(vma, start, pfn, PAGE_SIZE, vma->vm_page_prot); + + if (ret) { + return ret; + } + + start += PAGE_SIZE; + vmalloc_area_ptr += PAGE_SIZE; + length -= PAGE_SIZE; + } + + pr_info("success mmap\n"); + return 0; +} + +static const struct file_operations mmap_fops = { + .owner = THIS_MODULE, + .open = my_open, + .release = my_release, + .mmap = my_mmap, + .read = my_read, + .write = my_write +}; + +static int my_seq_show(struct seq_file *seq, void *v) +{ + struct mm_struct *mm; + struct vm_area_struct *vma_iterator; + unsigned long total = 0; + + /* TODO 3: Get current process' mm_struct */ + + /* TODO 3: Iterate through all memory mappings and print ranges */ + + /* TODO 3: Release mm_struct */ + + /* TODO 3: write the total count to file */ + return 0; +} + +static int my_seq_open(struct inode *inode, struct file *file) +{ + /* TODO 3: Register the display function */ + return 0; +} + +static const struct proc_ops my_proc_ops = { + .proc_open = my_seq_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +static int __init my_init(void) +{ + int ret = 0; + int i; + /* TODO 3: create a new entry in procfs */ + + ret = register_chrdev_region(MKDEV(MY_MAJOR, 0), 1, "mymap"); + if (ret < 0) { + pr_err("could not register region\n"); + goto out_no_chrdev; + } + + /* TODO 1: allocate NPAGES using vmalloc */ + vmalloc_area = (char *)vmalloc(NPAGES * PAGE_SIZE); + if (vmalloc_area == NULL) { + return -ENOMEM; + } + + /* TODO 1: mark pages as reserved */ + for (i = 0; i < NPAGES * PAGE_SIZE; i += PAGE_SIZE) { + SetPageReserved(vmalloc_to_page(vmalloc_area + i)); + } + + /* TODO 1: write data in each page */ + vmalloc_area[0] = 0xaa; + vmalloc_area[1] = 0xbb; + vmalloc_area[2] = 0xcc; + vmalloc_area[3] = 0xdd; + + cdev_init(&mmap_cdev, &mmap_fops); + ret = cdev_add(&mmap_cdev, MKDEV(MY_MAJOR, 0), 1); + if (ret < 0) { + pr_err("could not add device\n"); + goto out_vfree; + } + + return 0; + +out_vfree: + vfree(vmalloc_area); +out_unreg: + unregister_chrdev_region(MKDEV(MY_MAJOR, 0), 1); +out_no_chrdev: + remove_proc_entry(PROC_ENTRY_NAME, NULL); +out: + return ret; +} + +static void __exit my_exit(void) +{ + int i; + + cdev_del(&mmap_cdev); + + /* TODO 1: clear reservation on pages and free mem.*/ + for(i = 0; i < NPAGES * PAGE_SIZE; i += PAGE_SIZE) { + ClearPageReserved(virt_to_page(((unsigned long)vmalloc_area) + i)); + } + vfree(vmalloc_area); + + unregister_chrdev_region(MKDEV(MY_MAJOR, 0), 1); + /* TODO 3: remove proc entry */ +} + +module_init(my_init); +module_exit(my_exit);