What's wrong on this mmap() ?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi, 
I'm having some problem understanding how mmap really works.

I have a (dummy) driver that allocate some memory (4k), and gives it to a 
userspace through mmap() - the usual remap_pfn_pages found in every 
example.

The userspace can read the memory correctly, but can't write to it. (though 
it maps with PROT_WRITE)
As the manpage says: PROT_WRITE - Pages may be written.

I'm attaching both the driver and the userspace testbed, with the hope 
someone tells me what's wrong with this.

Note that I tested on ARM and x86 architectures, and different kernel, so It 
seems more likely a fault of mine, that a kernel strangeness. (obviusly, 
you'll say... :P )

The userspace log are something like that:
Base userspace address for mmapped buffer is 0xb7786000
Wrote 00 in user-space 0xb7786000. Reading from it seems 00
Wrote 01 in user-space 0xb7786001. Reading from it seems 01
Wrote 02 in user-space 0xb7786002. Reading from it seems 02
[ .... snip ... ]

While the kernel are:
allocated: 4096 @virt: c7000000 @phys: 8c800000 virtToPhys(): 87000000
mmap worked. vmaflasg: 40184477 phys: 87000000 virt: c7000000
VMA open: vma->vm_start: 40236000 - vma->pgoffs: 87000 - vma->vm_end: 
40237000, vma_flags: 40184477 
VMA close: vma->vm_start: 40236000 - vma->pgoffs: 87000 - vma->vm_end: 
40237000, vma_flags: 40184477 
 00  ff  fe  fd  fc  fb  fa  f9  f8  f7  f6  f5  f4  f3  f2  f1  f0  ef  ee  
ed  ec  eb  ea  e9  e8  e7  e6  e5  e4  e3  e2  e1 

Regards, bye!
-- 
- Andrea Gasparini -
-----------------------------------------------
-------- https://launchpad.net/~gaspa ---------
----- HomePage: http://gaspa.yattaweb.it ------
/* 
 * File:   main.c
 * Author: janesconference
 *
 * Created on September 13, 2011, 11:09 AM
 */

#include <stdio.h>
#include <stdlib.h>
#include <stropts.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>


#define ACQ_DEV "/dev/mmap_test"
#define HEADER_SIZE 32
/* The file descriptor for the device */
static int fd;
int i, retval;
char* buffer;

int main(int argc, char** argv) {
    

    int shared = 0;
    int private = 0;
    int anonymous = 0;
    int fixed = 0;
    int invalidate = 0;
    
    int c;

    opterr = 0;

    while ((c = getopt (argc, argv, "spafui")) != -1)
    switch (c)
      {
      case 's':
        shared = 1;
        break;
      case 'p':
        private = 1;
        break;
      case 'a':
        anonymous = 1;
        break;
      case 'f':
        fixed = 1;
        break;
        case 'i':
        invalidate = 1;
        break;
      case '?':
        if (isprint (optopt))
          fprintf (stderr, "Unknown option `-%c'.\n", optopt);
        else
          fprintf (stderr,
                   "Unknown option character `\\x%x'.\n",
                   optopt);
        return 1;
      default:
        abort ();
      }

    int flags;
    
    if (shared && private) {
        printf ("can't map private AND shared. Setting to default (private)\n");
        shared = 0;
        flags = MAP_PRIVATE;
    }
    
    if (!shared && !private) {
        shared = 0;
        printf ("can't map not shared nor private. Setting to default (private)\n");
        flags = MAP_PRIVATE;
    }
    
    if (shared) {
        printf ("setting MAP_SHARED\n");
        flags = MAP_SHARED;
    }
    
    if (private) {
        printf ("setting MAP_PRIVATE\n");
        flags = MAP_PRIVATE;
    }
    
    if (anonymous) {
        printf ("setting MAP_ANONYMOUS\n");
        flags |= MAP_ANONYMOUS;
    }
    
    if (fixed) {
        printf ("setting MAP_FIXED\n");
        flags |= MAP_FIXED;
    }
    
    int real_frame_len = 4096;
    
    printf ("Opening file descriptor %s\n", ACQ_DEV);
    /* Open the device file descriptor*/
    fd = open(ACQ_DEV,O_RDONLY);
    if ( fd < 0 ) {
        fprintf (stderr, "%s\n", strerror(errno));
        return -1;
    }
    printf ("Opened file descriptor %s\n", ACQ_DEV);
    
    printf ("mmapping mmap buffer number %d with size: %lu \n", i, real_frame_len);
        buffer = mmap(NULL, real_frame_len, PROT_READ | PROT_WRITE, flags, fd, 0);
        if (buffer == MAP_FAILED){
            perror("mmap failed");
            fprintf (stderr, "mmap size: %d offset: 0\n", real_frame_len);
            return -1;
        }
        
    printf("Base userspace address for mmapped buffer is %p\n",buffer);
    
    flags = MS_SYNC;
    
    if (invalidate) {
        printf ("setting MS_INVALIDATE\n");
        flags |= MS_INVALIDATE;
    }
        
    for (i = 0; i < 4096; i+=1) {

        char character = i % 0xFF;
        buffer[i] = character;
        printf("Wrote %02X in user-space %p. Reading from it seems %02X\n", character, buffer + i, buffer[i]);
        retval = msync(buffer, 4096, flags);
        if (retval == -1) {
            perror ("error syncyng mmap\n");
        }
        getchar();
        
    }
    
    return (EXIT_SUCCESS);
}
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/proc_fs.h>
#include <linux/device.h>
#include <linux/mman.h>

#include <asm/io.h>
#include <asm/page.h>
#include <asm/uaccess.h>
#include <linux/version.h>

MODULE_AUTHOR("Andrea Gasparini");
MODULE_DESCRIPTION("Linux Kernel - MMap Test module");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");


#define DEVNAME "mmap_test"

unsigned int major = 0;
static unsigned long maxframebuffer = PAGE_SIZE;
static char* buffer = NULL;

#define BUFFER_ADDRESS (0x80000000 + 200u*1024u*1024u)

void simple_vma_open(struct vm_area_struct *vma){
    printk("VMA open: vma->vm_start: %lx - vma->pgoffs: %lx - vma->vm_end: %lx, vma_flags: %lx \n",
            vma->vm_start, vma->vm_pgoff, vma->vm_end,vma->vm_flags);
}

int simple_vma_fault(struct vm_area_struct* vma, struct vm_fault *vmf){
    printk("VMA FAULT: vma->vm_start: %lx - vma->pgoffs: %lx - vma->vm_end: %lx, vma_flags: %lx \n",
            vma->vm_start, vma->vm_pgoff, vma->vm_end,vma->vm_flags);
}

int simple_vma_mkrite(struct vm_area_struct* vma, struct vm_fault *vmf){
    printk("VMA MKWRITE: vma->vm_start: %lx - vma->pgoffs: %lx - vma->vm_end: %lx, vma_flags: %lx \n",
            vma->vm_start, vma->vm_pgoff, vma->vm_end,vma->vm_flags);
}

void simple_vma_close(struct vm_area_struct *vma){
    printk("VMA close: vma->vm_start: %lx - vma->pgoffs: %lx - vma->vm_end: %lx, vma_flags: %lx \n",
            vma->vm_start, vma->vm_pgoff, vma->vm_end,vma->vm_flags);
}

static struct vm_operations_struct simple_remap_vm_ops = {
    .open = simple_vma_open,
    .fault = simple_vma_fault,
    .page_mkwrite = simple_vma_mkrite,
    .close = simple_vma_close,

};

int mmaptest_mmap(struct file *filp, struct vm_area_struct *vma)
{
    unsigned int buffersize = 0;

    /* Total length of buffers */
    buffersize = (maxframebuffer >> PAGE_SHIFT)+1;

/*    printk("mmap before buffersize check buffersize: %ld requested: %ld\n",buffersize,(vma->vm_end - vma->vm_start));*/
    if ( (vma->vm_end - vma->vm_start)>>PAGE_SHIFT > buffersize )
        return -ENOMEM;

    vma->vm_flags |= VM_IO | VM_RESERVED;
    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

    vma->vm_pgoff = virt_to_phys(buffer) >> PAGE_SHIFT;
    //vma->vm_pgoff = BUFFER_ADDRESS >> PAGE_SHIFT;
    if (remap_pfn_range(vma, vma->vm_start,
                    virt_to_phys(buffer) >> PAGE_SHIFT,
                    vma->vm_end - vma->vm_start,
                    vma->vm_page_prot)){
        printk("remap returned something wrong\n");
        return -EAGAIN;
    }


    printk("mmap worked. vmaflasg: %lx phys: %p virt: %p\n",vma->vm_flags,virt_to_phys(buffer),buffer);

    /* We don't care about forking processes (at least yet) */
    vma->vm_ops = &simple_remap_vm_ops;
    simple_vma_open(vma);
    return 0;
}

static int mmaptest_open(struct inode *inode, struct file *filp){
    unsigned int i=0;
    //buffer = ioremap_nocache(BUFFER_ADDRESS,maxframebuffer);
    buffer =  __get_free_pages(GFP_USER | GFP_DMA, get_order(maxframebuffer));
    printk("allocated: %ld @virt: %p @phys: %p virtToPhys(): %p\n",maxframebuffer,buffer,BUFFER_ADDRESS,virt_to_phys(buffer));
    for(i=0; i<maxframebuffer; i++)
        buffer[i] = (char)(maxframebuffer - i);
    return 0;
}

int mmaptest_release(struct inode *inode, struct file *filp){
    unsigned int i;
    for(i=0; i< 32; i++){
        printk(" %.02x ",buffer[i] );
    }
    printk("\n");
    free_pages(buffer,get_order(maxframebuffer));
    //iounmap(buffer);
    return 0;
}

static struct file_operations mmaptest_fops = {
    .owner   = THIS_MODULE,
    .open    = mmaptest_open,
    .release = mmaptest_release,
    .mmap  = mmaptest_mmap,
};

static int mmaptest_init_module(void) {
    int result;
    printk(KERN_INFO DEVNAME "module loading...\n");
    result = register_chrdev(major, DEVNAME, &mmaptest_fops);
    if ( result < 0 ) {
        printk(KERN_ERR DEVNAME ": unable to register character device\n");
        unregister_chrdev( major, DEVNAME );
        return -1;
    }
    if (!major) {
        major = result;
    }

    printk(KERN_INFO "Register initialized\n");
    printk(KERN_INFO DEVNAME " device: %d registered \n",major);
    return 0;
}

static void mmaptest_cleanup_module(void) {
    //printk(KERN_INFO DEVNAME " module unloaded\n");
    unregister_chrdev(major, DEVNAME );
}


module_init(mmaptest_init_module);
module_exit(mmaptest_cleanup_module);
_______________________________________________
Kernelnewbies mailing list
Kernelnewbies@xxxxxxxxxxxxxxxxx
http://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies

[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux