public
Last active

Really simple example of using get_user_pages and memory aligned allocation using posix_memalign. Hope this helps!

  • Download Gist
awesome.patch
Diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
diff -PurN dummy/gu_page.c get_user_pages/gu_page.c
--- dummy/gu_page.c 1970-01-01 01:00:00.000000000 +0100
+++ get_user_pages/gu_page.c 2012-06-14 14:41:31.797310260 +0100
@@ -0,0 +1,124 @@
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <asm/errno.h>
+
+#include <linux/pagemap.h>
+
+#define LED_MAJOR 42
+#define DEVICE_NAME "simpleuser"
+
+/*
+ * Prototypes
+ */
+static int device_open(struct inode *, struct file *);
+static int device_release(struct inode *, struct file *);
+static ssize_t device_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos);
+
+static struct file_operations led_ops = {
+ .owner = THIS_MODULE,
+ .open = device_open,
+ .release = device_release,
+ .write = device_write
+};
+
+static struct class *mmap_class;
+
+static int device_open(struct inode *inode, struct file *file)
+{
+ printk ("device_open: %d.%d\n", MAJOR (inode->i_rdev),
+ MINOR (inode->i_rdev));
+ return 0;
+}
+
+static int device_release(struct inode *inode, struct file *file)
+{
+ pr_info("device_release: %d.%d\n", MAJOR (inode->i_rdev),
+ MINOR (inode->i_rdev));
+ return 0;
+}
+
+static ssize_t device_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
+{
+ int res;
+ unsigned long uaddr;
+ char addrstr[80];
+ struct page *page;
+ char *my_page_address;
+
+ unsigned long copied = copy_from_user(addrstr, buf, sizeof(addrstr));
+ if (copied != 0)
+ {
+ pr_err("Told to copy %d, but only copied %lu\n", count, copied);
+ }
+ uaddr = simple_strtoul(addrstr, NULL, 0);
+
+ down_read(&current->mm->mmap_sem);
+ res = get_user_pages(current, current->mm,
+ uaddr,
+ 1, /* Only want one page */
+ 1, /* Do want to write into it */
+ 1, /* do force */
+ &page,
+ NULL);
+ if (res == 1) {
+ pr_err("Got page\n");
+ /* Do something with it */
+ my_page_address = kmap(page);
+ strcpy (my_page_address, "Hello, is it me you're looking for?\n");
+ pr_err("Got address %p and user told me it was %lx\n",my_page_address, uaddr);
+ pr_err("Wrote: %s", my_page_address);
+
+ kunmap(page);
+
+ /* Clean up */
+ if (!PageReserved(page))
+ SetPageDirty(page);
+ page_cache_release(page);
+ } else {
+ pr_err("Couldn't get page :(\n");
+ }
+ up_read(&current->mm->mmap_sem);
+
+
+ return count;
+}
+
+static int __init el504_init(void)
+{
+ int ret ;
+
+ /* Register the character device */
+ ret = register_chrdev (LED_MAJOR, DEVICE_NAME, &led_ops);
+ if (ret < 0) {
+ pr_alert("el504_init: failed with %d\n", ret);
+ return ret;
+ }
+
+ mmap_class = class_create (THIS_MODULE, "mmap");
+ device_create (mmap_class, NULL, MKDEV (LED_MAJOR, 0), NULL, "mmap");
+ return 0;
+}
+
+static void __exit el504_exit(void)
+{
+ pr_debug("Goodbye\n");
+ device_destroy (mmap_class, MKDEV (LED_MAJOR, 0));
+ class_destroy (mmap_class);
+ unregister_chrdev (LED_MAJOR, DEVICE_NAME);
+}
+
+module_init (el504_init);
+module_exit (el504_exit);
+
+MODULE_LICENSE ("GPL");
+MODULE_AUTHOR ("Nick Glynn");
+MODULE_DESCRIPTION ("Example for get_user_pages()");
+
diff -PurN dummy/Makefile get_user_pages/Makefile
--- dummy/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ get_user_pages/Makefile 2012-06-14 14:21:11.271274011 +0100
@@ -0,0 +1,9 @@
+obj-m += gu_page.o
+KDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+
+all:
+ $(MAKE) -C $(KDIR) M=$(PWD) modules
+clean:
+ $(MAKE) -C $(KDIR) M=$(PWD) clean
+
diff -PurN dummy/userspace/Makefile get_user_pages/userspace/Makefile
--- dummy/userspace/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ get_user_pages/userspace/Makefile 2012-06-14 14:53:29.528816418 +0100
@@ -0,0 +1,8 @@
+BIN := userspace
+OBJECTS += $(BIN).c
+
+all:
+ gcc $(OBJECTS) -o $(BIN)
+
+clean:
+ rm $(BIN)
diff -PurN dummy/userspace/userspace.c get_user_pages/userspace/userspace.c
--- dummy/userspace/userspace.c 1970-01-01 01:00:00.000000000 +0100
+++ get_user_pages/userspace/userspace.c 2012-06-14 14:47:25.754137234 +0100
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+int main (int argc, char *argv[])
+{
+ int mh, res;
+ char addrstr[80];
+ char *ptr, *bp;
+ const char *string = "Testing page";
+
+ printf ("%s\n", argv[0]);
+ if (argc < 2)
+ {
+ printf ("Usage: %s <file>\n", argv[0]);
+ return 1;
+ }
+ mh = open (argv [1], O_RDWR);
+ if (mh == -1)
+ {
+ printf ("Unable to open '%s'\n", argv [1]);
+ return 1;
+ }
+ /* Grab a page of memory (4096) and sprintf into it */
+
+ res = posix_memalign((void **)&ptr, 4096, 4096);
+
+ if (NULL != ptr)
+ {
+ sprintf(ptr, "%s", string);
+
+ bp = ptr;
+
+ /* Do a write to the kernel at which point it should mess with it */
+ sprintf(addrstr, "%p", ptr);
+ printf("Telling kernel it is %p\n", ptr);
+ res = write(mh, addrstr, strlen(addrstr));
+
+ /* See what lives there still */
+ printf ("Marker text 1: %s\n", ptr);
+ printf ("ptr = %p, bp = %p\n", ptr, bp);
+
+ }
+ close (mh);
+ return 0;
+}

The point of this code was to setup some page-aligned memory in user space, pass the address to the kernel and then have the kernel mess with it to highlight how you can use get_user_pages.

There's a lot of debuggy stuff in there and it's clearly sample code but I struggled to find decent examples on how to do what I wanted to do (allocate my own memory) so hopefully this should help some people out.

@17twnty

...
unsigned long copied = copy_from_user(addrstr, buf, sizeof(addrstr));
if (copied != 0)
{
    pr_err("Told to copy %d, but only copied %lu\n", count, copied);
}
...

You'll need to check copy_from_user result for -EFAULT at least.

@17twenty. Thanks!....You are indeed helping out other!

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.