linux非解压代码的启动过程分析 unicore head.S vmlinux解压后的代码运行 临时MMU的建立

我们首先看看内核的生成过程:


非压缩内核映象由 make Image 命令产生。其生成过程是:

(1) 内核的各个模块经过编译,链接,在内核源代码的顶层目录下生成 vmlinux 文件,这是

一个 ELF 格式的映象

(2) 用 arm-linux-objcopy 命令把 vmlinux 转换为二进制格式映象 arch/arm/boot/Image

压缩内核映象由 make zImage 命令产生,其生成过程是:

(1) 用 gzip 对 非 压 缩 内 核 二 进 制 映 象 arch/arm/boot/ Image 进 行 压 缩 , 生 成

arch/arm/boot/compressed/piggy.gz 文件

(2) arch/arm/boot/compressed/目录下有三个文件:piggy.s,定义了一个包含./piggy.gz 文件的

数据段;head.S 包含了对 gzip 压缩过的内核进行解压的代码;vmlinux-lds 是链接脚本。

这几个文件经过编译链接,在 arch/arm/boot/compressed/目录下产生 vmlinux 文件,这是

一个 ELF 格式的映象

(3) 用 arm-linux-objcopy 命令把 arch/arm/boot/compressed/vmlinux 转换为二进制格式映象:

arch/arm/boot/compressed/zImage

 

 

/*

 * linux/arch/unicore32/kernel/head.S

  */

 

 

#if (PHYS_OFFSET & 0x003fffff)

#error "PHYS_OFFSET must be at an even 4MiB boundary!"

#endif

 

#define KERNEL_RAM_VADDR(PAGE_OFFSET + KERNEL_IMAGE_START)  @0xc0000000+0x8000

#define KERNEL_RAM_PADDR(PHYS_OFFSET + KERNEL_IMAGE_START)  @0x40000000+0x8000

 

#define KERNEL_PGD_PADDR(KERNEL_RAM_PADDR - 0x1000)     @0x40008000-0x1000 = 0x40007000

#define KERNEL_PGD_VADDR(KERNEL_RAM_VADDR - 0x1000)     @0xc0008000-0x1000 = 0x40007000

 

#define KERNEL_STARTKERNEL_RAM_VADDR                @0xc0008000

#define KERNEL_END_end                            @in the vmlinux-linux.lds

 

/*

 * swapper_pg_dir is the virtual address of the initial page table.

 * We place the page tables 4K below KERNEL_RAM_VADDR.  Therefore, we must

 * make sure that KERNEL_RAM_VADDR is correctly set.  Currently, we expect

 * the least significant 16 bits to be 0x8000, but we could probably

 * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x1000.

 */

#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000     @the least 16 bite must be 0x8000 as the error said below

#error KERNEL_RAM_VADDR must start at 0xXXXX8000      @预留了32k的空间

#endif

 

.globlswapper_pg_dir

.equswapper_pg_dir, KERNEL_RAM_VADDR - 0x1000        @swapper_pg_dir = KERNEL_PGD_VDIR =0xc0007000

 

/*

 * Kernel startup entry point.

 * ---------------------------

 *

 * This is normally called from the decompressor code.  The requirements

 * are: MMU = off, D-cache = off, I-cache = dont care

 *

 * This code is mostly position independent, so if you link the kernel at

 * 0xc0008000, you call this at __pa(0xc0008000).

  注意前面这段代码肯定是地址无关的(也就是如上所述position independent

 */

__HEAD

ENTRY(stext)

@ set asr

movr0, #PRIV_MODE@ ensure priv mode  PRIV_MODE = 0x13 =0b10011

orr0, #PSR_R_BIT | PSR_I_BIT@ disable irqs   关闭实时模式和屏蔽中断

mov.aasr, r0

 

@ process identify

movcr0, p0.c0, #0@ cpuid

movlr1, 0xff00ffff@ mask

movlr2, 0x4d000863@ value

andr0, r1, r0

cxor.ar0, r2

bne__error_p@ invalid processor id

 

/*

 * Clear the 4K level 1 swapper page table

 */

movlr0, #KERNEL_PGD_PADDR@ page table address  0x40007000

