待调试驱动文件 myleds.c
#include
#include
#include
#include
#include
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<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
");
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
");
}
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
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
int fd;
char val;
char *filename;
if(argc!=3)
{
printf("Usage:
");
printf("%s
",argv[0]);
return 0;
}
filename = argv[1];
fd = open(filename,O_RDWR);
if(fd<0)
{
printf("can't open %s !
",filename);
return 0;
}
if(strcmp(argv[2],"on")==0)
val = 1;
else if(strcmp(argv[2],"off")==0)
val = 0;
else
{
printf("Usage:
");
printf("%s
",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 : [] lr : [] 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
",__FILE__,__FUNCTION__,__LINE__);
*gpfcon &= ~(0x3<<(4*2)|(0x3<<(5*2))|(0x3<<(6*2)));
DBG_PRINTK("%s %s %d
",__FILE__,__FUNCTION__,__LINE__);
*gpfcon |= (0x1<<(4*2)|(0x1<<(5*2))|(0x1<<(6*2)));
DBG_PRINTK("%s %s %d
",__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
#include
#include
#include
#include
#include
#include
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
");
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
#include
#include
#include
#include
#include
#include
#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;jf_flags & O_NONBLOCK) && is_mylog_empty())
return -EAGAIN;
error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty());
while (!error && (mylog_getc(&c)) && i 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
#include
#include
#include
#include
#include
#include
#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;jf_flags & O_NONBLOCK) && is_mylog_empty())
return -EAGAIN;
error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty());
while (!error && (mylog_getc(&c)) && i 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
#include
#include
#include
#include
#include
#include
#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;jf_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 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 : [] lr : [] 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 : []不属于内核函数地址。cat /proc/kallsyms 可以得到加载驱动的函数地址范围,找到相近的地址:bf003000 t myleds_open [myleds],找到了myleds.ko。
在pc上反汇编:arm-linux-objdump -D myleds.ko > myleds.dis,在myleds.dis找到myleds_open为00000000 :(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 : [] lr : [] 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 : []属于内核函数地址。
在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:
[] (myleds_open+0x0/0x3c [myleds]) from [] (chrdev_open+0x14c/0x164)
[] (chrdev_open+0x0/0x164) from [] (__dentry_open+0x100/0x1e8)
r8:c3d4c3a0 r7:c0474ea0 r6:c008d73c r5:c3eea0e0 r4:c3da5520
[] (__dentry_open+0x0/0x1e8) from [] (nameidata_to_filp+0x34/0x48)
[] (nameidata_to_filp+0x0/0x48) from [] (do_filp_open+0x40/0x48)
r4:00000002
[] (do_filp_open+0x0/0x48) from [] (do_sys_open+0x54/0xe4)
r5:00000004 r4:00000002
[] (do_sys_open+0x0/0xe4) from [] (sys_open+0x24/0x28)
[] (sys_open+0x0/0x28) from [] (ret_fast_syscall+0x0/0x2c)
Code: e24cb004 e59f1024 e3a00000 e5912000 (e5923000)
上述Backtrace信息的打印需要在内核vi .config中CONFIG_FRAME_POINTER=y
如果没有则除了配置之外,还可以根据Stack翻译出此信息。在上面出错的PC值开始,一直找到了myleds.dis:
00000000 :
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 ,
c008d73c :
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 :
继续类似查找得到Backtrace回溯信息。
3、自制寄存器编辑器
vi ker_rw.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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:
");
printf("%s [num]
", file);
printf("%s
", 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
");
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
", 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
", 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
", 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
int main(int argc, char **argv)
{
int fd;
int val = 1;
fd = open("/dev/xyz", O_RDWR);
if (fd < 0)
{
printf("can't open!
");
}
if (argc != 2)
{
printf("Usage :
");
printf("%s
", 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 ime.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
",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
",current->pid,current->comm);
printk("pc = %08x
", 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语句。