本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接。内容可任意使用,但对因使用该内容引起的后果不做任何保证。
作者:fireaxe_hq@hotmail.com
博客:fireaxe.blog.chinaunix.net
背景
:
开发设备驱动时,经常需要查看物理设备的寄存器内容,比如在powerpc平台开发时,有时需要查看IMMR指向的cpu寄存器空间的内容,IMMR物理地址是0xe0000000,这个地址无法在shell中直接查看。只能到内核中才能查看,非常不方便。因此希望开发一些小工具,用于实现类似功能。
1. 我首先是实现一个memdump函数用于打印某段内存的值,这种直接操作物理地址空间的操作,当然只能在内核中做。
这里需要用到ioremap函数,因为即使在内核也不能直接操作物理地址,需要把它映射到内核空间上。
当然最后还需要使用iounmap解除映射。
-
static void memdump(u32 para[4])
-
{
-
int i;
-
char *base;
-
u32 size = para[1];
-
-
base = (char*)ioremap(para[0], size);
-
-
for (i = 0; i < size; i++) {
-
if ((i%4 == 0) && i)
-
printk(" ");
-
-
if (i%(4*4) == 0)
-
printk("\n%08x: ", (u32)(para[0] + i));
-
-
printk("%02x", *(base + i));
-
}
-
-
printk("\n%");
-
iounmap(base);
-
}
2. 我们的目标是在shell中能够查看,所以需要有方法在shell中调用该函数。我们可以做一个简单的字符设备驱动,然后通过read、write的方式调用它。另外为了以后可以很方便的增加新的功能,这里实现了一个简单的命令解析器。
struct MyCmd用于定义已实现的命令格式。驱动中实现的所有命令都存在myCmdList中,本例中只是先了一个“d“命令。struct USR_CMD用于用户向内核传送命令,只有当USR_CMD中的命令能够与myCmdList中的命令匹配上时,才会调用对应命令,否则返回失败。
-
struct USR_CMD {
-
char name[ASB_NAME_MAX]; // 命令
-
u32 para[4]; // 参数
-
};
-
-
struct MyCmd {
-
char *name; // 命令
-
void (*ptr)(u32 para[4]); // 参数
-
}
-
-
struct MyCmd myCmdList[] = {"d", memdump};
-
-
static ssize_t chr_write (struct file *f, const char __user *addr, size_t size, loff_t *off)
-
{
-
void (*ptr)(u32 para[4]);
-
struct USR_CMD cmd;
-
int i;
-
-
// get cmd from user space to kernel space;
-
copy_from_user((char*)&cmd, addr, sizeof(struct USR_CMD));
-
printk(KERN_INFO"show command: %s", cmd.name);
-
-
// get function ptr by name;
-
for (i = 0; i < sizeof(myCmdList)/sizeof(struct MyCmd); i++) {
-
if (0 == strcmp(cmd.name, myCmdList[i].name)) {
-
ptr = myCmdList[i].ptr;
-
break;
-
}
-
}
-
if (i >= sizeof(myCmdList)/sizeof(struct MyCmd)) {
-
printk(KERN_ERR"error command: %s", cmd.name);
-
return (-1);
-
}
-
-
// run function ptr;
-
ptr(cmd.para);
-
-
return 0;
-
}
3. 实现命令解释器,用于把从shell输入的命令变成上面定义的USR_CMD格式。
-
int CmdParser(USR_CMD *cmd, int argc, char *argv[])
-
{
-
ssize_t size;
-
int i;
-
if (argc < 2) {
-
printf("too few para:%d\n", argc);
-
return (-1);
-
}
-
if (argc > 6) {
-
printf("too many para:%d\n", argc);
-
return (-1);
-
}
-
memset(cmd, 0, sizeof(struct USR_CMD));
-
strcpy(cmd->name, argv[1]);
-
for (i = 2; i < argc; i++) {
-
if (('0' == *(argv[i])) && ('x' == *(argv[i]+1))) {
-
// hex parse
-
sscanf(argv[i], "0x%x", &cmd->para[i-2]);
-
} else if ('"' == *(argv[i])) {
-
// string parse
-
} else {
-
cmd->para[i-2] = atoi(argv[i]);
-
}
-
}
-
4. 最后实现用户空间入口
点击(此处)折叠或打开
-
int main(int argc, char *argv[])
-
{
-
int fd;
-
ssize_t size;
-
struct USR_CMD cmd;
-
int i;
-
-
// parse cmd
-
if (-1 == CmdParser(&cmd, argc, argv))
-
{
-
Printf(“CmdParser error.\n”);
-
Return (-1);
-
}
-
-
// open device
-
fd = open("/dev/hqdebug", O_RDWR);
-
if (-1 == fd) {
-
printf("open failed: %x\n", errno);
-
return (-1);
-
}
-
-
// write cmd to device
-
size = write(fd, (char*)&cmd, sizeof(cmd));
-
if (-1 == size) {
-
printf("write failed: %x\n", errno);
-
return (-1);
-
}
-
-
// close device
-
close(fd);
-
-
return 0;
-
}
在用户空间编译连接上面的 函数,我们命名为hqdbg。
5. 调用
以powepc上的IMMR为例,IMMR的地址一般为0xe0000000。
在shell 上输入:
> ./hqdbg d 0xe0000000 0x1000
则会在shell上显示从0xe0000000开始的长度为0x1000的一段内存。
本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接。内容可任意使用,但对因使用该内容引起的后果不做任何保证。
作者:fireaxe_hq@hotmail.com
博客:fireaxe.blog.chinaunix.net
阅读(4882) | 评论(0) | 转发(1) |