LQ Newbie
Registered: Jul 2011
Posts: 3
Rep:
|
copy_from_user strangeness
I'm having problems using copy_from_user to copy a buffer passed to an IOCTL to device physical memory. The copy appears to work (returns 0), but the memory doesn't actually get written. If I use memset, or use copy_from_user to a different kernel space buffer, and then memcpy to the physical memory, it works fine. Similarly I can READ into the user space buffer just fine using copy_to_user. Should I not be expecting copy_from_user to be able to write to a kernel virtual address that was mapped to physical device space?
Here's some code.
I initialize my pointer during PCI probe as such, the test code at the end is just to initialize the memory to indentifiable values that i can later dump:
drv_data->length = pci_resource_len(pdev,0);
drv_data->virtual_address = ioremap_nocache(drv_data->physical_address,drv_data->length);
if(drv_data->virtual_address == NULL) {
printk(KERN_ERR "%s ERROR: Could not remap BAR0(0x%#lX,%ld bytes).\n",
DRV_NAME,drv_data->physical_address, drv_data->length);
return -ENOMEM;
}
if(request_mem_region(drv_data->physical_address, drv_data->length, drv_data->device_name)==NULL) {
printk(KERN_ERR "%s ERROR: Memory range %#lX-%#lX could not be allocated.\n",
DRV_NAME,drv_data->physical_address,drv_data->physical_address+drv_data->length-1);
return -EBUSY;
}
/*Test Code*/
ptr = drv_data->virtual_address;
for(i=0;i<1024*1024;i++) {
*ptr++=i;
}
ptr = drv_data->virtual_address;
for(i=0;i<1024*1024;i++) {
if(i%16 ==0 && i!=0) printk("\n");
printk("%.2X ",*ptr++);
}
The IOCTL write from user space to device physical space is done via:
err = !access_ok(VERIFY_READ, (void __user *)arg, sizeof(struct pci_data));
if(err) return -EFAULT;
udata = (struct pci_data *)arg;
get_user(offset,&udata->addr);
if(offset+64 >= drv_data->length)
return -EFAULT;
kmem = drv_data->virtual_address;
kmem += offset;
//memset(dummy,0,64); //If i replace dummy with kmem, this works
notcopied=copy_from_user(kmem, udata->data, 64); //returns 0, memory not set
//memcpy(kmem,dummy,64); //if I replace kmem with dummy in copy_from_user above, this works
printk("%s Wrote %.2X to %#lX (%#lX), didn't copy %d bytes (%.16lX, %.16lX, %.16lX).\n",DRV_NAME,t,offset,(unsigned long)kmem,notcopied,udata->data, &udata->data, &udata->data[0]);
ret = 0;
break;
Then I read the data out with this ioctl command: (This code works as I expect)
err = !access_ok(VERIFY_WRITE, (void __user *)arg, sizeof(struct pci_data));
if(err) return -EFAULT;
udata = (struct pci_data *)arg;
get_user(offset,&udata->addr);
if(offset+64 >= drv_data->length)
return -EFAULT;
kmem = drv_data->virtual_address;
kmem += offset;
notcopied=copy_to_user(udata->data, kmem, 64);
printk("%s Reading 64 bytes from %#lx (%#lX), first value %.2X. Didn't copy %d bytes.\n",DRV_NAME,offset,(unsigned long)kmem,*kmem,notcopied);
ret = 0;
break;
|