20150501调试分析之 自制工具<寄存器编辑器>
2015-05-1 Lover雪儿
今天还是继续我们内核错误调试,今天是制作一个寄存器编辑器,可以自由的读写某些我们需要调试的寄存器.
一.首先完成一个可自动创建设备节点的字符设备驱动程序
这儿我们前面都写过了N遍,此处不再赘述,直接附上代码:
1 /****************************** 2 内核调试之自制寄存器读写工具(驱动) 3 *****************************/ 4 #include <linux/module.h> 5 #include <linux/init.h> 6 #include <linux/kernel.h> 7 #include <linux/delay.h> 8 #include <linux/types.h> 9 #include <linux/ioctl.h> 10 #include <linux/gpio.h> 11 #include <linux/fs.h> 12 #include <linux/device.h> 13 #include <linux/uaccess.h> 14 #include <linux/irq.h> 15 #include <linux/wait.h> 16 #include <linux/sched.h> 17 #include <linux/interrupt.h> 18 #include <linux/io.h> 19 20 21 static int major; 22 23 //auto to create device node 24 static struct class *class; 25 static struct class_device *kernel_class_dev; 26 27 28 static int kernel1_rw_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) 29 { 30 31 return 0; 32 } 33 34 //定义字符设备结构体 35 static struct file_operations kernel_rw_ops = { 36 .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ 37 .ioctl = kernel1_rw_ioctl, 38 }; 39 40 static int kernel_rw_init(void) 41 { 42 printk("<0>kernel_rw_init\n"); 43 major = register_chrdev(0, "kernel_rw", &kernel_rw_ops); 44 class = class_create(THIS_MODULE,"kernel_rw"); 45 kernel_class_dev = device_create(class,NULL,MKDEV(major,0),NULL,"kernel_rw"); /*/dev/kernel_rw*/ 46 47 48 return 0; 49 } 50 51 static void kernel_rw_exit(void) 52 { 53 printk("<0>kernel_rw_exit\n"); 54 device_unregister((void *)kernel_class_dev); 55 class_destroy((struct class *)class); 56 unregister_chrdev(major,"kernel_rw"); 57 } 58 59 module_init(kernel_rw_init); 60 module_exit(kernel_rw_exit); 61 MODULE_LICENSE("GPL"); 62 MODULE_AUTHOR("Lover雪儿");
二.在ioctl中增加寄存器的读写功能
定义ioctl的command命令.
1 #define KERNEL_RW_R8 0 2 #define KERNEL_RW_R16 1 3 #define KERNEL_RW_R32 6 4 5 #define KERNEL_RW_W8 3 6 #define KERNEL_RW_W16 4 7 #define KERNEL_RW_W32 5
实现ioctl函数.
1 static int kernel1_rw_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) 2 { 3 volatile unsigned char *p8; 4 volatile unsigned short *p16; 5 volatile unsigned int *p32; 6 unsigned long val; 7 unsigned long addr; 8 9 unsigned int buf[2]; 10 11 12 copy_from_user(buf, (const void __user *)arg, 8); 13 addr = buf[0]; 14 val = buf[1]; 15 printk("<0>\naddr %x : %d \n",addr, val); 16 p8 = (volatile unsigned char *)ioremap(addr, 4); 17 p16 = p8; 18 p32 = p8; 19 20 switch (cmd) 21 { 22 case KERNEL_RW_R8: 23 { 24 val = *p8; 25 copy_to_user((void __user *)(arg+4), &val, 4); 26 printk("<0>enter r8\n"); 27 break; 28 } 29 30 case KERNEL_RW_R16: 31 { 32 val = *p16; 33 copy_to_user((void __user *)(arg+4), &val, 4); 34 printk("<0>enter r16\n"); 35 break; 36 } 37 38 case KERNEL_RW_R32: 39 { 40 val = *p32; 41 copy_to_user((void __user *)(arg+4), &val, 4); 42 printk("<0>enter r32\n"); 43 break; 44 } 45 46 case KERNEL_RW_W8: 47 { 48 *p8 = val; 49 printk("<0>enter w8, val = %x\n",val); 50 break; 51 } 52 53 case KERNEL_RW_W16: 54 { 55 *p16 = val; 56 printk("<0>enter w16, val = %x\n",val); 57 break; 58 } 59 60 case KERNEL_RW_W32: 61 { 62 *p32 = val; 63 printk("<0>enter w32, val = %x\n",val); 64 break; 65 } 66 default: 67 printk("<0>enter default\n"); 68 break; 69 } 70 71 iounmap(p8); 72 return 0; 73 }
附上驱动程序kernel_rw.c
1 /****************************** 2 内核调试之自制寄存器读写工具(驱动) 3 *****************************/ 4 #include <linux/module.h> 5 #include <linux/init.h> 6 #include <linux/kernel.h> 7 #include <linux/delay.h> 8 #include <linux/types.h> 9 #include <linux/ioctl.h> 10 #include <linux/gpio.h> 11 #include <linux/fs.h> 12 #include <linux/device.h> 13 #include <linux/uaccess.h> 14 #include <linux/irq.h> 15 #include <linux/wait.h> 16 #include <linux/sched.h> 17 #include <linux/interrupt.h> 18 #include <linux/io.h> 19 20 #define KERNEL_RW_R8 0 21 #define KERNEL_RW_R16 1 22 #define KERNEL_RW_R32 6 23 24 #define KERNEL_RW_W8 3 25 #define KERNEL_RW_W16 4 26 #define KERNEL_RW_W32 5 27 28 static int major; 29 30 //auto to create device node 31 static struct class *class; 32 static struct class_device *kernel_class_dev; 33 34 35 static int kernel1_rw_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) 36 { 37 volatile unsigned char *p8; 38 volatile unsigned short *p16; 39 volatile unsigned int *p32; 40 unsigned long val; 41 unsigned long addr; 42 43 unsigned int buf[2]; 44 45 46 copy_from_user(buf, (const void __user *)arg, 8); 47 addr = buf[0]; 48 val = buf[1]; 49 printk("<0>\naddr %x : %d \n",addr, val); 50 p8 = (volatile unsigned char *)ioremap(addr, 4); 51 p16 = p8; 52 p32 = p8; 53 54 switch (cmd) 55 { 56 case KERNEL_RW_R8: 57 { 58 val = *p8; 59 copy_to_user((void __user *)(arg+4), &val, 4); 60 printk("<0>enter r8\n"); 61 break; 62 } 63 64 case KERNEL_RW_R16: 65 { 66 val = *p16; 67 copy_to_user((void __user *)(arg+4), &val, 4); 68 printk("<0>enter r16\n"); 69 break; 70 } 71 72 case KERNEL_RW_R32: 73 { 74 val = *p32; 75 copy_to_user((void __user *)(arg+4), &val, 4); 76 printk("<0>enter r32\n"); 77 break; 78 } 79 80 case KERNEL_RW_W8: 81 { 82 *p8 = val; 83 printk("<0>enter w8, val = %x\n",val); 84 break; 85 } 86 87 case KERNEL_RW_W16: 88 { 89 *p16 = val; 90 printk("<0>enter w16, val = %x\n",val); 91 break; 92 } 93 94 case KERNEL_RW_W32: 95 { 96 *p32 = val; 97 printk("<0>enter w32, val = %x\n",val); 98 break; 99 } 100 default: 101 printk("<0>enter default\n"); 102 break; 103 } 104 105 iounmap(p8); 106 return 0; 107 } 108 109 //定义字符设备结构体 110 static struct file_operations kernel_rw_ops = { 111 .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ 112 .ioctl = kernel1_rw_ioctl, 113 }; 114 115 static int kernel_rw_init(void) 116 { 117 printk("<0>kernel_rw_init\n"); 118 major = register_chrdev(0, "kernel_rw", &kernel_rw_ops); 119 class = class_create(THIS_MODULE,"kernel_rw"); 120 kernel_class_dev = device_create(class,NULL,MKDEV(major,0),NULL,"kernel_rw"); /*/dev/kernel_rw*/ 121 122 123 return 0; 124 } 125 126 static void kernel_rw_exit(void) 127 { 128 printk("<0>kernel_rw_exit\n"); 129 device_unregister((void *)kernel_class_dev); 130 class_destroy((struct class *)class); 131 unregister_chrdev(major,"kernel_rw"); 132 } 133 134 module_init(kernel_rw_init); 135 module_exit(kernel_rw_exit); 136 MODULE_LICENSE("GPL"); 137 MODULE_AUTHOR("Lover雪儿");
三.编写测试函数
1 /****************************** 2 内核调试之自制寄存器读写工具(应用) 3 *****************************/ 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <unistd.h> 7 #include <sys/types.h> 8 #include <sys/stat.h> 9 #include <fcntl.h> 10 #include <termios.h> 11 #include <errno.h> 12 #include <limits.h> 13 #include <asm/ioctls.h> 14 #include <time.h> 15 #include <pthread.h> 16 #include <poll.h> 17 #include <string.h> 18 19 #define KERNEL_RW_R8 (unsigned int)0 20 #define KERNEL_RW_R16 (unsigned int)1 21 #define KERNEL_RW_R32 (unsigned int)6 22 23 #define KERNEL_RW_W8 (unsigned int)3 24 #define KERNEL_RW_W16 (unsigned int)4 25 #define KERNEL_RW_W32 (unsigned int)5 26 27 /* 28 Usage: ./reg_editor bit位数 读的地址 数据的个数 29 读取内核寄存器: 30 ./reg_editor r8 addr [num] 31 ./reg_editor r16 addr [num] 32 ./reg_editor r32 addr [num] 33 34 Usage: ./reg_editor bit位数 写的地址 数据 35 写入内核寄存器: 36 ./reg_editor w8 addr val 37 ./reg_editor w16 addr val 38 ./reg_editor w32 addr val 39 */ 40 41 void print_usage(char *file) 42 { 43 printf("Usage: \n"); 44 printf("%s <r8|r16|r32> <phy addr> [num]\n",file); 45 printf("%s <w8|w16|w32> <phy addr> <val>\n",file); 46 } 47 48 int main(int argc, char **argv) 49 { 50 int fd; 51 unsigned int buf[2]; 52 unsigned int i; 53 unsigned int num; 54 55 buf[0] = 0; 56 buf[1] = 1; 57 58 if((argc != 3) && (argc != 4)){ 59 print_usage(argv[0]); 60 return -1; 61 } 62 63 fd = open("/dev/kernel_rw", O_RDWR); 64 if(fd < 0){ 65 printf("can't open /dev/kernel_rw "); 66 return -1; 67 } 68 buf[0] = strtoul(argv[2], NULL, 0); 69 70 if (argc == 4) 71 { 72 buf[1] = strtoul(argv[3], NULL, 0); 73 num = buf[1]; 74 } 75 else 76 { 77 num = 1; 78 } 79 80 if(strcmp(argv[1], "r8") == 0){ 81 for(i = 0; i< num; i++){ 82 ioctl(fd, KERNEL_RW_R8, buf); //结果放在buf[1]里 83 printf("%02d. [%08x] = %02x\n",i,buf[0],(unsigned char )buf[1]); 84 buf[0] += 1; //地址加1 85 } 86 }else if(strcmp(argv[1], "r16") == 0){ 87 for(i = 0; i< num; i++){ 88 ioctl(fd, KERNEL_RW_R16, buf); //结果放在buf[1]里 89 printf("%02d. [%08x] = %02x\n",i,buf[0],(unsigned short )buf[1]); 90 buf[0] += 2; //地址加2 91 } 92 }else if(strcmp(argv[1], "r32") == 0){ 93 for(i = 0; i< num; i++){ 94 ioctl(fd, KERNEL_RW_R32, buf); //结果放在buf[1]里 95 96 printf("%02d. [%08x] = %02x\n",i,buf[0],(unsigned short )buf[1]); 97 buf[0] += 2; //地址加2 98 } 99 }else if(strcmp(argv[1], "w8") == 0){ 100 ioctl(fd, KERNEL_RW_W8, buf); //addr = buf[0] val = buf[1] 101 }else if(strcmp(argv[1], "w16") == 0){ 102 ioctl(fd, KERNEL_RW_W16, buf); //addr = buf[0] val = buf[1] 103 }else if(strcmp(argv[1], "w32") == 0){ 104 ioctl(fd, KERNEL_RW_W32, buf); //addr = buf[0] val = buf[1] 105 }else{ 106 print_usage(argv[0]); 107 return -1; 108 } 109 110 return 0; 111 } 112 113 /* 114 0x53FCC000 0x53FC_FFFF 16 Kbytes GPIO-1 115 116 */
四.err_led驱动程序
1 #include<linux/cdev.h> 2 #include<linux/module.h> 3 #include<linux/types.h> 4 #include<linux/fs.h> 5 #include<linux/errno.h> 6 #include<linux/mm.h> 7 #include<linux/sched.h> 8 #include<linux/init.h> 9 #include<asm/io.h> 10 #include<asm/system.h> 11 #include<asm/uaccess.h> 12 #include<linux/device.h> 13 #include <linux/delay.h> 14 15 #define Driver_NAME "err_led_dev" 16 #define DEVICE_NAME "err_led_dev" 17 18 static int major = 0; 19 20 #define LED_ON 0 21 #define LED_OFF 1 22 23 24 //auto to create device node 25 static struct class *drv_class = NULL; 26 static struct class_device *drv_class_dev = NULL; 27 28 //寄存器基址; 29 static unsigned long mem_iomux; 30 static unsigned long mem_gpio3; 31 static unsigned long base_iomux; //iomux基址 0X 43FA C000 - 0X 43FA FFFF 32 static unsigned long base_gpio3; //gpio3 0X 53FA 4000 - 0X 53FA 7FFF 33 // MUX_CTL模式选择 配置寄存器 34 #define MUX_CTL (*(volatile unsigned long *)(base_iomux + 0x0060)) 35 // PAD_CTL GPIO常用功能设置 36 #define PAD_CTL (*(volatile unsigned long *)(base_iomux + 0x0270)) 37 // GPIO DR 数据寄存器 DR 38 #define DR_GPIO3 (*(volatile unsigned long *)(base_gpio3 + 0x0000)) 39 // GPIO GDIR 方向控制寄存器 GDIR 40 #define GDIR_GPIO3 (*(volatile unsigned long *)(base_gpio3 + 0x0004)) 41 42 43 static int key_open(struct inode *inode, struct file *file) 44 { 45 printk("<0>function open!\n\n"); 46 return 0; 47 } 48 49 static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 50 { 51 return 0; 52 } 53 54 static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) 55 { 56 printk("<0>function write!\n\n"); 57 return 1; 58 } 59 60 static int key_release(struct inode *inode, struct file *filp) 61 { 62 printk("<0>function write!\n\n"); 63 return 0; 64 } 65 66 static int key_ioctl(struct inode *inode,struct file *flip,unsigned int command,unsigned long arg) 67 { 68 printk("<0>function ioctl!\n\n"); 69 70 switch(command) 71 { 72 case LED_ON: 73 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 亮 74 break; 75 case LED_OFF: 76 77 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 灭 78 break; 79 default: 80 break; 81 } 82 83 return 0; 84 } 85 static struct file_operations key_fops = { 86 .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ 87 .open = key_open, 88 .read = key_read, 89 .write = key_write, 90 .release= key_release, 91 .ioctl = key_ioctl, 92 }; 93 94 void gpio_addr(void){ 95 printk("<0>addr base_iomux : %x \n",base_iomux); 96 printk("<0>addr base_gpio3 : %x \n",base_gpio3); 97 printk("<0>addr MUX_CTL : %x \n",&MUX_CTL); 98 printk("<0>addr PAD_CTL : %x \n",&PAD_CTL); 99 printk("<0>addr GDIR_GPIO3 : %x \n",&GDIR_GPIO3); 100 printk("<0>addr DR_GPIO3 : %x \n",&DR_GPIO3); 101 } 102 103 104 105 void led_on_off(void){ 106 ssleep(1); 107 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 108 ssleep(1); 109 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 110 ssleep(1); 111 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 112 ssleep(1); 113 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 114 ssleep(1); 115 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 116 ssleep(1); 117 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 118 ssleep(1); 119 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 120 ssleep(1); 121 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 122 ssleep(1); 123 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 124 } 125 126 static int __init key_irq_init(void) 127 { 128 printk("<0>\nHello,this is %s module!\n\n",Driver_NAME); 129 //register and mknod 130 major = register_chrdev(0,Driver_NAME,&key_fops); 131 drv_class = class_create(THIS_MODULE,Driver_NAME); 132 drv_class_dev = device_create(drv_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME); /*/dev/key_query*/ 133 134 //IO端口申请 ioremap 可以直接通过指针来访问这些地址 135 base_iomux = ioremap(0x43FAC000,0xFFF); 136 base_gpio3 = ioremap(0x53FA4000,0xFFF); 137 138 //MUX_CTL 139 MUX_CTL &= ~(0x07 << 0); 140 MUX_CTL |= (0X05 << 0); //设置为ALT5 GPIO3_23 ERR_LED 141 //PAD_CTL 142 PAD_CTL &= ~(0x01<<13 | 0x01<<3 | 0x03<<1 | 0x01<<0); //1.8v 不需要上拉下拉 CMOS输出 slew rate 143 //GDIR_GPIO3 配置为输出模式 144 GDIR_GPIO3 &= ~(0x01 << 23); 145 GDIR_GPIO3 |= (0x01 << 23); //配置为输出模式 146 147 //DR_GPIO3 配置为输出0 点亮ERR_LED 148 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 149 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 150 gpio_addr(); 151 led_on_off(); 152 return 0; 153 } 154 155 static void __exit key_irq_exit(void) 156 { 157 gpio_addr(); 158 printk("<0>\nGoodbye,%s!\n\n",Driver_NAME); 159 led_on_off(); 160 161 unregister_chrdev(major,Driver_NAME); 162 device_unregister(drv_class_dev); 163 class_destroy(drv_class); 164 165 //释放IO端口 166 iounmap(base_iomux); 167 iounmap(base_gpio3); 168 } 169 170 171 /* 这两行指定驱动程序的初始化函数和卸载函数 */ 172 module_init(key_irq_init); 173 module_exit(key_irq_exit); 174 175 /* 描述驱动程序的一些信息,不是必须的 */ 176 MODULE_AUTHOR("Lover雪儿"); 177 MODULE_VERSION("0.1.0"); 178 MODULE_DESCRIPTION("IMX257 key Driver"); 179 MODULE_LICENSE("GPL");
五.err_led测试程序.
1 /****************************** 2 内核调试之自制寄存器读写工具(应用) 3 *****************************/ 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <unistd.h> 7 #include <sys/types.h> 8 #include <sys/stat.h> 9 #include <fcntl.h> 10 #include <termios.h> 11 #include <errno.h> 12 #include <limits.h> 13 #include <asm/ioctls.h> 14 #include <time.h> 15 #include <pthread.h> 16 #include <poll.h> 17 18 #define LED_ON 0 19 #define LED_OFF 1 20 21 /* 22 Usage: 23 ./err_led_test <on | off> 24 */ 25 26 void print_usage(char *file) 27 { 28 printf("Usage: \n"); 29 printf("%s <on | off>\n",file); 30 } 31 32 int main(int argc, char **argv) 33 { 34 int fd ; 35 36 if(argc != 2){ 37 print_usage(argv[0]); 38 return -1; 39 } 40 41 fd = open("/dev/err_led_dev", O_RDWR); 42 if(fd < 0){ 43 printf("can't open /dev/err_led_dev \n"); 44 return -1; 45 } 46 47 if(strcmp(argv[1], "on") == 0){ 48 ioctl(fd, LED_ON); 49 }else if(strcmp(argv[1], "off") == 0){ 50 ioctl(fd, LED_OFF); 51 }else{ 52 print_usage(argv[0]); 53 return -1; 54 } 55 56 return 0; 57 }
六.编译测试
步骤:
①加载err_led.ko
②加载kernel_rw.ko
③使用reg_editor应用程序读取 0x53fa4002 led灯引脚的数据
④使用err_led的应用程序熄灭led灯
⑤再次使用reg_editor应用程序读取 0x53fa4002 led灯引脚的数据
⑥使用reg_editor应用程序往0x53fa4000地址写入1000000,观察LED是否熄灭
⑥使用reg_editor应用程序往0x53fa4000地址写入0000000,观察LED是否点亮
1 //加载 kernel_rw.ko 2 root@EasyARM-iMX257 /mnt/nfs/module/38_debug_kernel_rw# insmod kernel_rw.ko 3 kernel_rw_init 4 //读取地址 0x53fa4002的数据,由于此时灯是亮的,所以数据为0x05 5 root@EasyARM-iMX257 /mnt/nfs/module/38_debug_kernel_rw# ./app/reg_editor r32 0x53fa4002 6 enter r32 7 00. [53fa4002] = 05 8 //熄灭led灯,也就是给led的引脚高电平 9 root@EasyARM-iMX257 /mnt/nfs/module/38_debug_kernel_rw# ./err_gpio/app/err_led_test off 10 function open! 11 //再次 读取地址 0x53fa4002的数据,发现数据变为85了 12 root@EasyARM-iMX257 /mnt/nfs/module/38_debug_kernel_rw# ./app/reg_editor r32 0x53fa4002 13 enter r32 14 00. [53fa4002] = 85 15 root@EasyARM-iMX257 /mnt/nfs/module/38_debug_kernel_rw# 16 17 ********************************************************************* 18 写地址:灭灯 19 root@EasyARM-iMX257 /mnt/nfs/module/38_debug_kernel_rw# ./app/reg_editor w32 0x5 20 3fa4000 10000000 21 er w32, val = 989680 灯被熄灭 22 23 写地址:亮灯 24 root@EasyARM-iMX257 /mnt/nfs/module/38_debug_kernel_rw# ./app/reg_editor w32 0x5 25 3fa4000 00000000 26 val = 0 写完后,发现IMX257板子的LED被点亮 27 28 root@EasyARM-iMX257 /mnt/nfs/module/38_debug_kernel_rw#
如图所示: