linux内核镜像解压,解压内核镜像

uboot 将 zimage 复制到内存之后,跳转到 zimage 处开始执行,首先执行的代码是

arch/arm/boot/compressed/head.s 文件,首先是一些涉及不同体系结构调试相关的汇编宏定义

#ifdef debug

#if defined(config_debug_icedcc)

#if defined(config_cpu_v6) || defined(config_cpu_v6k) || defined(config_cpu_v7)

.macro loadsp, rb, tmp

.endm

.macro writeb, ch, rb

mcr p14, 0, \ch, c0, c5, 0

.endm

... 省略 ...

#endif

#endif

#endif

步骤 1

首先是保存 bootloader 传递过来的机器 id 和 atags 起始地址

@ 设置段名

.section ".start", #alloc, #execinstr

.align

.arm @ 设置指令为 arm 模式

start:

.type start,#function @ 声明为函数标签

.rept 7

mov r0, r0

.endr

arm( mov r0, r0 )

arm( b 1f ) @ 向下跳转

thumb( adr r12, bsym(1f) )

thumb( bx r12 )

.word 0x016f2818 @ magic numbers to help the loader

.word start @ 程序其实地址,即 zimage 的地址

.word _edata @ zimage 结束地址

thumb( .thumb )

1: mov r7, r1 @ 保存 bootloader 传递过来的机器 id

mov r8, r2 @ 保存 bootloader 传递过来的 atags 起始地址

步骤 2

关闭中断、进入 svc 模式

#ifndef __arm_arch_2__

mrs r2, cpsr @ get current mode

tst r2, #3 @ not user?

bne not_angel

mov r0, #0x17 @ 进入 svc 模式

arm( swi 0x123456 ) @ angel_swi_arm

thumb( svc 0xab ) @ angel_swi_thumb

not_angel:

mrs r2, cpsr

orr r2, r2, #0xc0 @ 关闭 fiq 和 irq

msr cpsr_c, r2

#else

teqp pc, #0x0c000003 @ turn off interrupts

步骤 3

为了加速解压过程,打开缓存

#ifdef config_auto_zreladdr

@ 如果配置了运行时自动计算重定位地址

@ 则根据当前 pc 位置和 text_offset 计算出解压位置

@ text_offset 在 arch/arm/makefile 中指定,表示的是相对于内存起始地址的偏移

@ 定义为 textofs-y := 0x00008000 text_offset := $(textofs-y)

mov r4, pc

and r4, r4, #0xf8000000 @ 这里假设 zimage 是被放在内存的前 128mb 内的

add r4, r4, #text_offset @ 得到 zimage 解压的物理地址

#else

@ 这里是直接在 arch/arm/mach-s5p4418/makefile.boot 中定义的

@ 定义为 zreladdr-y := 0x40008000

ldr r4, =zreladdr @ r4 存放解压后内核存放的起始地址

#endif

@ 打开缓存和 mmu

bl cache_on

步骤 4

检查解压后的内核镜像是否与当前镜像发生覆盖

restart: adr r0, lc0

ldmia r0, {r1, r2, r3, r6, r10, r11, r12} @ 将 lc0 数据放入寄存器中