movr1, #0

addr2, r0, #0x1000               @so r2 =0x40008000

101:stw.wr1, [r0]+, #4                 @clear 0x40007000-0x40008000

stw.wr1, [r0]+, #4

stw.wr1, [r0]+, #4

stw.wr1, [r0]+, #4

cxor.ar0, r2

bne101b

 

movlr4, #KERNEL_PGD_PADDR@ page table address  r4 = 0x40007000

movr7, #PMD_TYPE_SECT | PMD_PRESENT@ page size: section  PMD_TYPE_SECT 3<<0 即0b11 PMD_PRESENT 1<<2

orr7, r7, #PMD_SECT_CACHEABLE@ cacheable  其他的对应unicore一级页表超页

orr7, r7, #PMD_SECT_READ | PMD_SECT_WRITE | PMD_SECT_EXEC  @读写及可执行权  所有综合以上3条指令应该是准备超页(4M)的属性数据

 

/*

 * Create identity mapping for first 4MB of kernel to

 * cater for the MMU enable.  This identity mapping

 * will be removed by paging_init().  We use our current program

 * counter to determine corresponding section base address.

        这是第一次映射 va = pa 的映射 pgd页表项地址为0x40007000+0x400 

 */

movr6, pc                          @镜像解压后的链接地址是0xc0008000  而实际上是在0x40008000那里开始跑所以这里得到的pc应该是0x40008xxx

movr6, r6 >> #22@ start of kernel section得到物理地址的高10位 即0b0100_0000_00

orr1, r7, r6 << #22@ flags + kernel phys base

stwr1, [r4+], r6 << #2@ identity mapping  至此已为0x40000000后的4M建立了映射0x40400000  而r6右移动两位是一个页表项有4个字节,10有1024个表项,所以站4k,这里的意思是,由于这里的cpu发出的地址仍然为0x4xxxxxxx 当开MMU时,在前面的4M物理地址和虚拟地址完全相同,当pc发出的地址经过MMU后找到的物理地址数据其实还是一样的,此时页表项的内容为地址0x40007000+0x400

 

/*

 * Now setup the pagetables for our kernel direct

 * mapped region.

       这是第二次的映射 建立内核虚拟地址的运用

       */

addr0, r4,  #(KERNEL_START & 0xff000000) >> 20   @KERNEL_START = 0xc0008000  r0 = 0x40007000 + 0xc00

stw.wr1, [r0+], #(KERNEL_START & 0x00c00000) >> 20

 @页表项内容地址0x40007000+0xc00  至此内核后0xc0000000后面4M内容已经建立了页表,以上两句话相当于KERNEL_START&0b_1111_1111_1100(高10)位。而r1为0b_0100_0000_00。所以还身下22位可以变动。所以为4M

movlr6, #(KERNEL_END - 1)  @获得kernel的最后虚拟地址

addr0, r0, #4    @r0 = 0x40007000+0xc00+4 也就是下一个页表项,以免内核超过4M

addr6, r4, r6 >> #20  @得到内核最后的页表项地址

102:csub.ar0, r6

addr1, r1, #1 << 22 @下一个4M的物理地址高10位和相应的属性位

bua103f

stw.wr1, [r0]+, #4

b102b   @至此内核所有的虚拟地址都建立好了超页的页表项  此时页表项有两类,至少两个,一个是平坦的即va=pa 而另外一个是内核虚拟地址开始即0xc0000000到内核结束地址的映射

103:

/*

 * Then map first 4MB of ram in case it contains our boot params.

 */

addr0, r4, #PAGE_OFFSET >> 20     

 @这里有电特殊,如果我们的其实链接地址不是0xc0008000而是如0xc0048000这样超过4M的地址,那么这里就是必须的了

orr6, r7, #(PHYS_OFFSET & 0xffc00000)  

@但是此时在这里其实和上面一段是完全一样的

stwr6, [r0]

 

ldw      r15, __switch_data        @ address to jump to after  这里是我们关键之中的关键,这里r15必定会变成0xc0008xxx,所以从这个函数开始cpu使用虚拟地址取数据

 

/*

 * Initialise TLB, Caches, and MMU state ready to switch the MMU

 * on.

 */

