解压内核镜像
程序员文章站
2022-07-06 09:54:45
步骤 0 uboot 将 zImage 复制到内存之后,跳转到 zImage 处开始执行,首先执行的代码是 arch/arm/boot/compressed/head.S 文件,首先是一些涉及不同体系结构调试相关的汇编宏定义 ifdef DEBUG if defined(CONFIG_DEBUG_I ......
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
上一篇: centos7 ——wget安装
下一篇: 我跟一车男的睡觉呢