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

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM