Hallo.
I try to use a mmap for communicate userspace application and other
servers via kernel character driver. and its doesn't work. but, of
course its work in native linux.
my mmap call return no error, but when i try acces to memmory via
returned pointer, i got seg fault
Here is driver source and example program, based on mmap example:
any of vmalloc or kalloc got seg fault:
mmap ok, vaddr = 40034000
Segmentation fault
//#include <linux/config.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#ifdef MODVERSIONS
# include <linux/modversions.h>
#endif
#include <asm/io.h>
/* character device structures */
static dev_t vhsm_dev;
static struct cdev vhsm_cdev;
/* methods of the character device */
static int vhsm_open(struct inode *inode, struct file *filp);
static int vhsm_release(struct inode *inode, struct file *filp);
static int vhsm_mmap(struct file *filp, struct vm_area_struct *vma);
/* the file operations, i.e. all character device methods */
static struct file_operations vhsm_fops = {
.open = vhsm_open,
.release = vhsm_release,
.mmap = vhsm_mmap,
.owner = THIS_MODULE,
};
// internal data
// length of the two memory areas
#define NPAGES 16
// pointer to the vmalloc'd area - alway page aligned
static int *vmalloc_area;
// pointer to the kmalloc'd area, rounded up to a page boundary
static int *kmalloc_area;
// original pointer for kmalloc'd area as returned by kmalloc
static void *kmalloc_ptr;
/* character device open method */
static int vhsm_open(struct inode *inode, struct file *filp)
{
return 0;
}
/* character device last close method */
static int vhsm_release(struct inode *inode, struct file *filp)
{
return 0;
}
// helper function, mmap's the kmalloc'd area which is physically contiguous
int vhsm_kmem(struct file *filp, struct vm_area_struct *vma)
{
int ret;
long length = vma->vm_end - vma->vm_start;
/* check length - do not allow larger mappings than the number of
pages allocated */
if (length > NPAGES * PAGE_SIZE)
return -EIO;
/* map the whole physically contiguous area in one piece */
if ((ret = remap_pfn_range(vma,
vma->vm_start,
virt_to_phys((void *)kmalloc_area)
>> PAGE_SHIFT,
length,
vma->vm_page_prot)) < 0) {
return ret;
}
return 0;
}
/* character device mmap method */
static int vhsm_mmap(struct file *filp, struct vm_area_struct *vma)
{
/* at offset 0 we map the vmalloc'd area */
if (vma->vm_pgoff == 0) {
return vhsm_vmem(filp, vma);
}
/* at offset NPAGES we map the kmalloc'd area */
if (vma->vm_pgoff == NPAGES) {
return vhsm_kmem(filp, vma);
}
/* at any other offset we return an error */
return -EIO;
}
/* module initialization - called at module load time */
static int __init vhsm_init(void)
{
int ret = 0;
int i;
/* allocate a memory area with kmalloc. Will be rounded up to
a page boundary */
if ((kmalloc_ptr = kmalloc((NPAGES + 2) * PAGE_SIZE,
GFP_KERNEL)) == NULL) {
ret = -ENOMEM;
goto out;
}
/* round it up to the page bondary */
kmalloc_area = (int *)((((unsigned long)kmalloc_ptr) +
PAGE_SIZE - 1) & PAGE_MASK);
/* allocate a memory area with vmalloc. */
if ((vmalloc_area = (int *)vmalloc(NPAGES * PAGE_SIZE)) == NULL) {
ret = -ENOMEM;
goto out_kfree;
}
/* get the major number of the character device */
if ((ret = alloc_chrdev_region(&vhsm_dev, 0, 1, "mmap")) < 0) {
printk(KERN_ERR "could not allocate major number for mmap\n");
goto out_vfree;
}
/* initialize the device structure and register the device
with the kernel */
cdev_init(&vhsm_cdev, &vhsm_fops);
if ((ret = cdev_add(&vhsm_cdev, vhsm_dev, 1)) < 0) {
printk(KERN_ERR "could not allocate chrdev for mmap\n");
goto out_unalloc_region;
}
/* mark the pages reserved */
for (i = 0; i < NPAGES * PAGE_SIZE; i+= PAGE_SIZE) {
SetPageReserved(vmalloc_to_page((void *)(((unsigned
long)vmalloc_area) + i)));
SetPageReserved(virt_to_page(((unsigned
long)kmalloc_area) + i));
}
/* store a pattern in the memory - the test application will
check for it */
for (i = 0; i < (NPAGES * PAGE_SIZE / sizeof(int)); i += 2) {
vmalloc_area[i] = (0xaffe << 16) + i;
vmalloc_area[i + 1] = (0xbeef << 16) + i;
kmalloc_area[i] = (0xdead << 16) + i;
kmalloc_area[i + 1] = (0xbeef << 16) + i;
}
return ret;
out_unalloc_region:
unregister_chrdev_region(vhsm_dev, 1);
out_vfree:
vfree(vmalloc_area);
out_kfree:
kfree(kmalloc_ptr);
out:
return ret;
}
/* module unload */
static void __exit vhsm_exit(void)
{
int i;
/* remove the character deivce */
cdev_del(&vhsm_cdev);
unregister_chrdev_region(vhsm_dev, 1);
/* unreserve the pages */
for (i = 0; i < NPAGES * PAGE_SIZE; i+= PAGE_SIZE) {
SetPageReserved(vmalloc_to_page((void *)(((unsigned
long)vmalloc_area) + i)));
SetPageReserved(virt_to_page(((unsigned
long)kmalloc_area) + i));
}
/* free the memory areas */
vfree(vmalloc_area);
kfree(kmalloc_ptr);
}
module_init(vhsm_init);
module_exit(vhsm_exit);
test program:
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#define NPAGES 16
/* this is a test program that opens the mmap_drv.
It reads out values of the kmalloc() and vmalloc()
allocated areas and checks for correctness.
You need a device special file to access the driver.
The device special file is called 'node' and searched
in the current directory.
To create it
- load the driver
'insmod mmap_mod.o'
- find the major number assigned to the driver
'grep mmapdrv /proc/devices'
- and create the special file (assuming major number 254)
'mknod node c 254 0'
*/
int main(void)
{
int fd;
unsigned int *vadr;
unsigned int *kadr;
int len = NPAGES * getpagesize();
if ((fd=open("node", O_RDWR|O_SYNC))<0)
{
perror("open");
exit(-1);
}
vadr = mmap(0, len, PROT_READ, MAP_SHARED, fd, 0);
if (vadr == MAP_FAILED)
{
perror("mmap");
exit(-1);
}
if ((vadr[0]!=0xaffe0000) || (vadr[1]!=0xbeef0000)
|| (vadr[len/sizeof(int)-2]!=(0xaffe0000+len/sizeof(int)-2))
|| (vadr[len/sizeof(int)-1]!=(0xbeef0000+len/sizeof(int)-2)))
{
printf("0x%x 0x%x\n", vadr[0], vadr[1]);
printf("0x%x 0x%x\n", vadr[len/sizeof(int)-2], vadr[len/sizeof(int)-1]);
}
kadr = mmap(0, len, PROT_READ|PROT_WRITE, MAP_SHARED| MAP_LOCKED, fd, len);
if (kadr == MAP_FAILED)
{
perror("mmap");
exit(-1);
}
if ((kadr[0]!=0xdead0000) || (kadr[1]!=0xbeef0000)
|| (kadr[len / sizeof(int) - 2] != (0xdead0000 + len / sizeof(int) - 2))
|| (kadr[len / sizeof(int) - 1] != (0xbeef0000 + len / sizeof(int) - 2)))
{
printf("0x%x 0x%x\n", kadr[0], kadr[1]);
printf("0x%x 0x%x\n", kadr[len / sizeof(int) - 2], kadr[len /
sizeof(int) - 1]);
}
close(fd);
return(0);
}
--
Sartakov A. Vasily