Firstly a bit of context about the machine I'm working on (it's a SOM with a SoC that includes a FPGA and two CPU, and on one of them there is a linux OS running).
Characteristics:
I have a custom peripheral implemented on the FPGA that provides a standard AXI4 interface to a BRAM memory (thanks to this component). This peripheral is registered in my device-tree and is accessible through the generic-uio driver of the kernel (more documentation about this).
The size of the BRAM memory is 32kB. Here are the mapping characteristics that appear in /sys/class/uio/uioX/maps/map0/
:
I'm working on a C code that simply aims to read this BRAM memory and log what is inside in a text file. In order to do so I use mmap
(here is its man page) to create some memory projections of the file located in /dev/uioX
(that corresponds to my device).
I want to use only the file pages I need, more precisely mapping the file page per page. Here is the function I use to create the mapping of one page:
/**
* @brief Get a pointer to the corresponding file page
*
* @param filePage file page number
* @param fd pointer where the file descriptor will be stored
* @return int*
*/
int *getFilePagePointer(int *fd, unsigned int filePage)
{
// open driver file
*fd = open("/dev/uio1", O_RDWR);
if (*fd == -1)
{
int errsv = errno;
fprintf(stderr, "Error while opening /dev/uio1 : errno %d (%s).\n", errsv, strerror(errsv));
exit(EXIT_FAILURE);
}
// choose projection parameters
int length = sysconf(_SC_PAGE_SIZE);
int offset = filePage * sysconf(_SC_PAGE_SIZE);
// create mapping
void *filePagePointer = mmap((void *)NULL, (size_t)length, PROT_READ, MAP_PRIVATE, *fd, (off_t)offset);
if (filePagePointer == MAP_FAILED)
{
int errsv = errno;
fprintf(stderr, "Error while mapping file page : errno %d (%s).\n", errsv, strerror(errsv));
exit(EXIT_FAILURE);
}
return (int *)filePagePointer;
}
And then the issue: it works perfectly well if filePage
(i.e. offset) is 0
but if it is strictly positive, it fails with errno = 22
.
Unless I missed something in the man page, all parameters are valid (offset
is a multiple of sysconf(_SC_PAGE_SIZE)
, addr
is NULL
, length
is strictly positive and not too large, and flags are correct).
So, why an EINVAL ?...
kernel driver does
static int uio_find_mem_index(struct vm_area_struct *vma)
{
struct uio_device *idev = vma->vm_private_data;
if (vma->vm_pgoff < MAX_UIO_MAPS) {
if (idev->info->mem[vma->vm_pgoff].size == 0)
return -1;
return (int)vma->vm_pgoff;
}
return -1;
}
So it seems, offset
is the map index (like in map0
) but not the position within the memory.
You have to mmap()
the whole memory.