JZ2440笔记:驱动调试

待调试驱动文件 myleds.c

#include <asm/mach/map.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;


static int myleds_open(struct inode *inode,struct file *file)
{
	*gpfcon &= ~(0x3<<(4*2)|(0x3<<(5*2))|(0x3<<(6*2)));
	*gpfcon |=  (0x1<<(4*2)|(0x1<<(5*2))|(0x1<<(6*2)));
	return 0;
}

static ssize_t myleds_write(struct file *file,const char __user *buf,size_t len,loff_t *ppos)
{
	int minor;
	char val;
       	copy_from_user(&val,buf,len);
	minor = MINOR(file->f_dentry->d_inode->i_rdev);
	switch(minor)
	{
		case 0:
			if(val==1)
		                *gpfdat &= ~((1<<4)|(1<<5)|(1<<6));
       			 else
               			 *gpfdat |= ((1<<4)|(1<<5)|(1<<6));
			 break;
		case 1:
                        if(val==1)
                                *gpfdat &= ~((1<<4));
                         else
                                 *gpfdat |= ((1<<4));
                         break;
		case 2:
                        if(val==1)
                                *gpfdat &= ~((1<<5));
                         else
                                 *gpfdat |= ((1<<5));
                         break;
		case 3:
                        if(val==1)
                                *gpfdat &= ~((1<<6));
                         else
                                 *gpfdat |= ((1<<6));
                         break;


	}
	return 0;
}


static struct file_operations myleds_fops = {
	.owner = THIS_MODULE,
	.open = myleds_open,
	.write = myleds_write,
};

int major;
static struct class *firstdrv_class;
static struct class_device *firstdrv_device[4];
static int myleds_init(void)
{
	int i;
	major = register_chrdev(0,"myleds",&myleds_fops);
	firstdrv_class = class_create(THIS_MODULE,"firstdrv");
	firstdrv_device[0] = class_device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"leds");
	for(i=1;i<4;i++)
	{
		firstdrv_device[i] = class_device_create(firstdrv_class,NULL,MKDEV(major,i),NULL,"led%d",i);
	}

	//gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
	gpfcon = (volatile unsigned long *)0x56000050;
	gpfdat = gpfcon + 1;

	printk("myleds_drv_init\n");
	return 0;
}

static void myleds_exit(void)
{
	int i;

	iounmap(gpfcon);

	unregister_chrdev(major,"myleds");
	for(i=0;i<4;i++)
        {
        	class_device_destroy(firstdrv_class,MKDEV(major,i));
	}
	class_destroy(firstdrv_class);
	printk("myleds_drv_exit\n");
}

module_init(myleds_init); 
module_exit(myleds_exit);

MODULE_LICENSE("GPL");

将gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);改成gpfcon = (volatile unsigned long *)0x56000050;

使用测试文件vi myledstest.c

#include <unistd.h> 
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
	int fd;
	char val;
	char *filename;
	
	if(argc!=3)
	{
		printf("Usage: \n");
		printf("%s </dev/led?> <on|off>\n",argv[0]);
		return 0;
	}
	
	filename = argv[1];
	fd = open(filename,O_RDWR);
	if(fd<0)
	{
                printf("can't open %s !\n",filename);
		return 0;
	}

	if(strcmp(argv[2],"on")==0)
		val = 1;
	else if(strcmp(argv[2],"off")==0)
		val = 0;
	else
	{
		printf("Usage: \n");
                printf("%s </dev/led?> <on|off>\n",argv[0]);
                return 0;
	}

	write(fd,&val,1);
	return 0;
}

测试驱动:

