zoukankan      html  css  js  c++  java
  • 解压内核镜像

    步骤 0

    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 数据的大小
  • 相关阅读:
    nginx.conf中配置laravel框架站点
    centos6.4下安装php7+nginx+mariadb环境
    Windows Terminal 安装和运行
    微软 WSL 重装操作系统
    Pulumi 如何在 Windows 环境中设置
    Ubuntu 20.04 安装 JDK
    代码的 Lint 是什么意思
    CentOS 8 手动安装 Go 1.16 版本
    Raspberry Pi 安装 go 后提示错误 Exec format error
    系统管理--查看网卡、内存等
  • 原文地址:https://www.cnblogs.com/jiau/p/12860249.html
Copyright © 2011-2022 走看看