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 數據的大小
總結
以上是生活随笔為你收集整理的linux内核镜像解压,解压内核镜像的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux测试怎样看,linux入门篇:
- 下一篇: linux 查看正在执行的进程的pid编