# insmod myleds.ko
myleds_drv_init
# ./myledstest.o /dev/leds on
Unable to handle kernel paging request at virtual address 56000050
pgd = c3d30000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in: myleds
CPU: 0    Not tainted  (2.6.22.6 #17)
PC is at first_drv_open+0x18/0x3c [myleds]
LR is at chrdev_open+0x14c/0x164
pc : [<bf000018>]    lr : [<c008d888>]    psr: a0000013
sp : c3fa9e88  ip : c3fa9e98  fp : c3fa9e94
r10: 00000000  r9 : c3fa8000  r8 : c0498760
r7 : 00000000  r6 : 00000000  r5 : c3eec0e0  r4 : c0717660
r3 : bf000000  r2 : 56000050  r1 : bf000ae4  r0 : 00000000
Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  Segment user
 

调试驱动:

1、使用printk

(1)宏

#define DBG_PRINTK printk
//#define DBG_PRINTK(x...)

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;

static int myleds_open(struct inode *inode,struct file *file)
{
	DBG_PRINTK("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__);
	*gpfcon &= ~(0x3<<(4*2)|(0x3<<(5*2))|(0x3<<(6*2)));
	DBG_PRINTK("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__);
	*gpfcon |=  (0x1<<(4*2)|(0x1<<(5*2))|(0x1<<(6*2)));
	DBG_PRINTK("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__);
	return 0;
}

# ./myledstest /dev/leds on
/home/embedfire/linux/nfs/23/myleds.c myleds_open 15
Unable to handle kernel paging request at virtual address 56000050

可以看到*gpfcon &= ~(0x3<<(4*2)|(0x3<<(5*2))|(0x3<<(6*2)));运行错误。

(2)使用printk(KERN_DEBUG替换DBG_PRINTK(,

同时# echo "8 4 1 7" > /proc/sys/kernel/printk

# ./myledstest /dev/leds on
/home/embedfire/linux/nfs/23/myleds.c myleds_open 15
Unable to handle kernel paging request at virtual address 56000050

注意:在uboot里设置set bootargs loglevel=0 noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200,会使所有启动信息屏蔽。

在uboot里设置set bootargs debug noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200,恢复启动信息。

可以使用命令dmesg打印出log_buf里的所有信息。

可以cat /proc/kmsg打印出log_buf里的所有信息。

(3)打印驱动信息到单独的文件

vi mymsg.c

#include <asm/mach/map.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>


struct proc_dir_entry *myentry;

const struct file_operations proc_mykmsg_operations = {
};


static int mymsg_init(void)
{
	myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root);
	if (myentry)
		myentry->proc_fops = &proc_mykmsg_operations;
	return 0;
}

static void mymsg_exit(void)
{
	remove_proc_entry("mymsg",&proc_root);
}

module_init(mymsg_init); 
module_exit(mymsg_exit);

MODULE_LICENSE("GPL");

测试驱动:

# insmod mymsg.ko
# ls -l /proc/mymsg
-r--------    1 0        0               0 Jun  5 16:55 /proc/mymsg
# cat /proc/mymsg
cat: read error: Invalid argument

vi mymsg.c增加读函数

static ssize_t mymsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	printk("mymsg_read\n");
	return 0;
}


const struct file_operations proc_mykmsg_operations = {
	.read		= mymsg_read,
};

测试驱动:

# insmod mymsg.ko
# cat /proc/mymsg
mymsg_read

vi mymsg.c增加缓冲区

static char mylog_buf[1024];

static ssize_t mymsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	//int cnt;
	//cnt = min(1024,count);
	copy_to_user(buf, mylog_buf, 10);
	return 10;
}


const struct file_operations proc_mykmsg_operations = {
	.read		= mymsg_read,
};


static int mymsg_init(void)
{
	sprintf(mylog_buf,"asdfsdfdfgfbbvnbvcbc");

	myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root);
	if (myentry)
		myentry->proc_fops = &proc_mykmsg_operations;
	return 0;
}

测试驱动:

# insmod mymsg.ko
# cat /proc/mymsg
asdfsdfdfgasdfsdfdfgasdfsdfdfgasdfsdfdfgasdfsdfdfgasdfsdfdfgasdfsdfdfgasdfsdfdfgasdf

 vi mymsg.c增加myprintk

#include <asm/mach/map.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>

#define MYLOG_BUF_LEN 1024

static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);

struct proc_dir_entry *myentry;
static char mylog_buf[MYLOG_BUF_LEN];
static char tmp_buf[MYLOG_BUF_LEN];
static int mylog_r = 0;
static int mylog_w = 0;


static int is_mylog_empty(void)
{
	return (mylog_r==mylog_w);
}
static int is_mylog_full(void)
{
	return ((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r);
}
static void mylog_putc(char c)
{
	if(is_mylog_full())
		mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;
	mylog_buf[mylog_w] = c;
	mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN;

	wake_up_interruptible(&mymsg_waitq);
}
static int mylog_getc(char *p)
{
	if(is_mylog_empty())
		return 0;
	*p = mylog_buf[mylog_r];
	mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;
	return 1;
}

int myprintk(const char *fmt,...)
{
	va_list args;
	int i,j;

	va_start(args, fmt);
	i = vsnprintf(tmp_buf,INT_MAX,fmt,args);
	va_end(args);

	for(j=0;j<i;j++)
		mylog_putc(tmp_buf[j]);
	
	return i;
}

static ssize_t mymsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	int error = 0;
	int i = 0;
	char c;

	if((file->f_flags & O_NONBLOCK) && is_mylog_empty())
		return -EAGAIN;
	error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty());
	
	while (!error && (mylog_getc(&c)) && i < count) {
		error = __put_user(c,buf);
		buf++;
		i++;
	}
	if (!error)
		error = i;
	return error;
}


const struct file_operations proc_mykmsg_operations = {
	.read		= mymsg_read,
};


static int mymsg_init(void)
{
	sprintf(mylog_buf,"asdfsdfdfgfbbvnbvcbc");

	myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root);
	if (myentry)
		myentry->proc_fops = &proc_mykmsg_operations;
	return 0;
}

static void mymsg_exit(void)
{
	remove_proc_entry("mymsg",&proc_root);
}

module_init(mymsg_init); 
module_exit(mymsg_exit);

EXPORT_SYMBOL(myprintk);

MODULE_LICENSE("GPL");

测试驱动:

# insmod myleds.ko
myleds: Unknown symbol myprintk
insmod: cannot insert 'myleds.ko': Unknown symbol in module (-1): No such file or directory
# insmod mymsg.ko
# insmod myleds.ko
myleds_drv_init
# cat /proc/mymsg
/home/embedfire/linux/nfs/23/myleds.c myleds_init 78
/home/embedfire/linux/nfs/23/myleds.c myleds_init 88

# cat /proc/mymsg
再一次就没反应了

 vi mymsg.c 每次cat /proc/mymsg都打印

#include <asm/mach/map.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>

#define MYLOG_BUF_LEN 1024

static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);

struct proc_dir_entry *myentry;
static char mylog_buf[MYLOG_BUF_LEN];
static char tmp_buf[MYLOG_BUF_LEN];
static int mylog_r = 0;
static int mylog_w = 0;


static int is_mylog_empty(void)
{
	return (mylog_r==mylog_w);
}
static int is_mylog_full(void)
{
	return ((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r);
}
static void mylog_putc(char c)
{
	if(is_mylog_full())
		mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;
	mylog_buf[mylog_w] = c;
	mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN;

	wake_up_interruptible(&mymsg_waitq);
}
static int mylog_getc(char *p)
{
	if(is_mylog_empty())
		return 0;
	*p = mylog_buf[mylog_r];
	mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;
	return 1;
}

int myprintk(const char *fmt,...)
{
	va_list args;
	int i,j;

	va_start(args, fmt);
	i = vsnprintf(tmp_buf,INT_MAX,fmt,args);
	va_end(args);

	for(j=0;j<i;j++)
		mylog_putc(tmp_buf[j]);
	
	return i;
}

static ssize_t mymsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	int error = 0;
	int i = 0;
	char c;

	if((file->f_flags & O_NONBLOCK) && is_mylog_empty())
		return -EAGAIN;
	error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty());
	
	while (!error && (mylog_getc(&c)) && i < count) {
		error = __put_user(c,buf);
		buf++;
		i++;
	}
	if (!error)
		error = i;
	return error;
}


const struct file_operations proc_mykmsg_operations = {
	.read		= mymsg_read,
};


static int mymsg_init(void)
{
	sprintf(mylog_buf,"asdfsdfdfgfbbvnbvcbc");

	myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root);
	if (myentry)
		myentry->proc_fops = &proc_mykmsg_operations;
	return 0;
}

static void mymsg_exit(void)
{
	remove_proc_entry("mymsg",&proc_root);
}

module_init(mymsg_init); 
module_exit(mymsg_exit);

EXPORT_SYMBOL(myprintk);

MODULE_LICENSE("GPL");

测试驱动:

# insmod myleds.ko
myleds: Unknown symbol myprintk
insmod: cannot insert 'myleds.ko': Unknown symbol in module (-1): No such file or directory
# insmod mymsg.ko
# insmod myleds.ko
myleds_drv_init
# cat /proc/mymsg
/home/embedfire/linux/nfs/23/myleds.c myleds_init 78
/home/embedfire/linux/nfs/23/myleds.c myleds_init 88

# cat /proc/mymsg
再一次就没反应了

 vi mymsg.c 每次cat /proc/mymsg都打印

#include <asm/mach/map.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>

#define MYLOG_BUF_LEN 1024

static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);

struct proc_dir_entry *myentry;
static char mylog_buf[MYLOG_BUF_LEN];
static char tmp_buf[MYLOG_BUF_LEN];
static int mylog_r_for_read = 0;
static int mylog_r = 0;
static int mylog_w = 0;


static int is_mylog_empty(void)
{
	return (mylog_r==mylog_w);
}
static int is_mylog_empty_for_read(void)
{
	return (mylog_r_for_read==mylog_w);
}

static int is_mylog_full(void)
{
	return ((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r);
}
static void mylog_putc(char c)
{
	if(is_mylog_full())
	{
		mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;
		if((mylog_r_for_read + 1) % MYLOG_BUF_LEN == mylog_r)
			mylog_r_for_read = mylog_r;
	}
	
	mylog_buf[mylog_w] = c;
	mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN;

	wake_up_interruptible(&mymsg_waitq);
}
static int mylog_getc(char *p)
{
	if(is_mylog_empty())
		return 0;
	*p = mylog_buf[mylog_r];
	mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;
	return 1;
}
static int mylog_getc_for_read(char *p)
{
	if(is_mylog_empty_for_read())
		return 0;
	*p = mylog_buf[mylog_r_for_read];
	mylog_r_for_read = (mylog_r_for_read + 1) % MYLOG_BUF_LEN;
	return 1;
}


int myprintk(const char *fmt,...)
{
	va_list args;
	int i,j;

	va_start(args, fmt);
	i = vsnprintf(tmp_buf,INT_MAX,fmt,args);
	va_end(args);

	for(j=0;j<i;j++)
		mylog_putc(tmp_buf[j]);
	
	return i;
}

static int mymsg_open (struct inode *inode, struct file *file)
{
	mylog_r_for_read = mylog_r;
	return 0;
}


static ssize_t mymsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	int error = 0;
	int i = 0;
	char c;

	if((file->f_flags & O_NONBLOCK) && is_mylog_empty_for_read())
		return -EAGAIN;
	error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read());
	
	while (!error && (mylog_getc_for_read(&c)) && i < count) {
		error = __put_user(c,buf);
		buf++;
		i++;
	}
	if (!error)
		error = i;
	return error;
}


const struct file_operations proc_mykmsg_operations = {
	.open       = mymsg_open,
	.read		= mymsg_read,
};


static int mymsg_init(void)
{
	sprintf(mylog_buf,"asdfsdfdfgfbbvnbvcbc");

	myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root);
	if (myentry)
		myentry->proc_fops = &proc_mykmsg_operations;
	return 0;
}

static void mymsg_exit(void)
{
	remove_proc_entry("mymsg",&proc_root);
}

module_init(mymsg_init); 
module_exit(mymsg_exit);

EXPORT_SYMBOL(myprintk);

MODULE_LICENSE("GPL");

测试驱动:

# insmod myleds.ko
myleds: Unknown symbol myprintk
insmod: cannot insert 'myleds.ko': Unknown symbol in module (-1): No such file or directory
# insmod mymsg.ko
# insmod myleds.ko
myleds_drv_init
# cat /proc/mymsg
/home/embedfire/linux/nfs/23/myleds.c myleds_init 78
/home/embedfire/linux/nfs/23/myleds.c myleds_init 88

# cat /proc/mymsg
/home/embedfire/linux/nfs/23/myleds.c myleds_init 78
/home/embedfire/linux/nfs/23/myleds.c myleds_init 88
 

2、根据内核打印的段错误信息分析

