内核调试技术
2016-02-24 09:31
726 查看
转载Rockie Cheng
最近在调试Linux内核,跟踪启动过程。发现在没有turn on mmu之前,可以使用物理地址,通过向串口Fifo丢数据的方式输出调试信息。但是代码一旦运行到开启mmu,在汇编阶段,mmu只做了物理内存的映射,并没有映射io,所以就无法访问串口了。
此时应该通过串口输出的数据都保存在串口缓冲池里,直到在c语言阶段,建立io映射并初始化控制台后才进行输出。
但是,如果我想实时跟踪内核启动过程,应该如何才好?
这是就要提到Linux的Lowlevel Debug功能了。
这个选项位于:
Kernel hacking —>
[*] Kernel debugging
[*] Kernel low-level debugging functions
打开这个选项会在.config中增加CONFIG_DEBUG_LL宏定义
初步估计,影响的地方有以下几点:
(1)arch/arm/kernel/head.s的__create_page_tables部分
(2)arch/arc/mach-处理器/处理器.c中的宏定义部分
(3)kernel/prink.c
为了提供DEBUG_LL功能,还必须完善arch/arc/mach-处理器/include/mach/debug-macro.S
首先说说好处,为什幺打开Kernel low-level debugging functions 功能。
打开这个宏定义后,会在head.s函数的__create_page_tables建立一部分io映射,具体代码如下:
/*
Map in IO space for serial debugging.
This allows debug messages to be output
via a serial console before paging_init.
*/
ldr r3, [r8, #MACHINFO_PGOFFIO]
add r0, r4, r3
rsb r3, r3, #0x4000 @ PTRS_PER_PGD*sizeof(long)
cmp r3, #0x0800 @ limit to 512MB
movhi r3, #0x0800
add r6, r0, r3
ldr r3, [r8, #MACHINFO_PHYSIO]
orr r3, r3, r7
1: str r3, [r0], #4
add r3, r3, #1 << 20
teq r0, r6
bne 1b
代码中也提到,映射io的作用是为了串口调试使用。
这段代码中使用的物理地址和虚拟地址从何而来?arch/arc/mach-处理器/处理器.c中的宏定义部分
比如以下处理器:
MACHINE_START(EASIC0718, “SEP0718 board”)
.phys_io = 0x10005000, //调试寄存器的基地址
.io_pg_offst = ((0xe005000) >> 18) & 0xfffc,//调试寄存器的虚拟地址
.boot_params = 0x30000100,
.fixup = fixup_gfd0718,
.map_io = sep0718_map_io,
.init_irq = sep0718_init_irq,
.init_machine = sep0718_init,
.timer = &sep0718_timer,
MACHINE_END
中间
.phys_io = 0x10005000,
.io_pg_offst = ((0xe005000) >> 18) & 0xfffc,
就是供__create_page_tables的 CONFIG_DEBUG_LL中做io映射使用
最后就是对printk.c的改动了,主要是多定义了void printascii(char *)可以直接输出。
printascii函数是在arm/kernel/debug.S中定义:
ENTRY(printascii)
addruart r3
b 2f
1: waituart r2, r3
senduart r1, r3
busyuart r2, r3
teq r1, #’\n’
moveq r1, #’\r’
beq 1b
2: teq r0, #0
ldrneb r1, [r0], #1
teqne r1, #0
bne 1b
mov pc, lr
其中waituart ,busyuart 等宏都是在linux/include/asm-arm/arch-sep0718/debug-macro.S中定义的。
完善了debug-macro.S后可以用printascii了。建立了内存映射后,哪里都可以用,无论汇编还是C,无论开mmu前还是开mmu后。代码会判断有没有开mmu的。
同时prink会在缓冲池保留一份数据,也同时调用printkasii直接输出,因此内核启动后会看到信息输出了两次。
最后贴上debug-macro.S,writed by leeming,注意其中的io地址和处理器架构相关,不能直接使用。
/* linux/include/asm-arm/arch-sep0718/debug-macro.S
*
Debugging macro include header
*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
*
*/
最近在调试Linux内核,跟踪启动过程。发现在没有turn on mmu之前,可以使用物理地址,通过向串口Fifo丢数据的方式输出调试信息。但是代码一旦运行到开启mmu,在汇编阶段,mmu只做了物理内存的映射,并没有映射io,所以就无法访问串口了。
此时应该通过串口输出的数据都保存在串口缓冲池里,直到在c语言阶段,建立io映射并初始化控制台后才进行输出。
但是,如果我想实时跟踪内核启动过程,应该如何才好?
这是就要提到Linux的Lowlevel Debug功能了。
这个选项位于:
Kernel hacking —>
[*] Kernel debugging
[*] Kernel low-level debugging functions
打开这个选项会在.config中增加CONFIG_DEBUG_LL宏定义
初步估计,影响的地方有以下几点:
(1)arch/arm/kernel/head.s的__create_page_tables部分
(2)arch/arc/mach-处理器/处理器.c中的宏定义部分
(3)kernel/prink.c
为了提供DEBUG_LL功能,还必须完善arch/arc/mach-处理器/include/mach/debug-macro.S
首先说说好处,为什幺打开Kernel low-level debugging functions 功能。
打开这个宏定义后,会在head.s函数的__create_page_tables建立一部分io映射,具体代码如下:
ifdef CONFIG_DEBUG_LL
ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags/*
Map in IO space for serial debugging.
This allows debug messages to be output
via a serial console before paging_init.
*/
ldr r3, [r8, #MACHINFO_PGOFFIO]
add r0, r4, r3
rsb r3, r3, #0x4000 @ PTRS_PER_PGD*sizeof(long)
cmp r3, #0x0800 @ limit to 512MB
movhi r3, #0x0800
add r6, r0, r3
ldr r3, [r8, #MACHINFO_PHYSIO]
orr r3, r3, r7
1: str r3, [r0], #4
add r3, r3, #1 << 20
teq r0, r6
bne 1b
代码中也提到,映射io的作用是为了串口调试使用。
这段代码中使用的物理地址和虚拟地址从何而来?arch/arc/mach-处理器/处理器.c中的宏定义部分
比如以下处理器:
MACHINE_START(EASIC0718, “SEP0718 board”)
.phys_io = 0x10005000, //调试寄存器的基地址
.io_pg_offst = ((0xe005000) >> 18) & 0xfffc,//调试寄存器的虚拟地址
.boot_params = 0x30000100,
.fixup = fixup_gfd0718,
.map_io = sep0718_map_io,
.init_irq = sep0718_init_irq,
.init_machine = sep0718_init,
.timer = &sep0718_timer,
MACHINE_END
中间
.phys_io = 0x10005000,
.io_pg_offst = ((0xe005000) >> 18) & 0xfffc,
就是供__create_page_tables的 CONFIG_DEBUG_LL中做io映射使用
最后就是对printk.c的改动了,主要是多定义了void printascii(char *)可以直接输出。
printascii函数是在arm/kernel/debug.S中定义:
ENTRY(printascii)
addruart r3
b 2f
1: waituart r2, r3
senduart r1, r3
busyuart r2, r3
teq r1, #’\n’
moveq r1, #’\r’
beq 1b
2: teq r0, #0
ldrneb r1, [r0], #1
teqne r1, #0
bne 1b
mov pc, lr
其中waituart ,busyuart 等宏都是在linux/include/asm-arm/arch-sep0718/debug-macro.S中定义的。
完善了debug-macro.S后可以用printascii了。建立了内存映射后,哪里都可以用,无论汇编还是C,无论开mmu前还是开mmu后。代码会判断有没有开mmu的。
同时prink会在缓冲池保留一份数据,也同时调用printkasii直接输出,因此内核启动后会看到信息输出了两次。
最后贴上debug-macro.S,writed by leeming,注意其中的io地址和处理器架构相关,不能直接使用。
/* linux/include/asm-arm/arch-sep0718/debug-macro.S
*
Debugging macro include header
*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
*
*/
include
相关文章推荐
- Linux Kernel 4.0 RC5 发布!
- 比较详细的WinXP故障恢复控制台完全手册第1/2页
- mysql 控制台操作
- PowerShell实现在控制台中插入绿色的打勾符号
- C#控制台模拟电梯工作原理
- C#使用控制台列出当前所有可用的打印机列表
- C#下载网页并在控制台输出的方法
- C++实现基于控制台界面的吃豆子游戏
- c#入门之枚举和结构体使用详解(控制台接收字符串以相反的方向输出)
- 原创的C语言控制台小游戏
- js调试系列 初识控制台
- C#隐藏控制台键盘输入的方法
- Python实现控制台输入密码的方法
- yii框架通过控制台命令创建定时任务示例
- Java从控制台接受输入字符的简单方法
- java控制台输入示例分享
- java控制台输出百分比进度条示例
- Python实现控制台进度条功能
- python控制台中实现进度条功能
- python在控制台输出进度条的方法