- 浏览: 16479126 次
- 性别:
- 来自: 济南
最新评论
-
wu1236:
ef0793cd94337324b6fefc4c9474af5 ...
Android ApiDemos示例解析(87):Media->MediaPlayer -
77219634:
0127bf2236bee4dd1f632ce430f1af1 ...
本博客文章都为转载,没有任何版权! -
77219634:
0127bf2236bee4dd1f632ce430f1af1 ...
VPLEX - EMC的RAC -
77219634:
0127bf2236bee4dd1f632ce430f1af1 ...
qTip2 Show -
77219634:
0127bf2236bee4dd1f632ce430f1af1 ...
SecureCRT中文乱码、复制粘贴乱码解决办法(修改版)
1.介绍..........................................................................................2 |
2.相关定义介绍..........................................................................3 |
2.1.TEXTADDR......................................................3 |
2.2.stext....................................................................3 |
2.3.swapper_pg_dir..................................................4 |
2.4.(M)pgtbl.............................................................4 |
2.5.(M)krnladr..........................................................5 |
2.6..proc.info段.......................................................5 |
2.7.__proc_info_begin/__proc_info_end.................6 |
2.8..arch.info段.......................................................7 |
2.9.__arch_info_begin/__arch_info_end.................8 |
3.代码分析..................................................................................9 |
3.1.KERNEL ENTRY..............................................9 |
3.2.__arm920_setup...............................................11 |
3.3.__ret.................................................................13 |
3.4.__mmap_switched...........................................14 |
3.5.__lookup_processor_type................................16 |
3.6.__lookup_architecture_type.............................16 |
3.7.__create_page_tables.......................................19 |
1.介绍 |
这是一篇对armlinux内核启动的分析,主要是arch/arm/kernel/head-armv.S文件, head-armv.S文件是整个内核的入口,也就是说bootloader执行完毕后将跳转到head-armv.S的第一条指令,head-armv.S执行完后将跳转到start_kernel(),在head-armv.S的执行过程中也用到了其他一些文件,包括arch/arm/kernel/debug-armv.S、arch/arm/mm/proc-arm920.S等等 |
由于此分析基于MX1的内核启动过程,因此除了通用代码,只有定义在CONFIG_ARCH_MX1ADS下的代码和proc-arm920.S(arm920是MX1的CPU)的代码被分析 |
在下面程序流程的说明中,MX1板子启动过程中的寄存器值将会用绿色字体表示出来,而对于专门针对MX1的代码则会用下划线字体表示 |
2.相关定义介绍 |
2.1.TEXTADDR |
TEXTADDR是内核Image的映像地址,也是内核Image所处的虚拟地址,它在系统内核空间起始地址——通常是0xC0000000(这相对应于物理内存开始的地方)+32K的位置,也就是0xC0008000处TEXTADDR的赋值在arch/arm/Makefile文件中:(0xC0008000) |
ifeq ($(CONFIG_CPU_32),y) |
PROCESSOR = armv |
TEXTADDR = 0xC0008000 |
LDSCRIPT = arch/arm/vmlinux-armv.lds.in |
endif |
在内核映像之前的16K空间用来存放内核的页目录表,这就是为什么TEXTADDR要在系统要放在0xC0008000的缘故——它必须留出足够的物理空间来放页表 |
在head-armv.S中TEXTADDR将被检测: |
#if (TEXTADDR & 0xffff) != 0x8000 //TEXTADDR必须为0xXXXX8000 |
#error TEXTADDR must start at 0xXXXX8000 |
#endif |
2.2.stext |
stext是TEXTADDR相对应的物理地址,由于内核空间的起始地址(0xC0000000)相对应的物理地址就是最低物理内存的地址,因此stext就是最低物理内存+32K处(虽然这并不是有内核指定,而是由bootloader指定),它的赋值在连接脚本vmlinux-armv.lds.in中实现:(0x08008000) |
ENTRY(stext) |
SECTIONS |
{ |
. = TEXTADDR; //此处的映像地址为TEXTADDR |
.init : { /* Init code and data */ |
_stext = .; //stext为此处的物理地址 |
2.3.swapper_pg_dir |
swapper_pg_dir是页表的映像地址,由于启动页表在内核Image之前的16K处,因此它等于0xC0004000,它的定义在head-armv.S文件中 |
.globl SYMBOL_NAME(swapper_pg_dir) //设置swapper_pg_dir |
.equ SYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x4000 |
在定义init进程的mm_struct结构的宏INIT_MM中,swapper_pg_dir作为页表基址被赋给init_mm,INIT_MM定义在include/linux/sched.h文件中: #define INIT_MM(name) \ |
{ \ |
… |
pgd: swapper_pg_dir, \ |
… |
} |
2.4.(M)pgtbl |
pgtbl是一个用于获得启动页表物理地址的宏,它将stext减去16K给reg,它也在head-armv.S中定义: |
.macro pgtbl, reg, rambase |
adr \reg, stext |
sub \reg, \reg, #0x4000 //reg=stext-0x4000 |
.endm |
2.5.(M)krnladr |
这个宏用于由pgtable获得内核空间的起始物理地址的所在的段(MB),将pgtable,也就是页表地址(和内核空间的起始物理地址在同一个段内)和0x000FFFFF相与,因为页表地址后12位为零,所以将其和0x000FF000相与 |
/* |
* Since the page table is closely related to the kernel start address, we |
* can convert the page table base address to the base address of the section |
* containing both. |
*/ |
.macro krnladr, rd, pgtable, rambase |
bic \rd, \pgtable, #0x000ff000 |
.endm |
2.6..proc.info段 |
.proc.info段中存放的是各种处理器的信息,每个处理器的信息用一个proc_info_list结构来表示,这个结构在include/asm/procinfo.h文件中声明: |
struct proc_info_list { |
unsigned int cpu_val; //处理器类型 |
unsigned int cpu_mask; //处理器类型掩码 |
unsigned long __cpu_mmu_flags; /* used by head-armv.S */ |
unsigned long __cpu_flush; /* used by head-armv.S */ |
const char *arch_name; |
const char *elf_name; |
unsigned int elf_hwcap; |
struct proc_info_item *info; |
#ifdef MULTI_CPU |
struct processor *proc; |
#else |
void *unused; |
#endif |
}; |
虽然结构是在这里声明,但是真正的定义却是在proc-arm920.S文件的最后: |
.section ".proc.info", #alloc, #execinstr //声明以下代码在.proc.info段中 |
.type __arm920_proc_info,#object |
__arm920_proc_info: |
.long 0x41009200 //cpu_val |
.long 0xff00fff0 //cpu_mask |
.long 0x00000c1e @ mmuflags |
b __arm920_setup //是的!这是一条跳转指令 |
.long cpu_arch_name |
.long cpu_elf_name |
.long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT |
.long cpu_arm920_info |
.long arm920_processor_functions |
2.7.__proc_info_begin/__proc_info_end |
__proc_info_begin是.proc.info段的起始地址,而__proc_info_end是终止地址,他们存放在head-armv.S中: |
2: .long __proc_info_end |
.long __proc_info_begin |
在连接脚本vmlinux.lds.in文件中,他们被赋值: |
__proc_info_begin = .; |
*(.proc.info) |
__proc_info_end = .; |
2.8..arch.info段 |
.arch.info段类似于.proc.info段,不过它是用来存放板子信息的,它的定义是在include/asm/mach/arch.h文件中: |
struct machine_desc { |
/* |
* Note! The first four elements are used |
* by assembler code in head-armv.S |
*/ |
unsigned int nr; /* architecture number */ //板子ID |
unsigned int phys_ram; /* start of physical ram */ //物理内存起始地址 |
unsigned int phys_io; /* start of physical io */ //IO空间起始地址 |
unsigned int io_pg_offst; /* byte offset for io |
* page tabe entry */ |
//IO空间起始地址的虚拟地址在启动页表中的偏移 |
…… |
}; |
而具体MX1板子的machine_desc定义则通过宏在arch/arm/mach-XXXX/arch.c文件(或者该目录下的其他文件)中,这些宏的定义在include/asm/mach/arch.h文件中实现,通过这些宏定义了一个machine_desc: |
MACHINE_START(MX1ADS, "Motorola MX1ADS") |
MAINTAINER("WBSG SPS Motorola") |
#ifdef CONFIG_ARCH_MX1ADS_SRAM |
BOOT_MEM(0x12000000, 0x00200000, 0xf0200000) |
#else |
BOOT_MEM(0x08000000, 0x00200000, 0xf0200000) |
//phys_ram=0x08000000,phys_io=0x00200000, |
//io_pg_offset = ((0xf0200000)>>18)&0xfffc=0x00003c08 |
//右移20位获得IO空间虚拟地址在页表内偏移再乘以四(4个字节) |
#endif |
FIXUP(mx1ads_fixup) |
MAPIO(mx1ads_map_io) |
INITIRQ(mx1ads_init_irq) |
MACHINE_END |
2.9.__arch_info_begin/__arch_info_end |
__arch_info_begin是.arch.info段的起始地址,而__arch_info_end是终止地址,他们存放在head-armv.S中: |
.long __arch_info_begin |
.long __arch_info_end |
在连接脚本vmlinux.lds.in文件中,他们被赋值: |
__arch_info_begin = .; |
*(.arch.info) |
__arch_info_end = .; |
3.代码分析 |
3.1.KERNEL ENTRY |
下面是整个内核Image的入口,进入后必须满足以下条件,即r0为0,r1为板子ID,MMU和D-cache关闭,这其中r1的architecture ID是由bootloader传进来的 |
/* |
* Kernel startup entry point. |
* |
* The rules are: |
* r0 - should be 0 |
* r1 - unique architecture number |
* MMU - off |
* I-cache - on or off |
* D-cache - off |
* |
* See linux/arch/arm/tools/mach-types for the complete list of numbers |
* for r1. |
*/ |
.section ".text.init",#alloc,#execinstr //以下代码属于“.text.init”段 |
.type stext, #function |
ENTRY(stext) |
// mov r12, r0 |
mov r12, #0 // r12=0 |
#if defined(CONFIG_ARCH_MX1ADS) |
mov fp, r1 @ r1 contain pointer to cmdline from bootloader |
#endif //将r1中包含的内核命令行指针移到fp中 |
|
// for MX1ADS, we don't pass this from bootloader, so we'll set it here |
#if defined(CONFIG_ARCH_MX1ADS) |
mov r1, #MACH_TYPE_MX1ADS |
#endif //此时,r1=0x000000a0 |
mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode |
msr cpsr_c, r0 @ and all irqs disabled |
//将模式设置为svc模式,并禁止IRQ和FIQ |
bl __lookup_processor_type //检查处理器类型 |
//如果处理器有效: |
//r8 = __cpu_mmu_flags, r8 = 0x00000c1e |
//r9 =处理器ID |
r10指向处理器结构 |
teq r10, #0 @ invalid processor? //如果是无效处理器 |
moveq r0, #'p' @ yes, error 'p' //打印“p”和出错信息 |
beq __error |
bl __lookup_architecture_type //检查板子类型 |
//如果板子有效: |
// r5=物理内存的起始地址, r5 = 0x08000000 |
// r6=IO空间的起始物理地址 r6=0x00200000 |
// r7=IO空间虚拟地址在页表中的偏移 r7=0x00003c08 |
teq r7, #0 @ invalid architecture? //如果是无效板子 |
moveq r0, #'a' @ yes, error 'a' //打印“a”和出错信息 |
beq __error |
bl __create_page_tables //建立页表 |
//此时页表建立完毕, |
//r1=板子ID, |
//r4=页表地址 (stext-16K) |
//r9=处理器ID, |
//r10指向处理器结构 |
adr lr, __ret @ return address //将返回地址存放在lr中 |
add pc, r10, #12 @ initialise processor |
@ (return control reg) |
//跳转到处理器结构+12的位置,参看.proc.info段可以知 |
//道,这里是一条跳转指令“b __arm920_setup”,因此再 |
//跳转到proc-arm920.S中的__arm920_setup函数入口处 |
3.2.__arm920_setup |
__arm920_setup函数在proc-arm920.S文件中,在页表建立起来之后,此函数进行一些开启MMU之前的初始化操作 |
.section ".text.init", #alloc, #execinstr |
__arm920_setup: |
mov r0, #0 |
mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 |
//使无效整个I-cache和整个D-cache |
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 |
//将Write Buffer中的数据写进内存 |
mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 |
//使无效整个I-TLB和整个D-TLB |
mcr p15, 0, r4, c2, c0 @ load page table pointer |
//将r4(页表地址)写进c2(页表基址寄存器) |
mov r0, #0x1f @ Domains 0, 1 = client |
mcr p15, 0, r0, c3, c0 @ load domain access register |
//设置Domain0、Domain1和Domain2的访问权限 |
mrc p15, 0, r0, c1, c0 @ get control register v4 |
//将控制寄存器(control register)的值送给r0 |
/* |
* Clear out 'unwanted' bits (then put them in if we need them) |
*/ |
@ VI ZFRS BLDP WCAM |
bic r0, r0, #0x0e00 |
bic r0, r0, #0x0002 |
bic r0, r0, #0x000c |
bic r0, r0, #0x1000 @ ...0 000. .... 000. |
/* |
* Turn on what we want |
*/ |
orr r0, r0, #0x0031 |
orr r0, r0, #0x2100 @ ..1. ...1 ..11 ...1 |
#ifdef CONFIG_CPU_ARM920_D_CACHE_ON |
orr r0, r0, #0x0004 @ .... .... .... .1.. |
#endif |
#ifdef CONFIG_CPU_ARM920_I_CACHE_ON |
orr r0, r0, #0x1000 @ ...1 .... .... .... |
#endif |
//修改r0的某些位,使它的后16位为:XX1I 0001 XX11 0D01 |
//其中I表示根据CONFIG_CPU_ARM920_I_CACHE_ON设定 |
//D表示根据CONFIG_CPU_ARM920_D_CACHE_ON设定 |
//X表示不变,1表示置位,0表示清位 |
//具体含义如下: |
//M(bit0)=1,打开MMU |
//A(bit1)=0,关闭Alignment checking |
//C(bit2)=D,D-cache打开/关闭 |
//W(bit3)=0,关闭Write Buffer |
//P(bit4)=1,Exception handler进入32-bit模式 |
//D(bit5)=1,关闭32-bit address exception checking |
//L(bit6)=X,选择Early Abort模式或者Late Abort模式 |
//B(bit7)=X,Little Endian/Big Endian模式 |
//S(bit8)=1,System Protection Bit |
//R(bit9)=0,Rom Protection Bit |
//F(bit10)=0,Implementation Defined |
//Z(bit11)=0,关闭Branch prediction |
//I(bit12)=I,I-cache打开/关闭 |
//V(bit13)=1,选择High exception vector |
mov pc, lr //返回,跳转到head-armv.S的__ret处 |
3.3.__ret |
在proc-arm920.S中的__arm920_setup函数进行过一些启动MMU之前的初始化工作后,根据lr寄存器中的值跳转到__ret处执行,这里做了三件事: |
1. 首先将__switch_data处的值(即__mmap_switched)作为返回值存放在lr寄存器中 |
2. 开启MMU |
3. 最后返回(即跳转到__mmap_switched处) |
请注意!__switch_data中保存的值(也就是__mmap_switched)是一个映像地址(也就是虚拟地址),也就是说,PC的值从此处由物理地址的值跳到内核空间(0xCXXXXXXX) |
.type __switch_data, %object |
__switch_data: .long __mmap_switched |
.long SYMBOL_NAME(compat) |
.long SYMBOL_NAME(__bss_start) |
.long SYMBOL_NAME(_end) |
.long SYMBOL_NAME(processor_id) |
.long SYMBOL_NAME(__machine_arch_type) |
.long SYMBOL_NAME(cr_alignment) |
.long SYMBOL_NAME(init_task_union)+8192 |
#ifdef CONFIG_ARCH_MX1ADS |
.long SYMBOL_NAME(cmdline_from_bootloader) |
//这是为MX1板子定义的从bootloader传来的参数地址 |
#endif |
.type __ret, %function |
__ret: ldr lr, __switch_data //这里保存的是内核空间地址! |
mcr p15, 0, r0, c1, c0 //将r0中的值送回c1,开启MMU!! |
mov r0, r0 //执行三次NOP操作,清空流水线 |
mov r0, r0 |
mov r0, r0 |
mov pc, lr //跳到__mmap_switched处执行 |
3.4.__mmap_switched |
__ret开启MMU之后,通过将__switch_data中保存的__map_switched的值跳转到此处执行,也就是从此处开始PC值转为0xCXXXXXXX |
/* |
* This code follows on after the page |
* table switch and jump above. |
* |
* r0 = processor control register |
* r1 = machine ID |
* r9 = processor ID |
*/ |
.align 5 |
__mmap_switched: |
adr r3, __switch_data + 4 //r3=__switch_data+4 |
ldmia r3, {r2, r4, r5, r6, r7, r8, sp}@ r2 = compat |
@ sp = stack pointer |
//r2=compat |
//r4=bss_start=.bss段的起始地址 |
//r5=_end=.bss段的终止地址 |
//r6=processor_id=保存处理器ID的地址 |
//r7=__machine_arch_type=保存板子ID的地址 |
//r8=cr_alignment |
//sp=initial_task+8192,由task_union结构可知,这是init进程的堆栈 |
str r12, [r2] //将r12中的值(0)存进compat |
#ifdef CONFIG_ARCH_MX1ADS |
mov r12, fp @ fp/r11 gets used below (it originally contain @ pointer to cmdline from bootloader) |
#endif |
//在内核入口的一开始,r1中的包含指向内核命令行的指针被送到fp寄存器中,//现在将它送给r12 |
mov fp, #0 @ Clear BSS (and zero fp) |
1: cmp r4, r5 //将整个.bss段清零 |
strcc fp, [r4],#4 |
bcc 1b |
str r9, [r6] @ Save processor ID //保存处理器ID |
str r1, [r7] @ Save machine type //保存板子ID |
#ifdef CONFIG_ARCH_MX1ADS |
/* now save a pointer to the cmdline_from_bootloader */ |
adr r3, __switch_data + 32 @ cmdline_from_bootloader |
//r3=__switch_data+32=cmdline_from_bootloader的地址 |
ldmia r3, {r4} @ r4 = address of above |
//r4=[r3]=cmdline_from_bootloader |
str r12, [r4] // [r4]=r12, |
//将指向内核命令行的指针赋给cmdline_from_bootloader |
#endif |
#ifdef CONFIG_ALIGNMENT_TRAP |
orr r0, r0, #2 @ ...........A. |
#endif |
bic r2, r0, #2 @ Clear 'A' bit |
stmia r8, {r0, r2} @ Save control register values |
b SYMBOL_NAME(start_kernel) //跳到start_kernel处执行 |
3.5.__lookup_processor_type |
3.6.__lookup_architecture_type |
__lookup_processor_type例程用于检查当前的处理器是否有效,它没有输入,使用了r5,r6,r7三个寄存器,返回r8=__cpu_mmu_flags,r9=处理器ID,r10指向处理器结构 |
__lookup_architecture_type用于检查当前的板子是否有效,它需要输入r1为板子ID,使用了r2, r3, r4三个寄存器,返回r5=物理内存的起始地址,r6=IO空间的起始地址,r7=IO空间虚拟地址的段号 |
/* |
* Read processor ID register (CP#15, CR0), and look up in the linker-built |
* supported processor list. Note that we can't use the absolute addresses |
* for the __proc_info lists since we aren't running with the MMU on |
* (and therefore, we are not in the correct address space). We have to |
* calculate the offset. |
* |
* Returns: |
* r5, r6, r7 corrupted |
* r8 = page table flags |
* r9 = processor ID |
* r10 发表评论 |
相关推荐
描述中提到,这个编译器是用于编译高版本的嵌入式U-Boot和Linux内核。U-Boot是一种流行的Bootloader,负责启动嵌入式设备的操作系统。而Linux内核则是操作系统的核心部分,负责管理硬件资源。由于许多嵌入式设备的...
描述中提到的“移植Linux内核”,是指将Linux操作系统的核心代码修改和编译,使其适应特定的ARM硬件。这通常涉及修改内核配置,选择与硬件相关的驱动程序,并确保内核在目标设备上正确启动和运行。`arm-linux-gcc`在...
图解ARM-Linux的启动全过程:从内核的自解压到引导阶段,再到内核初始化,自后是文件系统的初始化。
ARM-Linux 启动 ARM-Linux的启动全过程:内核自解压阶段——>内核引导阶段——>内核初始化阶段——>BusyBox初始化阶段。
ARM-Linux 启动流程分析 在嵌入式系统领域,ARM 架构的 Linux 系统因其高效能和低功耗而广泛应用于各种设备,如智能手机、路由器、嵌入式设备等。了解 ARM-Linux 的启动流程对于系统调试和优化至关重要。本篇将详细...
【标题】:深入理解Arm-Linux内核移植与系统初始化 【描述】:Arm处理器在当前的嵌入式系统中占据主导地位,移植Linux到Arm平台对于开发人员来说至关重要,它涉及内核移植、系统初始化等多个关键环节。 【标签】:...
它包含了对Linux环境的库支持,但这里编译的是裸机程序,意味着只使用了基本的C语言功能,没有链接到任何Linux内核函数。 2. **运行在Mini2440**:Mini2440是一款基于S3C2440 ARM9处理器的嵌入式开发板,广泛用于...
Bootloader 是引导加载器,负责启动 ARM 设备并加载 Linux 内核;Linux 内核是操作系统的核心,处理硬件资源管理和调度任务;Rootfile 系统则包含了操作系统启动所需的基础文件和服务。理解这三个部分的交互,是成功...
2. **在主机上启动GDB**: `arm-linux-gdb program`,加载源码,并连接到目标设备的gdbserver:`target remote device_ip:port`。 3. **设置断点、查看内存、单步执行等操作**: 通过GDB提供的命令,可以对目标程序...
1. **ARM架构支持**:由于ARM是广泛应用于嵌入式设备和移动平台的处理器架构,`arm-linux-gdb`专为这种架构设计,可以理解和解析ARM汇编代码,并与运行在ARM处理器上的Linux内核进行通信。 2. **远程调试功能**:...
《深入解析ucosii在Mini2440上的arm-linux-gcc编译实践》 在嵌入式系统开发中,UCOSII操作系统以其轻量级、高效的特点被广泛应用。本篇将详细介绍如何在Mini2440开发板上,利用arm-linux-gcc工具链进行UCOSII的编译...
8. **kernel加载与启动**:介绍如何将Linux kernel映像加载到内存并启动,包括内核参数传递和启动过程。 9. **调试技巧**:提供U-Boot的调试方法,如使用串口、JTAG工具进行问题定位。 10. **实战案例**:通过实际...
本主题聚焦于“ARM-Linux内核配置与裁减”,特别是针对基于ARM7架构的44B0处理器。这个过程涉及到一系列技术细节,包括理解硬件特性、选择合适的内核选项以及优化性能。 首先,我们要了解ARM7架构。ARM7是ARM公司...
zImage自解压过程是Linux内核启动过程中的一个重要步骤,涉及到Bootloader的准备、zImage的生成和工作原理、MMU的设置等方面。通过对zImage自解压过程的分析,可以更好地理解Linux内核的启动过程,并为后续的内核...
图解ARM-Linux的启动全过程:内核自解压阶段——>内核引导阶段——>内核初始化阶段——>BusyBox初始化阶段。
2. **Linux内核源码获取**: 从Linux官方网站或者Git仓库中获取最新或者特定版本的内核源码。内核源码包含了驱动程序、调度器、内存管理等核心组件。 3. **构建根文件系统**: 根文件系统是启动后的Linux系统的...