# ./myledstest /dev/leds on
# ./myledstest /dev/leds on
pc : [<bf003018>]    lr : [<c008d888>]    psr: a0000013
sp : c3d91e88  ip : c3d91e98  fp : c3d91e94
r10: 00000000  r9 : c3d90000  r8 : c3dd4cc0
r7 : 00000000  r6 : 00000000  r5 : c3eea0e0  r4 : c0711120
r3 : bf003000  r2 : 56000050  r1 : bf003c24  r0 : 00000000

编译的内核处,vi System.map得到内核函数的起末地址:c0004000 - c03ccad4,

pc : [<bf003018>]不属于内核函数地址。cat /proc/kallsyms 可以得到加载驱动的函数地址范围,找到相近的地址:bf003000 t myleds_open  [myleds],找到了myleds.ko。

在pc上反汇编:arm-linux-objdump -D myleds.ko > myleds.dis,在myleds.dis找到myleds_open为00000000 <myleds_open>:(insmod后为bf003000)。那最终找到文件在00000018处出错。

18:   e5923000        ldr     r3, [r2] //根据r3 : bf003000  r2 : 56000050  r1 : bf003c24  r0 : 00000000,有 ldr bf003000,[56000050],结合上文对应的C为*gpfcon &= ~(0x3<<(4*2)|(0x3<<(5*2))|(0x3<<(6*2)));

如果将myleds.c编译进内核(./drivers/char makefile:obj-y    += myleds.o):

