linux 应用程序直接读写寄存器或物理内存
1.程序说明:
调试驱动程序时,经常遇到候需要查看或设置寄存器的情况,但是直接更改内核代码又不方便。
这里提供一个应用程序源码能在应用层访问底层寄存器。(网上找到的,进行过更改)。
这里只提供4字节数据的访问,如果需要其他字节宽度则需要更改代码。
line40 增加了O_DSYNC标志,防止cache导致数据写入不及时。
2.应用程序源码
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <time.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7 #include <stdint.h>
8 #include <sys/mman.h>
9 #include <errno.h>
10
11 static int dev_fd;
12 int main(int argc, char **argv)
13 {
14 uint32_t addr, value, map_size,base;
15 void *align_addr;
16 int flag = 0;
17 int i;
18 unsigned char *map_base;
19
20 if (argc != 5) {
21 printf("usage: m_reg r base address readcount\n");
22 printf("example: m_reg r 0x50008000 20 1\n");
23 printf("usage: m_reg w base address writevalue\n");
24 printf("example: m_reg w 0x50008000 20 0xffffffff\n");
25 return -1;
26 }
27
28 if (argv[1][0] == 'r') {
29 flag = 0;
30 value = strtol(argv[4], NULL, 10);
31 }
32 else {
33 flag = 1;
34 value = strtol(argv[4], NULL, 16);
35 }
36
37 base = strtol(argv[2], NULL, 16);
38 addr = strtol(argv[3], NULL, 16);
39
40 dev_fd = open("/dev/mem", O_RDWR | O_NDELAY | O_DSYNC);
41
42 if (dev_fd < 0)
43 {
44 printf("open(/dev/mem) failed.");
45 return 0;
46 }
47
48 addr &= ~0x3;
49 align_addr = addr & ~0xff;
50
51 map_size = addr + 0x100;
52 printf("map base:0x%x size:0x%x\n",base,map_size);
53 map_base = (unsigned int * )mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev_fd, base);
54 if(map_base == -1)
55 {
56 printf("map err %d\n",errno);
57 perror("errno:");
58 return -1;
59 }
60
61 if (flag == 0) {
62
63 for (i = 0; i < value; i++) {
64 if (i % 4 == 0) {
65 printf("\n");
66 printf("0x%08x\t", addr+i*4);
67 }
68 printf("%08x ", *(volatile unsigned int *)(map_base+addr+i*4));
69 }
70 printf("\n");
71
72 }
73 else {
74 printf("0x%08x\t value:0x%x", addr,value);
75 *(volatile unsigned int *)(map_base + addr) = value;
76 }
77 printf("\n");
78
79 if(dev_fd)
80 close(dev_fd);
81
82 munmap((unsigned int *)map_base, map_size);
83
84 return 0;
85 }
3. 内核设备文件实现
内核源码位置:/drivers/char/mem.c
/dev/mem设备文件mmap函数实现:
static int mmap_mem(struct file *file, struct vm_area_struct *vma) { size_t size = vma->vm_end - vma->vm_start; phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; /* It's illegal to wrap around the end of the physical address space. */ if (offset + (phys_addr_t)size - 1 < offset) return -EINVAL; if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) return -EINVAL; if (!private_mapping_ok(vma)) return -ENOSYS; if (!range_is_allowed(vma->vm_pgoff, size)) return -EPERM; if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size, &vma->vm_page_prot)) return -EINVAL; vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, size, vma->vm_page_prot); vma->vm_ops = &mmap_mem_ops; /* Remap-pfn-range will mark the range VM_IO */ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, size, vma->vm_page_prot)) { return -EAGAIN; } return 0; }
phys_mem_access_prot函数获得映射内存访问权限
static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, unsigned long size, pgprot_t vma_prot) { #ifdef pgprot_noncached phys_addr_t offset = pfn << PAGE_SHIFT; if (uncached_access(file, offset)) return pgprot_noncached(vma_prot); #endif return vma_prot; }
O_DSYNC标志将会使用不带cache的映射方式,该标志在打开设备文件时指定。
static int uncached_access(struct file *file, phys_addr_t addr) { /* * Accessing memory above the top the kernel knows about or through a * file pointer * that was marked O_DSYNC will be done non-cached. */ if (file->f_flags & O_DSYNC) return 1; return addr >= __pa(high_memory); }