如何在应用层通过进行地址映射,读写pcie bar
mmap函数说明
mmap 函数是 Unix 和 Linux 操作系统中的一个重要系统调用,用于将文件或者其它对象映射到进程的地址空间中。映射完成后,进程可以通过指针直接访问这些映射区域,就像访问普通的内存一样。这种方式可以提高文件 I/O 的效率,并支持内存映射文件的共享。
函数原型
mmap 的函数原型通常如下:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数说明
• addr: 指定映射区域的起始地址。如果设置为 NULL,则由系统自动选择一个合适的地址。
• length: 映射区域的长度(以字节为单位)。
• prot: 映射区域的保护标志,可以是以下标志的组合:
• PROT_READ: 区域可读。
• PROT_WRITE: 区域可写。
• PROT_EXEC: 区域可执行。
• PROT_NONE: 区域不可访问。
• flags: 映射区域的标志,可以是以下标志的组合:
• MAP_SHARED: 与文件共享映射,对映射区域的更改会被写回文件。
• MAP_PRIVATE: 私有映射,对映射区域的更改不会写回文件,而是创建一个写时复制(copy-on-write)的副本。
• MAP_ANONYMOUS 或 MAP_ANON: 匿名映射,映射不与任何文件关联。
• MAP_FIXED: 必须将映射区域映射到 addr 指定的地址,否则失败。
• MAP_POPULATE: 立即读取文件数据,而不是延迟加载。
• 其他系统特定的标志。
• fd: 文件描述符,通常是从 open 获得的。如果使用匿名映射,则设为 -1。
• offset: 文件映射的偏移量(以字节为单位)。
返回值
mmap 函数成功时返回映射区域的起始地址,如果失败则返回 MAP_FAILED(通常定义为 (void *) -1),并通过 errno 设置错误代码。
示例
假设有一个文件描述符 fd,我们想映射文件的一部分到内存中,可以这样做:
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
int main() {
int fd;
char *mapped_addr;
// 打开文件
fd = open("example.txt", O_RDWR);
if (fd == -1) {
perror("open");
return 1;
}
// 映射文件
mapped_addr = (char *)mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped_addr == MAP_FAILED) {
perror("mmap");
close(fd);
return 1;
}
// 使用映射的内存
printf("Mapped data at address: %p\n", mapped_addr);
// 可以直接修改或读取mapped_addr指向的内容
// 取消映射
munmap(mapped_addr, 4096);
// 关闭文件描述符
close(fd);
return 0;
}
注意事项
• 当使用 mmap 进行文件映射时,文件必须是可寻址的,即文件大小必须小于或等于映射区域的大小。
• 对于私有映射(MAP_PRIVATE),映射区域的更改不会直接写回到文件中,而是创建一个副本。如果需要将更改写回文件,可以使用 msync 函数。
• 使用完毕后,需要调用 munmap 函数来取消映射,释放资源。
mmap 是一种高效的数据访问方式,特别是在处理大型文件或共享内存时非常有用。
接下来贴一个通过mmap读写pcie bar的小程序,仅供参考:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <libgen.h>
#include <sys/mman.h>
#define MEM_DEBUG 0
#if MEM_DEBUG==1
#define open(f, o, m) 1
#define close(fd) 0
#define lseek(fd, off, opt) 1
#define read(fd, buf, size) strncpy((void *)buf,"0123456789abcdef", size)
#endif
#define ALIGN(p, n) (((p) & ~(n-1)))
#define ISPRINT(c) ((c) >= 0x20 && (c) < 0x7f)
typedef struct {
void *addr;
size_t length;
} my_mmap_t;
static void *
my_mmap(my_mmap_t *m, void *addr, size_t length, int prot, int flags,
int fd, off_t org_offset)
{
off_t offset;
off_t diff;
size_t pgsize;
void *p;
pgsize = sysconf(_SC_PAGE_SIZE);
offset = ALIGN(org_offset, pgsize);
diff = org_offset - offset;
if ((p = mmap(addr, length + diff, prot, flags, fd, offset)) == MAP_FAILED)
return p;
m->addr = p;
m->length = length + diff;
return (char *)p + diff;
}
static int
my_munmap(my_mmap_t *m)
{
return munmap(m->addr, m->length);
}
static void
memdump(u_int8_t *buf, int offset, size_t bytes, int width)
{
int last = (offset&0xf)+bytes;
int i, j, total=ALIGN(last+0xf, 16);
u_int8_t *bp;
for (i=0; i<total; i+=16) {
printf("%08x: ", ALIGN(offset+i, 16));
bp = buf + i - (offset&0xf);
for (j=0; j<16; j+=width) {
if (i+j<(offset&0xf) || i+j>=last) {
printf("%*s ", width*2, "");
continue;
}
switch (width) {
case 1:
printf("%02x", *(u_int8_t *) (bp + j));
break;
case 2:
printf("%04x", *(u_int16_t *) (bp + j));
break;
case 4:
printf("%08x", *(u_int32_t *) (bp + j));
break;
}
printf(" ");
}
printf("|");
for (j=0; j<16; j++) {
if (i+j<(offset&0xf) || i+j>=last) {
printf(" ");
continue;
}
printf("%c", ISPRINT(bp[j]) ? bp[j] : '.');
}
printf("\n");
}
}
static void
read_memory(int fd, u_int64_t s_addr, int len, int size)
{
my_mmap_t m;
void *addr;
addr = my_mmap(&m, 0, len, PROT_READ, MAP_FILE|MAP_SHARED, fd, s_addr);
if (addr==MAP_FAILED){
perror("mmap");
return;
}
memdump(addr, s_addr, len, size);
my_munmap(&m);
return;
}
static
read_memory_result(int fd, u_int64_t s_addr, int len, int size)
{
my_mmap_t m;
void *addr;
addr = my_mmap(&m, 0, len, PROT_READ, MAP_FILE|MAP_SHARED, fd, s_addr);
if (addr==MAP_FAILED){
perror("mmap");
return;
}
memdump(addr, s_addr, len, size);
my_munmap(&m);
return;
}
static void
write_memory(int fd, u_int64_t s_addr, u_int32_t val, int size, int total)
{
my_mmap_t m;
int i;
void *addr;
u_int8_t val_8 = val;
u_int16_t val_16 = val;
u_int32_t val_32 = val;
if (s_addr!=ALIGN(s_addr, size)){
printf("miss align %d %d\n", s_addr, size);
return;
}
printf("Write 0x%llx-0x%llx 0x%x %d\n",
s_addr, ALIGN(s_addr+total+size-1, size), val, size);
addr = my_mmap(&m, 0, total, PROT_READ|PROT_WRITE, MAP_SHARED, fd, s_addr);
if (addr==MAP_FAILED){
perror("mmap");
return;
}
for (i=0; i<total; i+=size) {
switch (size) {
case 1:
*(u_int8_t *)((u_int64_t)addr+i) = val_8;
break;
case 2:
*(u_int16_t *)((u_int64_t)addr+i) = val_16;
break;
case 4:
*(u_int32_t *)((u_int64_t)addr+i) = val_32;
break;
}
}
my_munmap(&m);
}
static void
and_memory(int fd, u_int64_t s_addr, u_int32_t val, int size)
{
my_mmap_t m;
void *addr;
if (s_addr!=ALIGN(s_addr, size)){
printf("miss align %d %d\n", s_addr, size);
return;
}
printf("AND 0x%x ", s_addr);
addr = my_mmap(&m, 0, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, s_addr);
if (addr==MAP_FAILED){
perror("mmap");
return;
}
switch (size) {
case 1:
printf("0x%02x->0x%02x\n",
*(u_int8_t *)addr, *(u_int8_t *)addr&(u_int8_t)val );
*(u_int8_t *)addr &= (u_int8_t)val;
break;
case 2:
printf("0x%04x->0x%04x\n",
*(u_int16_t *)addr, *(u_int16_t *)addr&(u_int16_t)val );
*(u_int16_t *)addr &= (u_int16_t)val;
break;
case 4:
printf("0x%08x->0x%08x\n",
*(u_int32_t *)addr, *(u_int32_t *)addr&(u_int32_t)val );
*(u_int32_t *)addr &= (u_int32_t)val;
break;
}
my_munmap(&m);
}
static void
or_memory(int fd, u_int64_t s_addr, u_int32_t val, int size)
{
my_mmap_t m;
void *addr;
if (s_addr!=ALIGN(s_addr, size)){
printf("miss align %d %d\n", s_addr, size);
return;
}
printf("OR 0x%x ", s_addr);
addr = my_mmap(&m, 0, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, s_addr);
if (addr==MAP_FAILED){
perror("mmap");
return;
}
switch (size) {
case 1:
printf("0x%02x->0x%02x\n",
*(u_int8_t *)addr, *(u_int8_t *)addr|(u_int8_t)val );
*(u_int8_t *)addr |= (u_int8_t)val;
break;
case 2:
printf("0x%04x->0x%04x\n",
*(u_int16_t *)addr, *(u_int16_t *)addr|(u_int16_t)val );
*(u_int16_t *)addr |= (u_int16_t)val;
break;
case 4:
printf("0x%08x->0x%08x\n",
*(u_int32_t *)addr, *(u_int32_t *)addr|(u_int32_t)val );
*(u_int32_t *)addr |= (u_int32_t)val;
break;
}
my_munmap(&m);
}
#undef ALIGN
static int count=0;
static u_int32_t pattern[] =
{0x00000000, 0xaaaaaaaa, 0x55555555,
0xa5a5a5a5, 0x5a5a5a5a, 0xffffffff};
static void
signal_handler_usr1(int sig)
{
printf("Signal %d catched\n", sig);
printf("Current count %d pattern %x\n",
count, pattern[count%(sizeof(pattern)/sizeof(u_int32_t))]);
}
static void
test_memory(int fd, u_int32_t size)
{
u_int32_t *addr;
int i;
if ((addr = malloc(size))==NULL) {
perror("malloc:");
return;
}
while (1) {
for (i=0; i<sizeof(u_int32_t); i++) {
*(addr+i) = pattern[count%(sizeof(pattern)/sizeof(u_int32_t))];
}
count++;
}
}
static void
verify_memory(int fd, u_int32_t s_addr, int val, int size)
{
my_mmap_t m;
u_int8_t *addr;
int i;
addr = my_mmap(&m, 0, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, s_addr);
if (addr==MAP_FAILED){
perror("mmap");
return;
}
for (i=0; i<size; i++) {
if (*(addr+i)!=val) {
printf("Addr %p %x!=%x\n", addr+i, *(addr+i), val);
}
}
my_munmap(&m);
}
static void
usage(char *com)
{
fprintf(stderr, "%s: read <addr> <length> [<1,2 or 4>]\n", com);
fprintf(stderr, "%*s write <addr> <val> [<1,2 or 4> [<repeat>]]\n",
strlen(com), " ");
fprintf(stderr, "%*s and <addr> <val> [<1,2 or 4>]\n", strlen(com), " ");
fprintf(stderr, "%*s or <addr> <val> [<1,2 or 4>]\n", strlen(com), " ");
fprintf(stderr, "%*s test <size>\n", strlen(com), " ");
fprintf(stderr, "%*s verify <addr> <val> <size>\n", strlen(com), " ");
}
int
main(int argc, char *argv[])
{
int fd;
int len;
u_int64_t addr;
u_int32_t val;
int size=1;
int total=1;
if (argc<3) {
usage(basename(argv[0]));
return 1;
}
if ((fd=open("/dev/mem", O_RDWR, 0))<0) {
perror("open");
return 1;
}
addr = strtoul(argv[2], NULL, 0);
if (argc>4) {
size = strtol(argv[4], NULL, 0);
if (size!=1 && size!=2 && size!=4 && strncmp(argv[1], "verify", strlen(argv[1])!=0)) {
size = 1;
}
}
if (strncmp(argv[1], "read", strlen(argv[1]))==0) {
len = strtoul(argv[3], NULL, 10);
read_memory(fd, addr, len, size);
}
else if (strncmp(argv[1], "write", strlen(argv[1]))==0) {
val = strtoul(argv[3], NULL, 0);
if (argc>5) {
total = strtoul(argv[5], NULL, 0);
}
else {
total = 1;
}
write_memory(fd, addr, val, size, total);
}
else if (strncmp(argv[1], "and", strlen(argv[1]))==0) {
val = strtoul(argv[3], NULL, 0);
and_memory(fd, addr, val, size);
}
else if (strncmp(argv[1], "or", strlen(argv[1]))==0) {
val = strtoul(argv[3], NULL, 0);
or_memory(fd, addr, val, size);
}
else if (strncmp(argv[1], "test", strlen(argv[1]))==0) {
signal(SIGUSR1, signal_handler_usr1);
addr = (addr/4)*4;
test_memory(fd, addr);
}
else if (strncmp(argv[1], "verify", strlen(argv[1]))==0) {
val = strtoul(argv[3], NULL, 0);
size = strtoul(argv[4], NULL, 0);
verify_memory(fd, addr, val, size);
}
close(fd);
return 0;
}