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得知,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

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

//*****************************************************************************

#define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \

__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\n", __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]\n", __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]\n", __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]\n", __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]\n", __LINE__, __FILE__, errno, strerror(errno));

close(fd);

return -1;

}

printf("map_base over 4k = [%p].\n", 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]\n", __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]\n", __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]\n", __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]\n", __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]\n", __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]\n", __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\n");

printf("The read/write is the command type, read or write data to memory\n");

printf("The 0xfa2000 is the memory physical address\n");

printf("The 20 is read/write data length, uint is 4 byte, so it is total 20 * 4 = 80 bytes\n");

printf("Usage: ./devmem_tool read 0xfa0000 20\n");

printf("Usage: ./devmem_tool write 0xfa0000 20\n");

}

/**

* @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\n");

memset(readData, 0, len);

Devmem_Read(addr, readData, len);// 读取数据

for (i = 0; i < len; i++)

{

printf("address = 0x%08x, data = 0x%08x\n", (addr + i * 4), readData[i]);

}

}

else if (strcmp(argv[1], "write") == 0)

{

printf("write data\n");

memset(writeData, 0, len);

for (i = 0; i < len; i++)

{

writeData[i] = i;

}

Devmem_Write(addr, writeData, len);// 写入数据

}

else

{

printf("error command type\n");

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 字节。

标签:__,MAP,errno,Linux,unsigned,len,long,memory,内存地址

来源: https://www.cnblogs.com/sky-heaven/p/13597581.html

linux 读取内存颗粒,Linux 读写memory操作,devmem直接访问物理内存地址【转】相关推荐

  1. linux 读取内存颗粒,Linux虚拟内存地址转化成物理内存地址

    背景 现代手机这种SOC(system on chip),因为功耗.Modem等功能soc上集成了很多core,他们还可以是独立的系统在运转. 比如ADSP简介ADSP(Application Dig ...

  2. linux 读取内存颗粒,linux查看主板内存槽与内存信息的命令dmidecode怎么用

    在Linux中,我们常常使用命令来实现许多操作,比如查看内存信息等,下面小编就为大家带来一篇linux查看主板内存槽与内存信息的命令dmidecode方法.小编觉得挺不错的,现在就分享给大家,也给大家 ...

  3. linux 读取内存颗粒,Linux中的内存管理模型浅析

    实际上这是一个内存方面的问题.要想研究这个问题,首先我们要将题目本身搞明白.由于我对Linux内核比较熟而对Windows的内存模型几乎毫不了解,因此在这篇文章中针对Linux环境对这个问题进行探讨. ...

  4. Matlab的各种数据读取、文件读写等操作汇总

    Matlab的各种数据读取.文件读写等操作汇总 MATLAB提供了多种方式从磁盘读入文件或将数据输入到工作空间,即读取数据,又叫导入数据:将工作空间的变量存储到磁盘文件中称为存写数据,又叫导出数据.至 ...

  5. linux下c语言读写文件操作,Linux C语言 文件操作

    打开函数 fopen 的原型如下. FILE * fopen(char *filename, char *mode); 返回值:打开成功,返回该文件对应的 FILE 类型的指针:打开失败,返回 NUL ...

  6. 机械硬盘 运行 linux 很慢,如果读写硬盘操作有问题,假死机、很慢等,就检查一下硬盘坏道...

    本帖最后由 tonyliu2ca 于 16-3-28 11:12 编辑 这个问题要是写出来有时一个大块文章,咱们这里简单说说. 现代的硬磁盘技术已经相当成熟和智能,成熟并不是说就不是好就是坏,磁盘的状 ...

  7. linux下c语言读写文件操作,linux下的系统级c语言文件读写操作

    最近初次接触Linux这么高端的东西,有种进城的感觉.进了linux,发现城里人说话做事的方式都很不一样. 个人感觉,初次接触linux主要的痛点在于命令行交互的方式,这就要求我接受城里人的思维,wh ...

  8. linux读取iso,Linux下iso文件的读取,创建

    chinaunix网友2009-07-13 14:05 我是Mysql 数据库同步: 现状是:slave_io_running:no, slave_sql_running:yes last_err: ...

  9. 安装nginx并进行配置(记录来源于马哥linux运维教与文件操作优化、访问、日志相关的配置配置 五)

    文件优化: send_file aio on | off //是否启动异步iodirectio size | off // 理解参考 https://blog.csdn.net/qq_34556414 ...

  10. Linux优化大量文件读写,[Linux] 使用noatime属性优化文件系统读取性能

    当文件被创建,修改和访问时,Linux系统会记录这些时间信息,当访问足够频繁将会是很大的开销,因为每次访问都会记录时间,所以 我们今天使用 # tar xf bonnie++-1.97.tgz # c ...

最新文章

  1. 重新想,重新看——CSS3变形,过渡与动画①
  2. kotlin 类及其成员的可见性
  3. labview与下位机通信的格式问题处理
  4. Qt状态机框架介绍(二)
  5. 【Elasticsearch】Elasticsearch 基于scoll技术滚动搜索大量数据
  6. JavaScript学习初步
  7. 【图论】[BZOJ 1051]受欢迎的牛
  8. 变色龙配置文件功能介绍
  9. 计算机查看图片的打开方式,windows10电脑怎么在右键菜单打开方式添加照片查看器...
  10. 线段树的简单实现(引入lazy_tag)
  11. 我用 Python 写了个基金涨跌通知助手
  12. 日志收集系统Flume笔记(基础版)
  13. 【CSS3】Advanced1:Rounded Corners
  14. 畅享7 plus android8,华为畅享7和畅享7Plus有什么区别【详细介绍】
  15. Silverlight实用窍门系列:29.Silverlight碰撞测试、检测自定义控件碰撞,雷达扫描图之扫描雷达点状态【附带源码实例】...
  16. Python爬虫实战,拉黑 QQ 空间屏蔽我的“大人物“
  17. python pandas 增加一列_Python之pandas新增列
  18. protect权限解析
  19. 大数定理、正态分布、中心极限定理
  20. splint安装与配置

热门文章

  1. 入门用Python进行Web爬取数据:为数据科学项目提取数据的有效方法
  2. 计算机配色故障,计算机配色模型中存在的限制点
  3. centos下载和安装mongodb
  4. 基于Qt的NAT检测和NAT穿透
  5. 【SLAM学习】(三)激光雷达原理及分类
  6. oracle中锁表是什么,oracle锁表查询和解锁方法是什么,oracle锁表和解锁
  7. 目标管理 督查督办系统
  8. HTML网页实训的目的,网页设计实习目的及意义
  9. 视频编解码器讲解-H.264、VP9、HEVC、AV1
  10. 框架设计--第八章 动态SQL--习题答案