Nios® V/II Embedded Design Suite (EDS)
Support for Embedded Development Tools, Processors (SoCs and Nios® V/II processor), Embedded Development Suites (EDSs), Boot and Configuration, Operating Systems, C and C++
12600 Discussions

Porting non-mmu code to MMU -- MMU memcpy SEGV

Altera_Forum
Honored Contributor II
1,554 Views

Thanks in Advance. 

 

With the non-mmu nios2 our design defined sdram regions that we utilized for buffers in our custom device driver. 

**** device driver  

sharedMem = kmalloc( SHARED_MEMSIZE, GFP_USER ); 

if ( sharedMem == NULL) 

res = -ENOMEM; 

exit; 

else 

sharedMemVertAddr = ioremap( sharedMem, SHARED_MEMSIZE ); 

**** end device driver 

 

So, in my user space code I have an ioctl that I call to get the address - "sharedMemAddr" from the driver then I do my: 

 

memcpy( sharedMemAddr, (const void *)&sharedMem, sizeof(sharedMem) ); 

 

FOR THE MMU system I get -- "SEGV"  

FOR THE non-MMU system the scheme works GREAT! 

 

What do I need to do to get the MMU system to allow this type of "memcpy?"
0 Kudos
11 Replies
Altera_Forum
Honored Contributor II
766 Views

Hi, 

 

 

--- Quote Start ---  

 

 

With the non-mmu nios2 our design defined sdram regions that we utilized for buffers in our custom device driver. 

**** device driver  

sharedMem = kmalloc( SHARED_MEMSIZE, GFP_USER ); 

if ( sharedMem == NULL) 

res = -ENOMEM; 

exit; 

else 

sharedMemVertAddr = ioremap( sharedMem, SHARED_MEMSIZE ); 

**** end device driver 

 

So, in my user space code I have an ioctl that I call to get the address - "sharedMemAddr" from the driver then I do my: 

 

memcpy( sharedMemAddr, (const void *)&sharedMem, sizeof(sharedMem) ); 

 

FOR THE MMU system I get -- "SEGV"  

FOR THE non-MMU system the scheme works GREAT! 

 

What do I need to do to get the MMU system to allow this type of "memcpy?" 

--- Quote End ---  

 

 

The MMU always checks the memory access of user code and protects your kernel from destructions. In this case, 'sharedMemAddr' has a kernel space address, so the SEGV means that your MMU and kernel are working well. To access the kernel space from your user application side, you must get the permission and virtual address from your kernel. In general, 

the function 'mmap' is used for this purpose. 

 

http://man7.org/linux/man-pages/man2/mmap.2.html 

 

Kazu
0 Kudos
Altera_Forum
Honored Contributor II
766 Views

Thanks! I figured there was a way. 

 

Now - my mapping needs to be fixed because this buffer is used in a contiguous manner in the firmware.  

So, I'm assuming that this command WILL adhere to that because I have already allocated this area in my device driver? and I'll use: MAP_FIXED with the address.
0 Kudos
Altera_Forum
Honored Contributor II
766 Views

Hi, 

 

 

--- Quote Start ---  

 

Now - my mapping needs to be fixed because this buffer is used in a contiguous manner in the firmware.  

So, I'm assuming that this command WILL adhere to that because I have already allocated this area in my device driver? and I'll use: MAP_FIXED with the address. 

 

--- Quote End ---  

 

 

It's possible to use the MAP_FIXED option, but in this case, your application code must know the value of physically mapped address and it affects the portability. To avoid this, 'device file' (e.g. /dev/fb0, etc.) is used and its file descripter is passed to the 'mmap' function as an argument. To regard everything as files is the key concept of Unix(Linux). Of course, to do so, your driver must have several 'file operations' functions. Two files 'fbmem.c' and 'altfb.c' in the directory of 'drivers/video' may be good(?) samples to understand these methodology.  

 

Kazu
0 Kudos
Altera_Forum
Honored Contributor II
766 Views

Thanks for pointing me to some drivers to study. I really don't need to be portable, but always try to be. 

I have been working on adding the "mmap" function to my device driver. Was hoping to keep the "kmalloc" to get the memory but was getting an error in the drivers "mmap." I was getting interrupted a lot and had only skimmed Rubini book to understand things better.  

Tomorrow I should have uninterrupted time. Going to start by reading Rubini. 

I wasn't planning on using the device file (from reading it didnt seem necessary). 

My other issue that I see I'll need to handle is the fact that I have 3 separate memory blocks to handle, so my mmap will need to take care of that by using the vm_start.
0 Kudos
Altera_Forum
Honored Contributor II
766 Views

I REALY AM STUMPPED... Any help would be GREATLY APPRECIATED!! 

 

I added the following to mmap to my device driver:  

 

static int my_mmap(struct file * filp, struct vm_area_struct * vma) 

int ret; 

printk( KERN_DEBUG "IN MYDRIVER MMAP\n"); 

 

 

long length = vma->vm_end - vma->vm_start; 

printk(KERN_DEBUG "length %d PageSize %d [%d]\n", length , PAGE_SIZE, MY_NPAGES*PAGE_SIZE); 