ldr sp, [r0, #28] @ 更新栈指针位置

sub r0, r0, r1 @ 计算当前程序位置和链接地址间的偏移量

add r6, r6, r0 @ 得到 zimage 运行地址的结束位置

add r10, r10, r0 @ 存放未压缩内核大小的运行地址

/*

* 内核编译系统会将未压缩的内核大小追加到压缩后的内核数据之后

* 并以小端格式存储

*/

@ 取出未压缩内核大小放入 r9

ldrb r9, [r10, #0]

ldrb lr, [r10, #1]

orr r9, r9, lr, lsl #8

ldrb lr, [r10, #2]

ldrb r10, [r10, #3]

orr r9, r9, lr, lsl #16

orr r9, r9, r10, lsl #24

#ifndef config_zboot_rom

/* 在栈指针之上分配内存空间放到 r10 中 (64k max) */

add sp, sp, r0

add r10, sp, #0x10000

#else

mov r10, r6

#endif

mov r5, #0 @ init dtb size to 0

/*

* 检查解压后的内核是否会覆盖当前代码

* r4 = 最终解压后的内核起始地址

* r9 = 解压后的内核镜像大小

* r10 = 当前镜像结束地址,其中包括所分配的内存区域

* we basically want:

* r4 - 16k 页表 >= r10 -> ok

* r4 + image length <= address of wont_overwrite -> ok

*/

add r10, r10, #16384 @ 内核镜像前的 16kb 页表

cmp r4, r10

bhs wont_overwrite @ 解压后的内核起始地址大于等于 r10

add r10, r4, r9

adr r9, wont_overwrite

cmp r10, r9 @ 或者解压后的内核结束地址在 wont_overwrite 地址之前

bls wont_overwrite

/*

* r6 = _edata

* r10 = end of the decompressed kernel

*/

@ 当前代码段向后移动到的目的地址,不足 256 字节的补足 256 字节,向上取整

add r10, r10, #((reloc_code_end - restart + 256) & ~255)

bic r10, r10, #255

@ 当前代码段起始地址,以 32 字节为单位,向下取整

adr r5, restart

bic r5, r5, #31

sub r9, r6, r5 @ 需要移动镜像的大小

add r9, r9, #31 @ 以 32 字节为单位向上取整

bic r9, r9, #31

add r6, r9, r5 @ 循环结束地址

add r9, r9, r10 @ 目的地址结束位置

@ 循环移动当前镜像

1: ldmdb r6!, {r0 - r3, r10 - r12, lr}

cmp r6, r5

stmdb r9!, {r0 - r3, r10 - r12, lr}

bhi 1b

sub r6, r9, r6 @ 代码重定位的偏移地址

#ifndef config_zboot_rom

add sp, sp, r6 @ 对栈指针也进行重定位

#endif

bl cache_clean_flush

@ 跳转到重定位后的镜像 restart 处重新执行,检查是否存在覆盖

adr r0, bsym(restart)

add r0, r0, r6

mov pc, r0

步骤 5

清理 bss 段、解压内核、关闭缓存,最后启动内核

/* 至此,当前镜像和解压后的内核不会发生覆盖问题 */

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

* r5 = appended dtb size (0 if not present)

* r7 = architecture id

* r8 = atags pointer

* r11 = got start

* r12 = got end

* sp = stack pointer

*/

orrs r1, r0, r5

beq not_relocated

add r11, r11, r0

add r12, r12, r0

not_relocated: mov r0, #0

1: str r0, [r2], #4 @ 清理 bss 段

str r0, [r2], #4

str r0, [r2], #4

str r0, [r2], #4

cmp r2, r3

blo 1b

/*

* 至此,c 语言运行环境已经初始化完成

* r4 = 解压后的内核起始地址

* r7 = 机器 id

* r8 = atags 指针

*/

mov r0, r4

mov r1, sp @ 在栈指针之上分配内存空间

add r2, sp, #0x10000 @ 64k max

mov r3, r7

bl decompress_kernel @ 解压内核

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

arm( mov pc, r4 ) @ 启动内核

thumb( bx r4 ) @ entry point is always arm

.align 2

.type lc0, #object

lc0: .word lc0 @ r1:lc0 的链接地址

.word __bss_start @ r2:bss 段的起始地址

.word _end @ r3:bss 段的结束地址

.word _edata @ r6:zimage 的结束地址

.word input_data_end - 4 @ r10:存放未压缩的内核大小的链接地址

.word _got_start @ r11:全局偏移表起始位置

.word _got_end @ ip:全局偏移表结束位置

.word .l_user_stack_end @ sp:栈指针

.size lc0, . - lc0 @ 该 lc0 数据的大小

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值