I am trying to read some physical address that is mapped to PCIe device using /dev/mem. The PCIe device is mapped to 0x387ffa000000
:
bash# lspci -s 1a:00.0 -v | grep Memory
Memory at 387ffa000000 (64-bit, prefetchable) [size=32M]
So I wrote some program that's based on what I found here at SO which makes use of mmap():
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Usage: %s <phys_addr> <offset>\n", argv[0]);
return 0;
}
off_t offset = strtoul(argv[1], NULL, 0);
size_t len = strtoul(argv[2], NULL, 0);
size_t pagesize = sysconf(_SC_PAGE_SIZE);
off_t page_base = (offset / pagesize) * pagesize;
off_t page_offset = offset - page_base;
int fd = open("/dev/mem", O_SYNC);
if (fd < 0) {
perror("Can't open");
return 1;
}
unsigned char *mem = mmap(NULL, page_offset len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, page_base);
if (mem == MAP_FAILED) {
perror("Can't map memory");
return 1;
}
size_t i;
for (i = 0; i < len; i) printf("x ", (int)mem[page_offset i]);
printf("\n");
return 0;
}
Although this works:
bash# ./mem_read.out 0x387ffa000000 0x10
00 1f 00 18 00 05 07 08 00 00 00 00 00 00 00 00
I wanted to try the same only with read()/write() calls instead of mmap().
But when I tried this:
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Usage: %s <phys_addr> <size>\n", argv[0]);
return 1;
}
off_t offset = strtoul(argv[1], NULL, 0);
size_t len = strtoul(argv[2], NULL, 0);
if (!len) {
printf("size argument can't be 0\n");
return 1;
}
int fd = open("/dev/mem", O_SYNC);
if (fd < 0) {
perror("Can't open");
return 1;
}
if (lseek(fd, offset, SEEK_SET) < 0) {
perror("Can't seek");
return 1;
}
char* buff = (char*)malloc(len * sizeof(char) 1);
if (read(fd, buff, len) < 0) {
perror("Can't read");
return 1;
}
printf("%s\n", buff);
return 0;
}
It fails with:
bash# ./mem_read.out 0x387ffa000000 0x10
Can't read: Bad address
That same code works when instead of /dev/mem
I use a regular text file.
Does anyone have an idea why the former works but the latter does not?
Just for additional context, "Bad address" is printed when errno == EFAULT
, which returned by read()
when:
buf is outside your accessible address space.
which makes no sense to me.
CodePudding user response:
Your open()
call:
int fd = open("/dev/mem", O_SYNC);
Here's that the manual page says:
The argument flags must include one of the following access modes: O_RDONLY, O_WRONLY, or O_RDWR. These request opening the file read-only, write-only, or read/write, respectively.
So I would expect that to have problems reading, or even opening. It could be that O_RDONLY
is 0-valued, I haven't checked but formally your code is missing the mode.
CodePudding user response:
Looking at the code for the /dev/mem
device, it seems to check that the address is a valid physical address.