科技 > 操作系统 > Linux

解压内核镜像

263人参与2020-05-10

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 数据的大小
原文地址:https://www.cnblogs.com/jiau/p/12860249.html

留下您精彩的一笔!!点此进行留言回复

最近更新

linux系统怎么判断路由转发功能是否开启?

06-22

深度Linux自带的录屏工具怎么录制gif图?

06-22

Linux怎么查找影子文件并进入?

06-22

Linux如何安装运行.AppImage文件?.AppImage文件两种运行方法介绍

06-22

linux怎么添加每天定时任务? linux添加定时任务的教程

06-22

Linux下如何安装deb格式的安装包?deb安装包安装教程

06-22

推荐阅读

解压内核镜像

05-10

基于mosquitto的MQTT客户端实现C语言

10-16

网络客户端工具

07-03

ubuntu18.04突然无法上网解决方法

08-19

shell 中 exit0 exit1 的区别

07-01

QT获取linux下的当年用户名

12-05

Linux系统配置rdate时间服务器教程

05-27

linux查看当前shell的方法

11-20

总结Linux中用于文本处理的awk、sed、grep命令用法

07-31

Linux中打开文件显示行号相关命令

06-05

docker tar 镜像 容器相互转换

05-31

CentOS随笔 - 修改CentOS7的IP

07-05

热门评论