movr0, #0

movcp0.c5, r0, #28@ cache invalidate all  这里对照手册很容易明白

nop8

movcp0.c6, r0, #6@ TLB invalidate all

nop8

 

/*

 * ..V. .... ..TB IDAM

 * ..1. .... ..01 1111

 */

movlr0, #0x201f@ control register setting 对协处理器控制器的数据准备

 

/*

 * Setup common bits before finally enabling the MMU.  Essentially

 * this is just loading the page table pointer and domain access

 * registers.

 */

#ifndef CONFIG_ALIGNMENT_TRAP      @属性位如DCHCHE使能 CACHE策略等等

andnr0, r0, #CR_A

#endif

#ifdef CONFIG_CPU_DCACHE_DISABLE

andnr0, r0, #CR_D

#endif

#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH

andnr0, r0, #CR_B

#endif

#ifdef CONFIG_CPU_ICACHE_DISABLE

andnr0, r0, #CR_I

#endif

 

movcp0.c2, r4, #0@ set pgd  pgd物理地址

b__turn_mmu_on

ENDPROC(stext)

 

/*

 * Enable the MMU.  This completely changes the structure of the visible

 * memory space.  You will not be able to trace execution through this.

 *

 *  r0  = cp#0 control register

 *  r15 = *virtual* address to jump to upon completion

 */

.align5

__turn_mmu_on:

movr0, r0

movcp0.c1, r0, #0@ write control reg

nop       @ fetch inst by phys addr  注意在这条指令之后有微妙而十分关键的变化cpu这这条指令之前(包括这条指令,都是从0x40008xxx地址取指令的,即使movcp0.c1, r0, #0 这条指令运行之后,MMU已经开了,但是由于在0x40007000+0x400已经建立了页表所以nop这条指令可以正常取到

movpc, r15                         @当pc被赋值为r15后,里面的pc就开始从0xc0008xxx取数据了

nop8@ fetch inst by phys addr

ENDPROC(__turn_mmu_on)

 

/*

 * Setup the initial page tables.  We only setup the barest

 * amount which are required to get the kernel running, which

 * generally means mapping in the kernel code.

 *

 * r9  = cpuid

 * r10 = procinfo

 *

 * Returns:

 *  r0, r3, r6, r7 corrupted

 *  r4 = physical page table address

 */

.ltorg

 

.align2

.type__switch_data, %object

__switch_data:

.long__mmap_switched

.long__bss_start@ r6

.long_end@ r7

.longcr_alignment@ r8

.longinit_thread_union + THREAD_START_SP @ sp

 

/*

 * The following fragment of code is executed with the MMU on in MMU mode,

 * and uses absolute addresses; this is not position independent.

 *

 *  r0  = cp#0 control register

 */

__mmap_switched:

adrr3, __switch_data + 4

 

ldm.w(r6, r7, r8), [r3]+

ldwsp, [r3]

 

movfp, #0@ Clear BSS (and zero fp)

203:csub.ar6, r7

bea204f

stw.wfp, [r6]+,#4

b203b

204:

andnr1, r0, #CR_A@ Clear 'A' bit

stm(r0, r1), [r8]+@ Save control register values

bstart_kernel                    @前面说过在0mmap_switched函数之后的已经变成了0xc0008xxx所以在这里肯定就已经是kernel的虚拟地址了

ENDPROC(__mmap_switched)

 

/*

 * Exception handling.  Something went wrong and we can't proceed.  We

 * ought to tell the user, but since we don't have any guarantee that

 * we're even running on the right architecture, we do virtually nothing.

 *

 * If CONFIG_DEBUG_LL is set we try to print out something about the error

 * and hope for the best (useful if bootloader fails to pass a proper

 * machine ID for example).

 */

__error_p:

#ifdef CONFIG_DEBUG_LL

adrr0, str_p1

b.lprintascii

movr0, r9

b.lprinthex8

adrr0, str_p2

b.lprintascii

901:nop8

b901b

str_p1:.asciz"\nError: unrecognized processor variant (0x"

str_p2:.asciz").\n"

.align

#endif

ENDPROC(__error_p)



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值