# ls /dev/led*
/dev/led1  /dev/led2  /dev/led3  /dev/leds
# ./myledstest /dev/leds on
Unable to handle kernel paging request at virtual address 56000050
pgd = c3f4c000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in:
CPU: 0    Not tainted  (2.6.22.6 #19)
PC is at myleds_open+0x18/0x3c
LR is at chrdev_open+0x14c/0x164
pc : [<c019df5c>]    lr : [<c008d888>]    psr: a0000013
sp : c3f79e88  ip : c3f79e98  fp : c3f79e94
r10: 00000000  r9 : c3f78000  r8 : c3e0b620
r7 : 00000000  r6 : 00000000  r5 : c3dc5e9c  r4 : c04c9180
r3 : c019df44  r2 : 56000050  r1 : c03c1c9c  r0 : 00000000

编译的内核处,vi System.map得到内核函数的起末地址:c0004000 - c03ccb74,

pc : [<c019df5c>]属于内核函数地址。

在pc上反汇编内核:aarm-linux-objdump -D vmlinux > vmlinux.dis,在vmlinux.dis找到c019df5c c019df5c:       e5923000        ldr     r3, [r2]  //根据r3 : c019df44  r2 : 56000050  r1 : c03c1c9c  r0 : 00000000,有 ldr c019df44,[56000050],结合上文对应的C为*gpfcon &= ~(0x3<<(4*2)|(0x3<<(5*2))|(0x3<<(6*2)));

# ./myledstest /dev/leds on
Stack: (0xc3f25e88 to 0xc3f26000)
5e80:                   c3f25ebc c3f25e98 c008d888 bf003010 00000000 c3da5520
5ea0: c3eea0e0 c008d73c c0474ea0 c3d4c3a0 c3f25ee4 c3f25ec0 c0089e48 c008d74c
5ec0: c3da5520 c3f25f04 00000003 ffffff9c c002c044 c3f8c000 c3f25efc c3f25ee8
5ee0: c0089f64 c0089d58 00000000 00000002 c3f25f68 c3f25f00 c0089fb8 c0089f40
5f00: c3f25f04 c3d4c3a0 c0474ea0 00000000 00000000 c3f55000 00000101 00000001
5f20: 00000000 c3f24000 c04cc528 c04cc520 ffffffe8 c3f8c000 c3f25f68 c3f25f48
5f40: c008a16c c009fc70 00000003 00000000 c3da5520 00000002 00000004 c3f25f94
5f60: c3f25f6c c008a2f4 c0089f88 00008520 bec33eb4 00008674 000086d8 00000005
5f80: c002c044 4013365c c3f25fa4 c3f25f98 c008a3a8 c008a2b0 00000000 c3f25fa8
5fa0: c002bea0 c008a394 bec33eb4 00008674 bec33f7a 00000002 00000004 bec33f7a
5fc0: bec33eb4 00008674 000086d8 00000003 00008520 00000000 4013365c bec33e88
5fe0: 00000000 bec33e60 0000266c 400c98e0 60000010 bec33f7a 00000000 00000000
Backtrace:
[<bf003000>] (myleds_open+0x0/0x3c [myleds]) from [<c008d888>] (chrdev_open+0x14c/0x164)
[<c008d73c>] (chrdev_open+0x0/0x164) from [<c0089e48>] (__dentry_open+0x100/0x1e8)
 r8:c3d4c3a0 r7:c0474ea0 r6:c008d73c r5:c3eea0e0 r4:c3da5520
[<c0089d48>] (__dentry_open+0x0/0x1e8) from [<c0089f64>] (nameidata_to_filp+0x34/0x48)
[<c0089f30>] (nameidata_to_filp+0x0/0x48) from [<c0089fb8>] (do_filp_open+0x40/0x48)
 r4:00000002
[<c0089f78>] (do_filp_open+0x0/0x48) from [<c008a2f4>] (do_sys_open+0x54/0xe4)
 r5:00000004 r4:00000002
[<c008a2a0>] (do_sys_open+0x0/0xe4) from [<c008a3a8>] (sys_open+0x24/0x28)
[<c008a384>] (sys_open+0x0/0x28) from [<c002bea0>] (ret_fast_syscall+0x0/0x2c)
Code: e24cb004 e59f1024 e3a00000 e5912000 (e5923000)

上述Backtrace信息的打印需要在内核vi .config中CONFIG_FRAME_POINTER=y

如果没有则除了配置之外,还可以根据Stack翻译出此信息。在上面出错的PC值开始,一直找到了myleds.dis:

00000000 <myleds_open>:
   0:   e1a0c00d        mov     ip, sp
   4:   e92dd800        stmdb   sp!, {fp, ip, lr, pc}
   8:   e24cb004        sub     fp, ip, #4      ; 0x4

可以得到返回地址c008d888(看得出属于内核地址)

Stack: (0xc3f25e88 to 0xc3f26000)
5e80:                   c3f25ebc c3f25e98 c008d888 bf003010 00000000 c3da5520
						myleds_open的栈      lr             调用者的栈

反编译内核得到c008d888所在c008d73c <chrdev_open>,

c008d73c <chrdev_open>:
c008d73c:       e1a0c00d        mov     ip, sp
c008d740:       e92dd9f0        stmdb   sp!, {r4, r5, r6, r7, r8, fp, ip, lr, pc}
c008d744:       e24cb004        sub     fp, ip, #4      ; 0x4
c008d748:       e24dd004        sub     sp, sp, #4      ; 0x4
c008d74c:       e59040e4        ldr     r4, [r0, #228]

可以得到chrdev_open占9(r4, r5, r6, r7, r8, fp, ip, lr, pc)+1(sub     sp, sp, #4)= 10 个word,

Stack: (0xc3f25e88 to 0xc3f26000)
5e80:                   c3f25ebc c3f25e98 c008d888 bf003010 00000000 c3da5520
						myleds_open的栈      lr             chrdev_open的栈
5ea0: c3eea0e0 c008d73c c0474ea0 c3d4c3a0 c3f25ee4 c3f25ec0 c0089e48 c008d74c
																lr	 	
5ec0: c3da5520 c3f25f04 00000003 ffffff9c c002c044 c3f8c000 c3f25efc c3f25ee8
	  调用者的栈

继续查找(搜索c0089e48)找到c0089d48 <__dentry_open>:

继续类似查找得到Backtrace回溯信息。

3、自制寄存器编辑器

vi ker_rw.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
#include <linux/device.h>


#define KER_RW_R8      0
#define KER_RW_R16     1
#define KER_RW_R32     2

#define KER_RW_W8      3
#define KER_RW_W16     4
#define KER_RW_W32     5



static int ker_rw_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	volatile unsigned char  *p8;
	volatile unsigned short *p16;
	volatile unsigned int   *p32;
	unsigned int val;
	unsigned int addr;

	unsigned int buf[2];

	copy_from_user(buf, (const void __user *)arg, 8);
	addr = buf[0];
	val  = buf[1];
	p8 = (volatile unsigned char *)ioremap(addr,4);
	p16 =p8;
	p32 =p8;
	
	switch(cmd)
	{
		case KER_RW_R8:
			val = *p8;
			copy_to_user((void __user *)(arg+4), &val, 4);
			break;
		case KER_RW_R16:
			val = *p16;
			copy_to_user((void __user *)(arg+4), &val, 4);
			break;
		case KER_RW_R32:
			val = *p32;
			copy_to_user((void __user *)(arg+4), &val, 4);
			break;
		case KER_RW_W8:
			*p8 = val;
			break;
		case KER_RW_W16:
			*p16 = val;
			break;
		case KER_RW_W32:
			*p32 = val;
			break;
	}
	iounmap(p8);
	return 0;
}


static struct file_operations ker_rw_fops = {
	.owner = THIS_MODULE,
	.ioctl = ker_rw_ioctl,
};

static int major;
static struct class *cls;
static int ker_rw_init(void)
{
	major = register_chrdev(0,"ker_rw",&ker_rw_fops);
	cls = class_create(THIS_MODULE, "ker_rw");
	class_device_create(cls, NULL, MKDEV(major,0), NULL, "ker_rw");
	return 0;
}

static void ker_rw_exit(void)
{
	class_device_destroy(cls, MKDEV(major,0));
	class_destroy(cls);
	unregister_chrdev(major, "ker_rw");
}

module_init(ker_rw_init); 
module_exit(ker_rw_exit);

MODULE_LICENSE("GPL");

vi regeditor.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

#define KER_RW_R8      0
#define KER_RW_R16     1
#define KER_RW_R32     2

#define KER_RW_W8      3
#define KER_RW_W16     4
#define KER_RW_W32     5


/* Usage:
 * ./regeditor r8  addr [num]
 * ./regeditor r16 addr [num]
 * ./regeditor r32 addr [num]
 *
 * ./regeditor w8  addr val
 * ./regeditor w16 addr val 
 * ./regeditor w32 addr val
 */

void print_usage(char *file)
{
	printf("Usage:\n");
	printf("%s <r8 | r16 | r32> <phy addr> [num]\n", file);
	printf("%s <w8 | w16 | w32> <phy addr> <val>\n", file);
}

int main(int argc, char **argv)
{
	int fd;
	unsigned int buf[2];
	unsigned int i;
	unsigned int num;
	
	if ((argc != 3) && (argc != 4))
	{
		print_usage(argv[0]);
		return -1;
	}

	fd = open("/dev/ker_rw", O_RDWR);
	if (fd < 0)
	{
		printf("can't open /dev/ker_rw\n");
		return -2;
	}

	/* addr */
	buf[0] = strtoul(argv[2], NULL, 0);

	if (argc == 4)
	{
		buf[1] = strtoul(argv[3], NULL, 0);
		num    = buf[1];
	}
	else
	{
		num = 1;
	}

	if (strcmp(argv[1], "r8") == 0)
	{
		for ( i = 0; i < num; i++)
		{
			ioctl(fd, KER_RW_R8, buf);  /* val = buf[1] */
			printf("%02d. [%08x] = %02x\n", i, buf[0], (unsigned char)buf[1]);
			buf[0] += 1;
		}
	}
	else if (strcmp(argv[1], "r16") == 0)
	{
		for ( i = 0; i < num; i++)
		{
			ioctl(fd, KER_RW_R16, buf);  /* val = buf[1] */
			printf("%02d. [%08x] = %02x\n", i, buf[0], (unsigned short)buf[1]);
			buf[0] += 2;
		}
	}
	else if (strcmp(argv[1], "r32") == 0)
	{
		for ( i = 0; i < num; i++)
		{
			ioctl(fd, KER_RW_R32, buf);  /* val = buf[1] */
			printf("%02d. [%08x] = %02x\n", i, buf[0], (unsigned int)buf[1]);
			buf[0] += 4;
		}
	}
	else if (strcmp(argv[1], "w8") == 0)
	{
		ioctl(fd, KER_RW_W8, buf);  /* val = buf[1] */
	}
	else if (strcmp(argv[1], "w16") == 0)
	{
		ioctl(fd, KER_RW_W16, buf);  /* val = buf[1] */
	}
	else if (strcmp(argv[1], "w32") == 0)
	{
		ioctl(fd, KER_RW_W32, buf);  /* val = buf[1] */
	}
	else
	{
		printf(argv[0]);
		return -1;
	}

	return 0;
	
}

测试:

# insmod ker_rw.ko
# ./regeditor r32 0x56000054
00. [56000054] = 7f
# insmod myleds.ko
myleds_drv_init
# ./myledstest /dev/leds on
# ./regeditor r32 0x56000054
00. [56000054] = 0f

4、修改系统时钟中断定位系统僵死问题

vi first_drv.c 在first_drv_write加入while(1);死循环

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

static struct class *firstdrv_class;
static struct class_device	*firstdrv_class_dev;

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;


static int first_drv_open(struct inode *inode, struct file *file)
{
	*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
	*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
	return 0;
}

static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	int val;
    copy_from_user(&val, buf, count); 

	while (1);

	if (val == 1)
	{
		*gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
	}
	else
	{
		*gpfdat |= (1<<4) | (1<<5) | (1<<6);
	}
	
	return 0;
}

static struct file_operations first_drv_fops = {
    .owner  =   THIS_MODULE,    
    .open   =   first_drv_open,     
	.write	=	first_drv_write,	   
};


int major;
static int first_drv_init(void)
{
	major = register_chrdev(0, "first_drv", &first_drv_fops);

	firstdrv_class = class_create(THIS_MODULE, "firstdrv");

	firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); 

	gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
	gpfdat = gpfcon + 1;

	return 0;
}