/* check length - do not allow larger mappings than the number of 

pages allocated */ 

if (length > MY_NPAGES * PAGE_SIZE) 

return -EIO; 

printk(KERN_DEBUG "before remap_pfn_range\n" ); 

/* 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) { 

printk(KERN_DEBUG "ret %l [%l]\n", ret); 

return ret; 

printk(KERN_DEBUG "return 0\n" ); 

return 0; 

 

GLOBALLY I HAVE: 

static int *kmalloc_area; 

// original pointer for kmalloc'd area as returned by kmalloc 

static void *kmalloc_ptr; 

# define MY_NPAGES 16 /// 100 

 

 

In the init of the device driver I have: 

 

/* allocate a memory area with kmalloc. Will be rounded up to a page boundary */ 

if ((kmalloc_ptr = kmalloc((MY_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); 

strcpy(kmalloc_area,"scmd_mmap test data"); 

 

for (i = 0; i < (MY_NPAGES * PAGE_SIZE / sizeof(int)); i += 2) { 

kmalloc_area[i] = (0xdead << 16) + i; 

kmalloc_area[i + 1] = (0xbeef << 16) + i; 

 

AND IN USER SPACE I DO THE FOLLOWING: 

 

 

# define DevName "/dev/myDriver0" 

static unsigned int * mmapResult;  

 

if ((fdPlayback = open("/dev/myDriver0", O_RDWR | O_NONBLOCK | O_SYNC)) < 0) { 

perror("DevNameIO"); 

printf("error opening device %s\n", DevName); 

return -1; 

mmapResult = (unsigned int *)mmap (0, MY_NPAGES*PAGE_SIZE, PROT_WRITE | PROT_READ , MAP_SHARED | MAP_FILE | MAP_FIXED , fdPlayback, 0 ); 

///////// printf("AFTER -- mmap call [%d] MAP_FAILED {%d}\n", mmapResult, MAP_FAILED); 

printf("userspace waiting ---- \n"); 

usleep(5000000); 

if ( mmapResult == (unsigned int *)MAP_FAILED ) 

printf("mmap error for output\n"); 

perror("BOH"); 

return -1; 

 

When I run my tstMmu program I get: 

Open dev_is_open 1 INODE major 240 minor 0 

IN PB9X-MMAP 

length 65536 PageSize 4096 [65536] 

before remap_pfn_range 

return 0 

CLOSING--> dev_is_open 0 INODE major 240 minor 0 

SEGV 

 

 

***************************************************************************************** 

SO -- what is NOT HAPPY? The close of the device driver gets executed before the SEGV 

 

Do I need something else to use the memory? I read an example where they have to do a 'mknod node c driverMajor# 0' and they opened up "node." I haven't tried this but if that is what I need to do is there somewhere I can read to explain this for me?
0 Kudos
Altera_Forum
Honored Contributor II
766 Views

UPDATE and QUESTION(S) 

 

I got the memory working. The thing that I am unclear upon and would like to understand is the "special node" for the mmap area? 

 

I have the device driver open "/dev/myDDriver0" -- which is MAJOR - 240 and Minor 0 

 

I created a node locally in the directory I'm running the test code: mknod node c 204 0 (same major and minor# ) 

I HAVE to do an open on that too.  

 

CODE snippet ***********************************************  

 

if ((fdPlayback = open(DevName, O_RDWR | O_NONBLOCK | O_SYNC)) < 0) { 

perror("DevNameIO"); 

printf("error opening device %s\n", DevName); 

return -1; 

 

if ((fd=open("node", O_RDWR|O_SYNC))<0) 

perror("open"); 

exit(-1); 

 

So I open that one too and do the "mmap" and all is well. 

 

So - why this special node (why can't I use the file pointer from the device driver open)? 

Could someone provide me with some reading to understand this? 

I'm going to need to have 2 more memory areas and would like to know what that is going to entail
0 Kudos
Altera_Forum
Honored Contributor II
766 Views

Hi, 

 

 

--- Quote Start ---  

 

I have the device driver open "/dev/myDDriver0" -- which is MAJOR - 240 and Minor 0 

 

I created a node locally in the directory I'm running the test code: mknod node c 204 0 (same major and minor# ) 

 

--- Quote End ---  

 

 

Same major and minor# ? 240 - 204 ? Sorry, I can't understand your question. 

 

 

--- Quote Start ---  

 

I'm going to need to have 2 more memory areas and would like to know what that is going to entail 

--- Quote End ---  

 

 

There are several ways. One is to use different minor numbers, like 0, 1, 2 etc.. 

 

Kazu
0 Kudos
Altera_Forum
Honored Contributor II
766 Views

 

--- Quote Start ---  

Hi, 

 

 

 

Same major and minor# ? 240 - 204 ? Sorry, I can't understand your question. 

 

OPPS -- they were both 240 sorry for the typo. Which bothered me because it worked but I'm opening the same driver with the same minor number - that isn't something you should do is it? 

 

 

 

 

There are several ways. One is to use different minor numbers, like 0, 1, 2 etc.. 

 

Kazu 

--- Quote End ---  

 

 

O.K. so I can use minor# 0 for the driver and the interrupt functionality, etc. and use minor# 's 1, 2, 3, ... for my mmap areas?
0 Kudos
Altera_Forum
Honored Contributor II
766 Views

Hi, 

 

 

--- Quote Start ---  

 

O.K. so I can use minor# 0 for the driver and the interrupt functionality, etc. and use minor# 's 1, 2, 3, ... for my mmap areas? 

--- Quote End ---  

 

 

Yes, you can request contiguous minor numbers and your mmap function can receive those minor numbers, so you can allocate your memory areas for each.... 

 

Kazu
0 Kudos
Altera_Forum
Honored Contributor II
766 Views

I've "THINK" I have properly requested four minor numbers for my driver.  

When I open "/dev/mine1" and try and do the mmap from user space the mmap function IS NOT entered!! 

So, I'm figuring I have done something incorrectly when doing the cdev_add and the driver funtions are not properly being pointed to --- but it looks like what Rubini has... 

 

Can anyone see anything wrong with what I've done? Thanks in advance. 

********************************************************************************************************** 

Here are the important parts of my driver code: 

 

# define MINOR_COUNT 4  

struct driver_dev { 

int anyNumber; 

struct cdev cdev; 

}; 

 

static struct driver_dev * my_devices;  

dev_t dev; // contains major and first minor#  

 

static int dev_is_open = 0; 

 

static struct file_operations fops_mine = 

 

.read = mine_sleepy_read,  

 

.write = mine_write, 

.open = mine_open, 

.release= mine_close,  

.unlocked_ioctl = mine_ioctl, 

////////////.ioctl = mine_ioctl, 

.mmap = mine_mmap, 

.owner = THIS_MODULE, 

}; 

///////////////////////////// 

 

struct file_operations *driver_fops_array[]={ 

&fops_mine 

}; 

 

static int __devinit mine_drv_probe( struct platform_device *op) 

struct resource *res; 

int ret; 

int i; 

 

printk("######################### IN PROBE \n"); 

 

if (!of_match_device(mine_of_match, &op->dev)) 

return -ENODEV; 

 

res = platform_get_resource( op, IORESOURCE_MEM, 0); 

if (!res) 

return -ENODEV; 

 

if (!request_mem_region(res->start, resource_size(res), "mine")) 

return -ENODEV; 

 

myVertAddr = of_iomap(op->dev.of_node, 0); 

if (!myVertAddr) 

return -ENODEV; 

 

////////////////////////////// 

 

if( register_chrdev_region( MKDEV(DEV_MAJOR,0) , MINOR_COUNT, "mine" ) ) 

printk(KERN_ALERT "register_chrdev_region of mineDriver failed!\n"); 

return -EIO; 

dev = MKDEV( DEV_MAJOR, 0 ); 

/// ALLOCATE MEMORY for COUNT my_devices 

my_devices = kmalloc( MINOR_COUNT* sizeof(struct driver_dev), GFP_KERNEL ); 

if ( my_devices == NULL ) 

res = -ENOMEM; 

goto fail; 

/// fill the my_devices region with ZEROS 

/////// SKIP FOR NOW 

/// Initialize the devices 

for ( i=0; i < MINOR_COUNT; i++ ) 

cdev_init( &my_devices.cdev, &fops_mine); 

my_devices.cdev.owner = THIS_MODULE; 

my_devices.cdev.ops = &fops_mine; 

 

res = cdev_add( &my_devices.cdev, MKDEV( MAJOR(dev), MINOR(dev)+i ), 1 ); 

if ( res ) 

printk( KERN_ALERT "Error %d adding mine%d\n", res, i ); 

else 

printk( KERN_ALERT "mine%d added\n",i ); 

 

///////////////////////////// 

/// / REQUEST IRQ ---> MOVED TO PROBE ---> 

irq = irq_of_parse_and_map( op->dev.of_node, 0 ); 

 

printk( "IRQ %d \n", irq ); 

if ( (ret = request_irq( irq, mine_ReadInterrupt, 0, "mine", op-dev ) )) 

printk(KERN_ALERT ": Can't request Board IRQ %d RET:%i\n", irq, ret ); 

if ( ret == -EBUSY ) printk(KERN_ALERT "IRQ busy\n"); 

printk( KERN_ALERT "IRQ requested %d successfully \n",irq ); 

 

printk("**************************** - probe complete\n"); 

return 0; 

 

fail: 

return ret; 

 

}
0 Kudos
Altera_Forum
Honored Contributor II
766 Views

Hi, 

 

 

--- Quote Start ---  

I've "THINK" I have properly requested four minor numbers for my driver.  

When I open "/dev/mine1" and try and do the mmap from user space the mmap function IS NOT entered!! 

So, I'm figuring I have done something incorrectly when doing the cdev_add and the driver funtions are not properly being pointed to --- but it looks like what Rubini has... 

 

--- Quote End ---  

 

 

What error code does the 'mmap' return ? It seems that you can open "/dev/mine1", so please check it in your function 'mine_open'. 

 

Kazu
0 Kudos
Reply