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); }