static void first_drv_exit(void)
{
	unregister_chrdev(major, "first_drv"); 

	class_device_unregister(firstdrv_class_dev);
	class_destroy(firstdrv_class);
	iounmap(gpfcon);
}

module_init(first_drv_init);
module_exit(first_drv_exit);


MODULE_LICENSE("GPL");

vi firstdrvtest.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char **argv)
{
	int fd;
	int val = 1;
	fd = open("/dev/xyz", O_RDWR);
	if (fd < 0)
	{
		printf("can't open!\n");
	}
	if (argc != 2)
	{
		printf("Usage :\n");
		printf("%s <on|off>\n", argv[0]);
		return 0;
	}

	if (strcmp(argv[1], "on") == 0)
	{
		val  = 1;
	}
	else
	{
		val = 0;
	}
	
	write(fd, &val, 4);
	return 0;
}

测试:

# insmod first_drv.ko
# ./firstdrvtest on

卡死。

在arch\arm\plat-s3c24xx\time.c s3c2410_timer_interrupt中断函数中加入

static pid_t pre_pid;
	static int cnt = 0;
	if(pre_pid==current->pid)
		cnt++;
	else 
	{
		cnt = 0;
		pre_pid = current->pid;
	}
	if(cnt==10*HZ)
	{
		cnt = 0;
		printk("s3c2410_timer_interrupt : pid = %d, task name = %s\n",current->pid,current->comm);
	}

重新编译内核,启动:

# insmod first_drv.ko
# ./firstdrvtest on
s3c2410_timer_interrupt : pid = 778, task name = firstdrvtest
s3c2410_timer_interrupt : pid = 778, task name = firstdrvtest

改进:在arch\arm\kernel\irq.c asm_do_IRQ中断函数中加入

if(irq == 30)
	{
		static pid_t pre_pid;
		static int cnt = 0;
		if(pre_pid==current->pid)
			cnt++;
		else 
		{
			cnt = 0;
			pre_pid = current->pid;
		}
		if(cnt==10*HZ)
		{
			cnt = 0;
			printk("asm_do_IRQ->s3c2410_timer_interrupt : pid = %d, task name = %s\n",current->pid,current->comm);
			printk("pc = %08x\n", regs->ARM_pc);
		}
	}

重新编译内核,启动:

# insmod first_drv.ko
# ./firstdrvtest on
asm_do_IRQ->s3c2410_timer_interrupt : pid = 778, task name = firstdrvtest
pc = bf000084

# insmod first_drv.ko
# cat /proc/kallsyms > kallsyms.txt   在kallsyms.txt找到bf000084所在的函数,重复上面所述过程,找到出错的C语句。

  • 22
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值