I’m currently using a PCIe driver on a Jetson Nano for DMA operations. However, when using copy_from_user() to copy a struct, the function always fails, returning a value equal to the full byte size of the struct.
I monitored the kernel logs with dmesg -w
, but aside from my printk messages, nothing unusual appears.
My Environment:
hardware version: Jetson nano b01 4gb
system version : ubuntu 18.04
kernel verison : 4.9.140-tegra
Here’s my code:
in main.c:
typedef struct DMA_DATA
{
unsigned char read_buf[4096];
unsigned char write_buf[4096];
} DMA_DATA;
typedef struct DMA_OPERATION
{
unsigned int current_len;
unsigned int offset_addr;
unsigned int cmd;
DMA_DATA data;
} DMA_OPERATION;
DMA_OPERATION dma_operation;
DMA_OPERATION *dma_oper = &dma_operation;// ← the struct that I want to copy
int fd;
int main(int argc, char *argv)
{
pthread_t t1, t2;
pthread_attr_t attr1, attr2;
struct sched_param sch1, sch2;
fd = open_pci_driver();
if (fd < 0)
{
return -1;
}
pthread_attr_init(&attr2);
sch2.sched_priority = 90;
pthread_attr_setinheritsched(&attr2, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&attr2, SCHED_FIFO);
pthread_attr_setschedparam(&attr2, &sch2);
pthread_create(&t2, &attr2, thread_func2, NULL);
}
void *thread_func2(void *ptr)
{
int len;
int i;
struct timeval starttime, endtime;
long timeuse = 0;
long recv_write_time = 0;
long recv_read_time = 0;
int k;
int r;
printf("dma number test start\n");
printf("fd=%d\n", fd);
attach_cpu(0);
printf("DMA_OPERATION sizeof = %ld\n",sizeof(DMA_OPERATION));
r = ioctl(fd, PCI_MAP_ADDR_CMD, dma_oper); //<----fails here
......
}
in the driver file
pci_driver.h:
typedef struct DMA_DATA
{
unsigned char read_buf[4096];
unsigned char write_buf[4096];
}DMA_DATA;
typedef struct DMA_OPERATION
{
unsigned int current_len;
unsigned int offset_addr;
unsigned int cmd;
DMA_DATA data;
}DMA_OPERATION;
DMA_OPERATION dma_operation;
in pci_driver.c:
long cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) // <---- User-implemented ioctl
{
struct PciPango *pci_pango = &pci_info;
unsigned int value = 0;
unsigned int current_len = 0;
switch (cmd)
{
.......
case PCI_MAP_ADDR_CMD:
{
int ret;
spin_lock(&dma_info.addr_r.lock);
if (!access_ok(VERIFY_READ, (DMA_OPERATION *)arg, sizeof(DMA_OPERATION)))
{
printk(KERN_ERR "EFAULT-access_ok failed!\n");
return -EFAULT;
} //<--- access_ok check didn't fail
ret = copy_from_user(&dma_operation, (DMA_OPERATION *)arg, sizeof(DMA_OPERATION)); // <---- this function failed to copy the struct
if (ret != 0)
{
printk(KERN_ERR "EFAULT-copy_from_user Error,ret=%d\n", ret); // <----- ret always equal sizeof(DMA_OPERATION)
spin_unlock(&dma_info.addr_r.lock);
return -EFAULT;
}
if (dma_operation.current_len <= 0 || dma_operation.current_len > DMA_MAX_PACKET_SIZE)
{
printk(KERN_ERR "EINVAL-current_len illegal\n");
spin_unlock(&dma_info.addr_r.lock);
return -EINVAL;
}
dma_info.addr_r.data_buf = pci_alloc_consistent(op_dev,
dma_operation.current_len,
&dma_info.addr_r.addr);
if (!dma_info.addr_r.data_buf)
{
printk(KERN_ERR "ENOMEM-DMA read buffer is not allocated\n");
spin_unlock(&dma_info.addr_r.lock);
return -ENOMEM;
}
dma_info.addr_w.data_buf = pci_alloc_consistent(op_dev,
dma_operation.current_len,
&dma_info.addr_w.addr);
if (!dma_info.addr_w.data_buf)
{
printk(KERN_ERR "ENOMEM-DMA write buffer is not allocated\n");
pci_free_consistent(op_dev, dma_operation.current_len,
dma_info.addr_r.data_buf, dma_info.addr_r.addr);
spin_unlock(&dma_info.addr_r.lock);
return -ENOMEM;
}
dma_info.addr_r.addr_size = ((dma_info.addr_r.addr >> 32) > 0) ? 1 : 0;
dma_info.addr_w.addr_size = ((dma_info.addr_w.addr >> 32) > 0) ? 1 : 0;
if (dma_operation.current_len < 4)
{
printk(KERN_ERR "EINVAL-current_len illegal\n");
pci_free_consistent(op_dev, dma_operation.current_len,
dma_info.addr_w.data_buf, dma_info.addr_w.addr);
pci_free_consistent(op_dev, dma_operation.current_len,
dma_info.addr_r.data_buf, dma_info.addr_r.addr);
spin_unlock(&dma_info.addr_r.lock);
return -EINVAL;
}
dma_info.cmd.data.length = (dma_operation.current_len >> 2) - 1;
dma_info.cmd.data.addr_type = dma_info.addr_r.addr_size;
spin_unlock(&dma_info.addr_r.lock);
break;
}
......
}
return 0;
}
related dmesg log:
[ 56.892131] init cdev class result : 0 // <— begin of program
[ 66.925193] EFAULT-copy_from_user Error,ret=8208 // <— printk in ioctl
[ 66.930349] DMA write buffer is not allocated // <— printk in ioctl
[ 67.935897] pango_cdev_release. // <— end of program
I wonder if its a problem related to the kernel version(4.9.140-tegra) or PAN/UAO mechanism of ARMv8 . I tried to build the kernel with PAN/UAO compile option off , but that didn’t solve my problem either.