zoukankan      html  css  js  c++  java
  • Linux 读写memory操作,devmem直接访问物理内存地址【转】

    转自:https://blog.csdn.net/xhoufei2010/article/details/99305356

    1 说明

    由于开发需要,需要通过memory传输数据,所以使用devmem 方式读写数据,操作linux 内存数据。devmem的方式是提供给驱动开发人员,在应用层能够侦测内存地址中的数据变化,以此来检测驱动中对内存或者相关配置的正确性验证。

    2 开发环境

    软件环境: ubuntu 虚拟机、arm-xilinx 交叉编译工具链
    硬件环境: ZYNQ7010

    3 内存地址说明

    基本上的内存物理地址都可以访问,但是如果需要ZYNQ的PS 和PL 都能读写数据,需要查看芯片的datasheet,确定哪个地址可以互相读写数据。
    通过《ug585-Zynq-7000-TRM.pdf》 的 29章表格“Table 29‐1: Initial OCM/DDR Address Map” 可以得到地址分配。
    图3-1 ZYNQ7010地址
    表3-1 ZYNQ7010 芯片地址分配
    从表格3-1得知,DDR的物理地址对应为 0x0010_0000 - 3FFF_FFFF

    4 devmem 工具

    工具的原理也比较简单,就是应用程序通过mmap函数实现对/dev/mem驱动中mmap方法的使用,映射了设备的内存到用户空间,实现对这些物理地址的读写操作。
    代码如下:

    /**
     * @addtogroup module_devmem
     * @{
     */
    
     /**
     * @file
     * @brief 内存管理工具,仿照标准linux devmem 工具进行改良,可以自由读写linux内存数据。
     * @details 驱动接口。
     * @version 1.1.0
     * @author sky.houfei
     * @date 2019-8-6
     */
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    #include <signal.h>
    #include <fcntl.h>
    #include <ctype.h>
    #include <termios.h>
    #include <sys/types.h>
    #include <sys/mman.h>
    #include <stdbool.h>
    
    //*****************************************************************************
    #define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]
    ", 
      __LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)
     
    #define MAP_SIZE 4096UL  			//映射的内存区大小(一般为一个叶框大小)
    #define MAP_MASK (MAP_SIZE - 1)   	//MAP_MASK = 0XFFF
    
    //*****************************************************************************
    /**
    * @brief 直接写入到内存实际的物理地址。
    * @details 通过 mmap 映射关系,找到对应的内存实际物理地址对应的虚拟地址,然后写入数据。
    * 写入长度,每次最低4字节
    * @param[in] writeAddr, unsigned long, 需要操作的物理地址。
    * @param[in] buf,unsigned long *, 需要写入的数据。
    * @param[in] len,unsigned long, 需要写入的长度,4字节为单位。
    * @return ret, int, 如果发送成功,返回0,发送失败,返回-1。
    */
    static int Devmem_Write(unsigned long writeAddr, unsigned long* buf, unsigned long len)
    {
    	int i = 0;
    	int ret = 0;
        int fd;
    	void *map_base, *virt_addr; 
    	unsigned long addr = writeAddr;
        int offset_len = 0;
    
    	if(len == 0)
    	{
            printf("%s %s %d, len = 0
    ", __FILE__, __func__, __LINE__);
            return -1;
        }
    	
    	if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
    	{
    		fprintf(stderr, "Error at line %d, file %s (%d) [%s]
    ", __LINE__, __FILE__, errno, strerror(errno));
    		return -1;
        }
    	
    	/* Map one page */ //将内核空间映射到用户空间
        map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr & ~MAP_MASK);
        if(map_base == (void *) -1)
    	{
    		fprintf(stderr, "Error at line %d, file %s (%d) [%s]
    ", __LINE__, __FILE__, errno, strerror(errno));
    		close(fd);
    		return -1;
        }
    	
    		// 发送实际数据内容
     	for (i = 0; i < len; i++)
     	{
    		// 翻页处理
            if(offset_len >= MAP_MASK)
            { 
                offset_len = 0;
                if(munmap(map_base, MAP_SIZE) == -1)
            	{
            		fprintf(stderr, "Error at line %d, file %s (%d) [%s]
    ", __LINE__, __FILE__, errno, strerror(errno));
            		close(fd);
            		return -1;
            	}
                map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr & ~MAP_MASK);
                if(map_base == (void *) -1)
            	{
            		fprintf(stderr, "Error at line %d, file %s (%d) [%s]
    ", __LINE__, __FILE__, errno, strerror(errno));
            		close(fd);
            		return -1;
                }
                printf("map_base over 4k = [%p].
    ", map_base);		// 翻页打印提示
            }
        
    		virt_addr = map_base + (addr & MAP_MASK);	// 映射地址
    		*((unsigned long *) virt_addr) = buf[i]; 	// 写入数据
    		addr += 4;
            offset_len += 4;
    	}
    	
    	    
    	if(munmap(map_base, MAP_SIZE) == -1)
    	{
    		fprintf(stderr, "Error at line %d, file %s (%d) [%s]
    ", __LINE__, __FILE__, errno, strerror(errno));
    		return -1;
    	}
    	
        close(fd);
    	return 0;
    }
    
    
    /**
    * @brief 从实际物理地址读取数据。
    * @details 通过 mmap 映射关系,找到对应的实际物理地址对应的虚拟地址,然后读取数据。
    * 读取长度,每次最低4字节。
    * @param[in] readAddr, unsigned long, 需要操作的物理地址。
    * @param[out] buf,unsigned char *, 读取数据的buf地址。
    * @param[in] bufLen,unsigned long , buf 参数的容量,4字节为单位,如 unsigned long buf[100],那么最大能接收100个4字节。
    * 用于避免因为buf容量不足,导致素组越界之类的软件崩溃问题。
    * @return len,unsigned long, 读取的数据长度,字节为单位。如果读取出错,则返回0,如果正确,则返回对应的长度。
    */
    static int Devmem_Read(unsigned long readAddr, unsigned long* buf, unsigned long len)
    {
    	int i = 0;
        int fd,ret;
        int offset_len = 0;
        void *map_base, *virt_addr; 
    	off_t addr = readAddr;
    	unsigned long littleEndianLength = 0;
    	
    	if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
    	{
    		fprintf(stderr, "Error at line %d, file %s (%d) [%s]
    ", __LINE__, __FILE__, errno, strerror(errno));
    		return 0;
        }
    	
        /* Map one page */ //将内核空间映射到用户空间
        map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr & ~MAP_MASK);
        if(map_base == (void *) -1)
    	{
    		fprintf(stderr, "Error at line %d, file %s (%d) [%s]
    ", __LINE__, __FILE__, errno, strerror(errno));
    		close(fd);
    		return 0;
        }
    	
    	for (i = 0; i < len; i++)
     	{
    		// 翻页处理
            if(offset_len >= MAP_MASK)
            {
                offset_len = 0;
                if(munmap(map_base, MAP_SIZE) == -1)
            	{
            		fprintf(stderr, "Error at line %d, file %s (%d) [%s]
    ", __LINE__, __FILE__, errno, strerror(errno));
            		close(fd);
            		return 0;
            	}
                map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr & ~MAP_MASK);
                if(map_base == (void *) -1)
            	{
            		fprintf(stderr, "Error at line %d, file %s (%d) [%s]
    ", __LINE__, __FILE__, errno, strerror(errno));
            		close(fd);
            		return 0;
                }
            }
        
    		virt_addr = map_base + (addr & MAP_MASK);	// 将内核空间映射到用户空间操作
    		buf[i] = *((unsigned long *) virt_addr);	// 读取数据
     		addr += 4;
            offset_len += 4;
    	}
    	
    	if(munmap(map_base, MAP_SIZE) == -1)
    	{
    		fprintf(stderr, "Error at line %d, file %s (%d) [%s]
    ", __LINE__, __FILE__, errno, strerror(errno));
    		close(fd);
    		return 0;
    	}
        close(fd);
    	return i;
    }
    
    
    static void Devmem_usage(void)
    {
    	printf("Usage ./devmem_tool read/write 0xfa0000 20
    ");
    	printf("The read/write is the command type, read or write data to memory
    ");
    	printf("The 0xfa2000 is the memory physical address
    ");
    	printf("The 20 is read/write data length, uint is 4 byte, so it is total 20 * 4 = 80 bytes
    ");
    	printf("Usage:  ./devmem_tool read 0xfa0000 20
    ");
    	printf("Usage:  ./devmem_tool write 0xfa0000 20
    ");
    }
    
    
    /**
    * @brief 内存工具主函数。
    * @details 操作方法,请参考Devmem_usage 函数。
    * 读取内存数据: ./devmem_tool read 0xfa0000 20
    * 读取内存物理地址 0xfa0000作为起始地址,一共读取20个4字节,共计 20 * 4 = 80 字节。
    * 写入内存数据: ./devmem_tool write 0xfa0000 20
    * 写入内存物理地址 0xfa0000作为起始地址,一共写入20个4字节,共计 20 * 4 = 80 字节。
    */
    int main(int argc, char** argv)
    {
    	unsigned long len = 0;
    	unsigned long writeData[8192];
    	unsigned long readData[8192];
    	unsigned long addr = 0;
    	unsigned long i = 0;
    	
    	if (argc != 4)
    	{
    		Devmem_usage();
    		return 0;
    	}
    	
    	addr = strtoul(argv[2], 0, 0);
    	len = strtoul(argv[3], 0, 0);
    	if (strcmp(argv[1], "read") == 0)
    	{
    		printf("read data
    ");
    		memset(readData, 0, len);				
    		Devmem_Read(addr, readData, len);		// 读取数据
    		for (i = 0; i < len; i++)
    		{
    			printf("address = 0x%08x, data = 0x%08x
    ", (addr + i * 4), readData[i]);
    		}
    	}
    	else if (strcmp(argv[1], "write") == 0)
    	{
    		printf("write data
    ");
    		memset(writeData, 0, len);
    		for (i = 0; i < len; i++)
    		{
    			writeData[i] = i;
    		}
    		Devmem_Write(addr, writeData, len);		// 写入数据
    	}
    	else
    	{
    		printf("error command type
    ");
    		Devmem_usage();
    	}
    }
    

    5 编译调试

    使用交叉编译工具链进行编译

    arm-xilinx-linux-gnueabi-gcc -o devmem_tool devmem.c
    
    • 1

    将编译好的 devmem_tool 下载到开发板,运行测试,本工具一次操作4个字节。
    测试命令如下:

    * 读取内存数据: ./devmem_tool read 0xfa0000 20
    * 读取内存物理地址 0xfa0000作为起始地址,一共读取20个4字节,共计 20 * 4 = 80 字节。
    * 写入内存数据: ./devmem_tool write 0xfa0000 20
    * 写入内存物理地址 0xfa0000作为起始地址,一共写入20个4字节,共计 20 * 4 = 80 字节。
    【作者】张昺华
    【大饼教你学系列】https://edu.csdn.net/course/detail/10393
    【新浪微博】 张昺华--sky
    【twitter】 @sky2030_
    【微信公众号】 张昺华
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    HDU3718 Similarity 最大权值匹配
    HDU2853 Assignment 最大权值匹配+简直是太神了
    HDU3488 Tour 再次感受到KM的强大
    ZOJ2575 Full of Painting 动态规划
    ZOJ2571 Big String Outspread 模拟
    POJ3565 Ants 空间点对不相交匹配最小权值匹配
    Fleury(弗罗莱)算法求欧拉路径
    HDU2426 Interesting Housing Problem 最大权值匹配不能完成匹配的处理
    POJ1392 Ouroboros Snake 欧拉回路
    HDU2255 奔小康赚大钱 最大权值匹配
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/13597581.html
Copyright © 2011-2022 走看看