嵌入式linux内核启动:汇编时代(1)

2019-07-12 17:20发布

转载请注明出处:青核桃的个人博客:gnssinfo.com    嵌入式linux内核启动:汇编时代(1)
早先也做过一些嵌入式LINUX的项目,改改驱动、增加一些功能等,但未完整重新定义过系统,对嵌入式LINUX只能算是比较熟悉,不能做过信手摘来即用,这次刚好有些时间,也在做新项目,就一起对整个过程再深入一点、OK? 青核桃,加油,相信这是可以完成的。
嗯,从启动开始吧!(BTW:这里用的是AT91SAM9X35的CPU做入口,在内核之前还有bootstrap与u-boot,笔者已重定制了一个版本,是从spi-flash启动的,用SPI0-NPCS0,更多请参照:AT91SAM9x5ek boot模式
注:笔者用的内核版本为:2.6.39   当uBoot将CPU的控制权交给内核后,第一个执行的是Head.S, 它完成了加载内核的大部分工作;针对ARM版,head.S存放在目录:arch/arm/boot/compressed/head.S,这里笔者对其作简要的分析:
//批注:撇开head.S头的版本声明不提,
// 文中先放了几个ASM汇编的宏,符合宏定义,以.macro开头,.endm结尾
// 代码从.section开始执行,启动段定义为.start,以ARM格式执行,
.section “.start”, #alloc, #execinstr
/*
* sort out different calling conventions
*/
.align
.arm @ Always enter in ARM state
start:
.type start,#function
.rept 8
//批注:定义Magic Number、zImage的起止地址
// 打开Cache
…… …… …… …… …… …… …… ……
// 中间省略若干,从这里继续,这里将开始解压内核喽,有一个重要问题:内核是在原地址解压,还是解压到另一个地址运行?
// 即Head.S中的delta。
wont_overwrite:
/*
* If delta is zero, we are running at the address we were linked at.
* r0 = delta
* r2 = BSS start
* r3 = BSS end
* r4 = kernel execution address
* r7 = architecture ID
* r8 = atags pointer
* r11 = GOT start
* r12 = GOT end
* sp = stack pointer
*/
…… …… …… …… …… …… …… ……
// 中间省略若干,从这里继续,这里将开始解压内核喽
/*
* The C runtime environment should now be setup sufficiently.
* Set up some pointers, and start decompressing.
* r4 = kernel execution address
* r7 = architecture ID
* r8 = atags pointer
*/
mov r0, r4
mov r1, sp @ malloc space above stack
add r2, sp, #0×10000 @ 64k max
mov r3, r7
bl decompress_kernel //调用misc.c中的解压函数
bl cache_clean_flush
bl cache_off
mov r0, #0 @ must be zero
mov r1, r7 @ restore architecture number
mov r2, r8 @ restore atags pointer
mov pc, r4 @ call kernel //此处跳转到解压后的地址执行 //?? 解压后跳转到哪了呢?以91SAMx5为例,附一段启动输出来解释, ## Booting kernel from Legacy Image at 22000000 …
   Image Name:   linux-2.6
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    1407128 Bytes = 1.3 MiB
   Load Address: 20008000
   Entry Point:  20008000
   Verifying Checksum … OK
   Loading Kernel Image … OK
OK
从上边的输出,可以看到,这里笔者将内核的压缩文件存放在内存0×22000000处,解压后至0×20800000,所以这里跳转至0×20800000执行。 此时,从arch/arm/kernel/head.S开始启动,如下: /*
* Kernel startup entry point.
* —————————
* 该处描述了执行的前提条件,该条件在解压完成后准备,并在跳转前完成。
* This is normally called from the decompressor code.  The requirements
 * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
 * r1 = machine nr, r2 = atags pointer.
 *
 * This code is mostly position independent, so if you link the kernel at
 * 0xc0008000, you call this at __pa(0xc0008000).
*
* See linux/arch/arm/tools/mach-types for the complete list of machine
* numbers for r1.
*
* We’re trying to keep crap to a minimum; DO NOT add any machine specific
* crap here – that’s what the boot loader (or in extreme, well justified
* circumstances, zImage) is for.
*/
__HEAD
ENTRY(stext)
setmode    PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
@ and irqs disabled
mrc    p15, 0, r9, c0, c0        @ get processor id
bl    __lookup_processor_type        @ r5=procinfo r9=cpuid
movs    r10, r5                @ invalid processor (r5=0)?
THUMB( it    eq )        @ force fixup-able long branch encoding
beq    __error_p            @ yes, error ‘p’ #ifndef CONFIG_XIP_KERNEL
adr    r3, 2f
ldmia    r3, {r4, r8}
sub    r4, r3, r4            @ (PHYS_OFFSET – PAGE_OFFSET)
add    r8, r8, r4            @ PHYS_OFFSET
#else
ldr    r8, =PLAT_PHYS_OFFSET
#endif /*
* r1 = machine no, r2 = atags,
* r8 = phys_offset, r9 = cpuid, r10 = procinfo
*/
bl    __vet_atags
#ifdef CONFIG_SMP_ON_UP
bl    __fixup_smp
#endif
#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
bl    __fixup_pv_table
#endif
bl    __create_page_tables /*
* The following calls CPU specific code in a position independent
* manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of
* xxx_proc_info structure selected by __lookup_processor_type
* above.  On return, the CPU will be ready for the MMU to be
* turned on, and r0 will hold the CPU control register value.
*/
ldr    r13, =__mmap_switched        @ address to jump to after
@ mmu has been enabled
adr    lr, BSYM(1f)            @ return (PIC) address
ARM(    add    pc, r10, #PROCINFO_INITFUNC    )
THUMB(    add    r12, r10, #PROCINFO_INITFUNC    )
THUMB(    mov    pc, r12                )
1:    b    __enable_mmu
ENDPROC(stext)
.ltorg
#ifndef CONFIG_XIP_KERNEL
2:    .long    .
.long    PAGE_OFFSET
#endif
// 最后跳转至__mmap_switched, 其在head-common.S中定义,其详细代码如下: __mmap_switched:
adr    r3, __mmap_switched_data ldmia    r3!, {r4, r5, r6, r7}
cmp    r4, r5                @ Copy data segment if needed
1:    cmpne    r5, r6
ldrne    fp, [r4], #4
strne    fp, [r5], #4
bne    1b mov    fp, #0                @ Clear BSS (and zero fp)
1:    cmp    r6, r7
strcc    fp, [r6],#4
bcc    1b ARM(    ldmia    r3, {r4, r5, r6, r7, sp})
THUMB(    ldmia    r3, {r4, r5, r6, r7}    )
THUMB(    ldr    sp, [r3, #16]        )
str    r9, [r4]            @ Save processor ID
str    r1, [r5]            @ Save machine type
str    r2, [r6]            @ Save atags pointer
bic    r4, r0, #CR_A            @ Clear ‘A’ bit
stmia    r7, {r0, r4}            @ Save control register values
b    start_kernel  //终于结束汇编,开始进入C内核时代了,新时代开始了,下一篇再续。加油!!
ENDPROC(__mmap_switched) 题尾:汇编结束了,笔记也写了不短的时间,明天继续! ---------华丽的分隔线,以下为misc.c内容----------------
unsigned long
decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p,
unsigned long free_mem_ptr_end_p,
int arch_id)
{
unsigned char *tmp; output_data = (unsigned char *)output_start;
free_mem_ptr = free_mem_ptr_p;
free_mem_end_ptr = free_mem_ptr_end_p;
__machine_arch_type = arch_id; arch_decomp_setup(); tmp = (unsigned char *) (((unsigned long)input_data_end) – 4);
output_ptr = get_unaligned_le32(tmp); putstr(“Uncompressing Linux…”);// 这个很熟悉吧,启动时输出的文本,开始解压内核
do_decompress(input_data, input_data_end – input_data,
output_data, error);
putstr(” done, booting the kernel. ”);//解压完成,开始运行
return output_ptr;
}