linux源码分析之解压内核映像 head.s

深入解析Linux UBoot 1.3.3核心概念与应用 UBoot,全称为 Universal Boot Loader,是一个开放源代码的引导加载程序,广泛应用于嵌入式设备,尤其是嵌入式Linux系统中。它作为硬件和操作系统之间的桥梁,负责在系统加电后初始化硬件设备,加载操作系统内核到内存中,并将控制权交给内核,从而启动操作系统。common/:这个目录包含了所有平台共用的代码,如命令行接口(CLI)和通用的驱动程序。board/:这个目录包含了特定硬件平台的初始化代码和配置文件。每一个子目录通常对应一个特定的硬件平台或者开发板。 阅读详情

arm-linux源码分析之解压内核映像

http://blog.youkuaiyun.com/BoySKung/archive/2008/12/05/3448083.aspx#


linux-2.6.20.6/arch/arm/boot/compressed/head.S
 
开头有一段宏定义,我们只看其中一段,分析一下gnu arm汇编的宏定义
 
#elif defined(CONFIG_ARCH_S3C2410)
              .macro loadsp, rb
              mov /rb, #0x50000000
              add  /rb, /rb, #0x4000 * CONFIG_S3C2410_LOWLEVEL_UART_PORT
              .endm
#else
这里定义了一个宏,宏名是loadsp,rb是这个宏的参数。宏的参数在被引用时必须加”/”,如:
mov /rb, #0x50000000.
 
宏定义结束之后定义了一个段,
              .section ".start", #alloc, #execinstr
 
这个段的段名是 .start,#alloc表示Section contains allocated data, #execinstr表示Section contains executable instructions.
/*
 * sort out different calling conventions
 */
              .align
start:
              .type       start,#function /*.type指定start这个符号是函数类型*/
              .rept 8
              mov r0, r0 //将此命令重复8次,相当于nop,这里为什么这样做还不清楚??
              .endr
 
              b     1f
              .word      0x016f2818           @ Magic numbers to help the loader
              .word      start               @ absolute load/run zImage address
              .word      _edata                   @ zImage end address
1:            mov r7, r1                    @ save architecture ID
              mov r8, r2                    @ save atags pointer
r1和r2中分别存放着由bootloader传递过来的architecture ID和指向标记列表的指针。这里将这两个参数先保存。
 
 
 
 
 
#ifndef __ARM_ARCH_2__
              /*
               * Booting from Angel - need to enter SVC mode and disable
               * FIQs/IRQs (numeric definitions from angel arm.h source).
               * We only do this if we were in user mode on entry.
               */
读取cpsr并判断是否处理器处于supervisor模式——从u-boot进入kernel,系统已经处于SVC32模式;而利用angel进入则处于user模式,还需要额外两条指令。之后是再次确认中断关闭,并完成cpsr写入
 
Angel 是 ARM 的调试协议,现在用的 MULTI-ICE 用的是 RDI 通讯协议, ANGLE 需要在板子上有 驻留程序,然后通过 串口就可以调试了
 
 
这里介绍一下半主机.
 
半主机是用于 ARM 目标的一种机制,可将来自应用程序代码的输入/输出请求
传送至运行调试器的主机。 例如,使用此机制可以启用 C 库中的函数,如
printf() 和 scanf(),来使用主机的屏幕和键盘,而不是在目标系统上配备屏幕和
键盘。
 
半主机是通过一组定义好的软件指令(如 swi)来实现的,这些指令通过程序控
制生成异常。 应用程序调用相应的半主机调用,然后调试代理处理该异常。 调
试代理提供与主机之间的必需通信。
 
              mrs  r2, cpsr          @ get current mode
              tst    r2, #3                    @ not user?
              bne  not_angel
 
下面两行实现了在主机和 ARM 目标之间启用调试 I/O 功能,
 
              mov r0, #0x17              @ angel_SWIreason_EnterSVC
              swi  0x123456              @ angel_SWI_ARM
0x17是angel_SWIreason_EnterSVC半主机操作,将处理器设置为超级用户模式,通过设置新 CPSR 中的两个中断掩码位来禁用所有中断。0x123456是arm指令集的半主机操作编号
 
not_angel:  //不是通过angel调试进入内核
              mrs  r2, cpsr          @ turn off interrupts to
              orr   r2, r2, #0xc0         @ prevent angel from running
              msr  cpsr_c, r2   //这里将cpsr中I、F位分别置“1”,关闭IRQ和FIQ
#else
              teqp pc, #0x0c000003          @ turn off interrupts
常用 TEQP PC,#(新模式编号) 来改变模式
#endif
 
 
 
 
链接器会把一些处理器相关的代码链接到这个位置,也就是arch/arm/boot/compressed/head-xxx.S文件中的代码。在那个文件里会对I/D cache以及MMU进行一些操作
 
/*
               * Note that some cache flushing and other stuff may
               * be needed here - is there an Angel SWI call for this?
               */
 
              /*
               * some architecture specific code can be inserted
               * by the linker here, but it should preserve r7, r8, and r9.
               */
 
              .text
              adr   r0, LC0 //当前运行时LC0符号所在地址位置
              ldmia       r0, {r1, r2, r3, r4, r5, r6, ip, sp}
              subs r0, r0, r1        @ calculate the delta offset //这里获得当前运行地址与链接地址
                                          @ if delta is zero, we are   //的偏移量,存入r0中。
              beq  not_relocated         @ running at the address we
                                          @ were linked at.
 
上面这几行代码用于判断代码是否已经重定位到内存中,LC0这个符号在288行定义。
              .type       LC0, #object
LC0:              .word      LC0               @ r1 //这个要加载到r1中的LC0是链接时LC0的地址
              .word      __bss_start            @ r2
              .word      _end                     @ r3
              .word      zreladdr          @ r4
              .word      _start                    @ r5
              .word      _got_start              @ r6
              .word      _got_end        @ ip
              .word      user_stack+4096           @ sp
通过当前运行时LC0的地址与链接器所链接的地址进行比较判断。若相等则是运行在链接的地址上。
 
 
如果不是运行在链接的地址上,则下面的代码必须运行
              /*
               * We're running at a different address.  We need to fix
               * up various pointers:
               *   r5 - zImage base address
               *   r6 - GOT start
               *   ip - GOT end
               */
              add  r5, r5, r0 //修改内核映像基地址
              add  r6, r6, r0
              add  ip, ip, r0 //修改got表的起始和结束位置
 
#ifndef CONFIG_ZBOOT_ROM
              /*若没有定义CONFIG_ZBOOT_ROM,此时运行的是完全位置无关代码
位置无关代码,也就是不能有绝对地址寻址。所以为了保持相对地址正确,
需要将bss段以及堆栈的地址都进行调整
               * If we're running fully PIC === CONFIG_ZBOOT_ROM = n,
               * we need to fix up pointers into the BSS region.
               *   r2 - BSS start
               *   r3 - BSS end
               *   sp - stack pointer
               */
              add  r2, r2, r0
              add  r3, r3, r0
              add  sp, sp, r0
 
              /*
               * Relocate all entries in the GOT table.
               */
1:            ldr   r1, [r6, #0]            @ relocate entries in the GOT
              add  r1, r1, r0        @ table.  This fixes up the
              str   r1, [r6], #4            @ C references.
              cmp r6, ip
              blo   1b
#else //若定义了CONFIG_ZBOOT_ROM,只对got表中在bss段以外的符号进行重定位
//为什么要这样做呢??我也不清楚
              /*
               * Relocate entries in the GOT table.  We only relocate
               * the entries that are outside the (relocated) BSS region.
               */
1:            ldr   r1, [r6, #0]            @ relocate entries in the GOT
              cmp r1, r2                    @ entry < bss_start ||
              cmphs     r3, r1                    @ _end < entry
              addlo       r1, r1, r0        @ table.  This fixes up the
              str   r1, [r6], #4            @ C references.
              cmp r6, ip
              blo   1b
#endif
 
 
 
 
 
如果运行当前运行地址和链接地址相等,则不需进行重定位。直接清除bss段
not_relocated: mov r0, #0
1:            str   r0, [r2], #4            @ clear bss
              str   r0, [r2], #4
              str   r0, [r2], #4
              str   r0, [r2], #4
              cmp r2, r3
              blo   1b
 
之后跳转到cache_on处
              /*
               * The C runtime environment should now be setup
               * sufficiently.  Turn the cache on, set up some
               * pointers, and start decompressing.
               */
              bl     cache_on
 
cache_on在327行定义
              .align       5
cache_on:       mov r3, #8                    @ cache_on function
              b     call_cache_fn
 
把r3的值设为8有什么用呢?下面会看到。这里又跳转到call_cache_fn。这个函数的定义在512行
 
call_cache_fn: adr   r12, proc_types  //把proc_types的地址加载到r12中
#ifdef CONFIG_CPU_CP15
              mrc p15, 0, r6, c0, c0   @ get processor ID
#else
              ldr   r6, =CONFIG_PROCESSOR_ID
#endif
1:            ldr   r1, [r12, #0]          @ get value
              ldr   r2, [r12, #4]          @ get mask
              eor   r1, r1, r6        @ (real ^ match)
              tst    r1, r2                    @       & mask
              addeq      pc, r12, r3             @ call cache function
              add  r12, r12, #4*5
              b     1b
 
这一段代码首先获得当前处理器id,然后查proc_types表,也就是处理器类型表与获得的处理器id进行比较,当找到相应的处理器后,就加载对应的cache处理函数。
addeq      pc, r12, r3             @ call cache function
这里用到了上面说的r3,他的值是8,也就是一个偏移量,r12中存储的是某个处理器相关处理模块的基地址。
 
proc_type的定义如下,在541行
 
              .type       proc_types,#object
proc_types:
              .word      0x41560600           @ ARM6/610
              .word      0xffffffe0
              b     __arm6_mmu_cache_off      @ works, but slow
              b     __arm6_mmu_cache_off
              mov pc, lr
@           b     __arm6_mmu_cache_on              @ untested
@           b     __arm6_mmu_cache_off
@           b     __armv3_mmu_cache_flush
 
              .word      0x00000000           @ old ARM ID
              .word      0x0000f000
              mov pc, lr
              mov pc, lr
              mov pc, lr
 
              .word      0x41007000           @ ARM7/710
              .word      0xfff8fe00
              b     __arm7_mmu_cache_off
              b     __arm7_mmu_cache_off
              mov pc, lr
 
              .word      0x41807200           @ ARM720T (writethrough)
              .word      0xffffff00
              b     __armv4_mmu_cache_on
              b     __armv4_mmu_cache_off
              mov pc, lr
 
              .word      0x41007400           @ ARM74x
              .word      0xff00ff00
              b     __armv3_mpu_cache_on
              b     __armv3_mpu_cache_off
              b     __armv3_mpu_cache_flush
             
              .word      0x41009400           @ ARM94x
              .word      0xff00ff00
              b     __armv4_mpu_cache_on
              b     __armv4_mpu_cache_off
              b     __armv4_mpu_cache_flush
 
              .word      0x00007000           @ ARM7 IDs
              .word      0x0000f000
              mov pc, lr
              mov pc, lr
              mov pc, lr
 
              @ Everything from here on will be the new ID system.
 
              .word      0x4401a100           @ sa110 / sa1100
              .word      0xffffffe0
              b     __armv4_mmu_cache_on
              b     __armv4_mmu_cache_off
              b     __armv4_mmu_cache_flush
 
              .word      0x6901b110           @ sa1110
              .word      0xfffffff0
              b     __armv4_mmu_cache_on
              b     __armv4_mmu_cache_off
              b     __armv4_mmu_cache_flush
 
              @ These match on the architecture ID
 
              .word      0x00020000    @ ARMv4T //这个就是我们要找的arm920t的处理器相关数
              .word      0x000f0000        //据,偏移8后刚好是b   __armv4_mmu_cache_on
              b     __armv4_mmu_cache_on  //指令的地址
              b     __armv4_mmu_cache_off
              b     __armv4_mmu_cache_flush
 
              .word      0x00050000           @ ARMv5TE
              .word      0x000f0000
              b     __armv4_mmu_cache_on
              b     __armv4_mmu_cache_off
              b     __armv4_mmu_cache_flush
 
              .word      0x00060000           @ ARMv5TEJ
              .word      0x000f0000
              b     __armv4_mmu_cache_on
              b     __armv4_mmu_cache_off
              b     __armv4_mmu_cache_flush
 
              .word      0x0007b000           @ ARMv6
              .word      0x0007f000
              b     __armv4_mmu_cache_on
              b     __armv4_mmu_cache_off
              b     __armv6_mmu_cache_flush
 
              .word      0                   @ unrecognised type
              .word      0
              mov pc, lr
              mov pc, lr
              mov pc, lr
 
              .size proc_types, . - proc_types
 
当找到我和我们处理器后,就调用相应的处理函数,我根据我们的arm920t处理器,这里应该调用__armv4_mmu_cache_on,这句调用指令在605行
      
              .word      0x00020000           @ ARMv4T
              .word      0x000f0000
              b     __armv4_mmu_cache_on
              b     __armv4_mmu_cache_off
              b     __armv4_mmu_cache_flush
 
__armv4_mmu_cache_on的在424行定义,
 
__armv4_mmu_cache_on:
              mov r12, lr
              bl     __setup_mmu
              mov r0, #0
              mcr p15, 0, r0, c7, c10, 4     @ drain write buffer
              mcr p15, 0, r0, c8, c7, 0      @ flush I,D TLBs
              mrc p15, 0, r0, c1, c0, 0      @ read control reg
              orr   r0, r0, #0x5000             @ I-cache enable, RR cache replacement
              orr   r0, r0, #0x0030   
              bl     __common_mmu_cache_on
              mov r0, #0
              mcr p15, 0, r0, c8, c7, 0      @ flush I,D TLBs
              mov pc, r12  //返回到cache_on
这 里首跳转到__setup_mmu,然后清空write buffer、I/Dcache、TLB.接着打开i-cache,设置为Round-robin replacement。调用__common_mmu_cache_on,打开mmu和d-cache.把页表基地址和域访问控制写入协处理器寄存器 c2、c3. __common_mmu_cache_on函数数定义在450行。
__common_mmu_cache_on:
#ifndef DEBUG
              orr   r0, r0, #0x000d             @ Write buffer, mmu
#endif
              mov r1, #-1 //-1的补码是ffff ffff,
              mcr p15, 0, r3, c2, c0, 0      @ load page table pointer
              mcr p15, 0, r1, c3, c0, 0      @ load domain access control //将domain access control寄存
              b     1f                                 //全部置’1’
              .align       5                   @ cache line aligned
1:            mcr p15, 0, r0, c1, c0, 0      @ load control register
              mrc p15, 0, r0, c1, c0, 0      @ and read it back to
              sub  pc, lr, r0, lsr #32    @ properly flush pipeline
 
重占来看一下__setup_mmu这个函数,定义在386行
 
__setup_mmu:       sub  r3, r4, #16384        @ Page directory size
              bic   r3, r3, #0xff          @ Align the pointer
              bic   r3, r3, #0x3f00
这里r4中存放着内核执行地址,将16K的一级页表放在这个内核执行地址下面的16K空间里,上面通过 sub  r3, r4, #16384  获得16K空间后,又将页表的起始地址进行16K对齐放在r3中。即ttb的低14位清零。
 
/*
 * Initialise the page tables, turning on the cacheable and bufferable
 * bits for the RAM area only.
 */
//初始化页表,并在RAM空间里打开cacheable 和bufferable位
              mov r0, r3
              mov r9, r0, lsr #18
              mov r9, r9, lsl #18         @ start of RAM
              add  r10, r9, #0x10000000    @ a reasonable RAM size
 
上 面这几行把一级页表的起始地址保存在r0中,并通过r0获得一个ram起始地址(256K对齐),并从这个起始地址开始的256M ram空间对应的描述符的C和B位均置”1” (参考arm920t datasheet 3.3.3, table 3-2 level one descryiptor bits), r9和r10中存放了这段内存的起始地址和结束地址
 
              mov r1, #0x12 //一级描述符的bit[1:0]为10,表示这是一个section描述符。bit[4]
//为1(参考arm9205 datasheet 3.3.3 table 3-2 level one
//descryiptor bits)此时bit[8:5]均为0,选择了D0域。
 
              orr   r1, r1, #3 << 10 //一级描述符的access permission bits bit[11:10]为11. 即
                         //all access types permitted in both modes
// (参考arm920t datasheet 3.3.3, table 3-2 level
//one descryiptor bits, 3.6, table 3-11 interpreting access
// permission(AP) bit)
 
              add  r2, r3, #16384  //一级描述符表的结束地址存放在r2中。
 
 
1:            cmp r1, r9                    @ if virt > start of RAM
              orrhs       r1, r1, #0x0c         @ set cacheable, bufferable
              cmp r1, r10                  @ if virt > end of RAM
              bichs       r1, r1, #0x0c         @ clear cacheable, bufferable
              str   r1, [r0], #4            @ 1:1 mapping
              add  r1, r1, #1048576
              teq   r0, r2
              bne  1b
 
上 面这段就是对一级描述符表(页表)的初始化,首先比较这个描述符所描述的地址是否在那个256M的空间中,如果在则这个描述符对应的内存区域是 cacheable ,bufferable。如果不在则noncacheable, nonbufferable.然后将描述符写入一个一级描述符表的入口,并将一级描述符表入口地址加4,而指向下一个1M section的基地址。如果页表入口未初始化完,则继续初始化。
 
一级描述符表的高12位是每个setcion的基地址,可以描述4096个section。一级页表大小为16K,每个页表项,即描述符占4字节,刚好可以容纳4096个描述符,所以这里就映射了4096*1M = 4G的空间。
 
/*
 * If ever we are running from Flash, then we surely want the cache
 * to be enabled also for our execution instance...  We map 2MB of it
 * so there is no map overlap problem for up to 1 MB compressed kernel.
 * If the execution is in RAM then we would only be duplicating the above.
 */
              mov r1, #0x1e
              orr   r1, r1, #3 << 10 //这两行将描述的bit[11:10] bit[4:1]置位,(参考arm920t
// datasheet 3.3.3, table 3-2 level one descryiptor bits)
              mov r2, pc, lsr #20
              orr   r1, r1, r2, lsl #20  //将当前地址进1M对齐,并与r1中的内容结合形成一个
                            //描述当前指令所在section的描述符。
 
              add  r0, r3, r2, lsl #2   //r3为刚才建立的一级描述符表的起始地址。通过将当前地
//址(pc)的高12位左移两位(形成14位索引)与r3中的地址
                            // (低14位为0)相加形成一个4字节对齐的地址,这个
                            //地址也在16K的一级描述符表内。当前地址对应的
                            //描述符在一级页表中的位置
                          
              str   r1, [r0], #4
              add  r1, r1, #1048576
              str   r1, [r0]          //这里将上面形成的描述符及其连续的下一个section描述
//写入上面4字节对齐地址处(一级页表中索引为r2左移
//2位)
 
              mov pc, lr       //返回,调用此函数时,调用指令的下一语句mov   r0, #0的地
                       //址保存在lr中
 
这里进行的是1:1的映射,物理地址和虚拟地址是一样。
 
 
 
__common_mmu_cache_on:执行完后返回到bl cache_on下一条指令处226行,
 
              mov r1, sp                    @ malloc space above stack
              add  r2, sp, #0x10000    @ 64k max
 
/*
 * Check to see if we will overwrite ourselves.
 *   r4 = final kernel address
 *   r5 = start of this image
 *   r2 = end of malloc space (and therefore this image)
 * We basically want:
 *   r4 >= r2 -> OK
 *   r4 + image length <= r5 -> OK
 */
              cmp r4, r2
              bhs  wont_overwrite
              sub  r3, sp, r5        @ > compressed kernel size
              add  r0, r4, r3, lsl #2      @ allow for 4x expansion
              cmp r0, r5
              bls   wont_overwrite
 
这 段代码首先在堆栈上确定了64K的malloc空间,空间的起始地址和结束地址分别存放在r1、r2中。然后判断最终内核地址,也就是解压后内核的起始地 址,是否大于malloc空间的结束地址,如果大于就跳到wont_overwrite执行,wont_overwrite函数后面会讲到。否则,检查最 终内核地址加解压后内核大小,也就是解压后内核的结束地址,是否小于现在未解压内核映像的起始地址。小于也会跳到wont_owerwrite执行。如两 这两个条件都不满足,则继续往下执行。
 
 
              mov r5, r2                    @ decompress after malloc space
              mov r0, r5
              mov r3, r7
              bl     decompress_kernel
 
这 里将解压后内核的起始地址设为malloc空间的结束地址。然后后把处理器id(开始时保存在r7中)保存到r3中,调用 decompress_kernel开始解压内核。这个函数的四个参数分别存放在r0-r3中,它在arch/arm/boot/compressed /misc.c中定义。
 
              add  r0, r0, #127
              bic   r0, r0, #127           @ align the kernel length
/*
 * r0     = decompressed kernel length
 * r1-r3  = unused
 * r4     = kernel execution address
 * r5     = decompressed kernel start
 * r6     = processor ID
 * r7     = architecture ID
 * r8     = atags pointer
 * r9-r14 = corrupted
 */
              add  r1, r5, r0        @ end of decompressed kernel
              adr   r2, reloc_start
              ldr   r3, LC1
              add  r3, r2, r3
1:            ldmia       r2!, {r9 - r14}              @ copy relocation code
              stmia       r1!, {r9 - r14}
              ldmia       r2!, {r9 - r14}
              stmia       r1!, {r9 - r14}
              cmp r2, r3
              blo   1b
这里首先计算出解压后内核的大小,然后对它的进行重定位
 
              bl     cache_clean_flush
              add  pc, r5, r0        @ call relocation code
重定位结束后跳到解压后内核的起始处开始执行,在运行解压后内核之前,先调用了
cache_clean_flush这个函数。这个函数的定义在第700行
 
cache_clean_flush:
              mov r3, #16
              b     call_cache_fn
其实这里又调用了call_cache_fn这个函数,注意,这里r3的值为16,call_cache_fn这个函数在前面有讲解,下面看看当r3为16时会调用到哪个函数,回到proc_types这个对像的定义,最终找到处理器相关的处理代码在603行开始
 
              .word      0x00020000           @ ARMv4T
              .word      0x000f0000
              b     __armv4_mmu_cache_on
              b     __armv4_mmu_cache_off
              b     __armv4_mmu_cache_flush
当偏移量为16时,会跳到b       __armv4_mmu_cache_flush这条指令,调用__armv4_mmu_cache_flush这个函数,它的定义在730行
 
__armv4_mmu_cache_flush:
              mov r2, #64*1024         @ default: 32K dcache size (*2)
              mov r11, #32         @ default: 32 byte line size
              mrc p15, 0, r3, c0, c0, 1      @ read cache type
              teq   r3, r6                    @ cache ID register present?
              beq  no_cache_id
              mov r1, r3, lsr #18
              and  r1, r1, #7  //获得Dsize中的size
              mov r2, #1024
              mov r2, r2, lsl r1           @ base dcache size *2//获得dcache字节大小
              tst    r3, #1 << 14          @ test M bit
              addne      r2, r2, r2, lsr #1     @ +1/2 size if M == 1
              mov r3, r3, lsr #12
              and  r3, r3, #3  //上两句获得Dsize中 cache line的长度len
              mov r11, #8
              mov r11, r11, lsl r3 @ cache line size in bytes //cache line的字节长度
no_cache_id:
              bic   r1, pc, #63            @ align to longest cache line
              add  r2, r1, r2
1:            ldr   r3, [r1], r11           @ s/w flush D cache 这个是指什么呢??
              teq   r1, r2
              bne  1b
上面这几句做了什么呢?为什么要这么做呢?
 
              mcr p15, 0, r1, c7, c5, 0      @ flush I cache
              mcr p15, 0, r1, c7, c6, 0      @ flush D cache
              mcr p15, 0, r1, c7, c10, 4     @ drain WB
              mov pc, lr
 
这里主要还是刷新I/Dcache和写缓冲。
 
下面看看前面提到的wont_overwrite函数。这个函数在282行定义
wont_overwrite:     mov r0, r4
              mov r3, r7
              bl     decompress_kernel
              b     call_kernel
 
同样,这里先设置好decompress_kernel的参数,然后调用decompress_kernel解压内核映像。然后调用call_kernel函数。此函数在491行定义
 
call_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
              mov pc, r4                    @ call kernel
 
这里也是先调用cache_clean_flush刷新i/d-cache,然后调用cashe_off函数。最后设置好参数,跳到解压后的内核执行。
 
cashe_off函数在644行定义
cache_off:      mov r3, #12                  @ cache_off function
              b     call_cache_fn
同样又是调用call_cache_fn函数,注意,这里r3的值是12,也就是偏移量是12,最终通过call_cache_fn找到603行的一段代码
 
              .word      0x00020000           @ ARMv4T
              .word      0x000f0000
              b     __armv4_mmu_cache_on
              b     __armv4_mmu_cache_off
              b     __armv4_mmu_cache_flush
 
因这里的偏移量是12,所以将执行b       __armv4_mmu_cache_off指令,调用__armv4_mmu_cache_off函数,这个函数在665行定义。
 
__armv4_mmu_cache_off:
              mrc p15, 0, r0, c1, c0
              bic   r0, r0, #0x000d
              mcr p15, 0, r0, c1, c0   @ turn MMU and cache off
              mov r0, #0
              mcr p15, 0, r0, c7, c7   @ invalidate whole cache v4
              mcr p15, 0, r0, c8, c7   @ invalidate whole TLB v4
              mov pc, lr
这里首先读控制寄存器,然后关闭icache和mmu,接着使全部cache和tlb无效。
 
现在总结一下在进入解压后的内核入口前都做了些什么(解压后的kernel入口在arch/arm/kernel/head.S中):
 
首 先保存从uboot中传入的参数,然后会执行一段处理器相关的代码位于arch/arm/boot/compressed/head-xxx.S中,这段 代码我们这里没有分析,在移植内核时会对这段代码作出分析。接着会判断一下要不要重定位,我们这里是不需要重定位,所以开始对bss段清零。之后初始化页 表,进行1:1映射。因为打开cache前必须打开mmu,所以这里先对页表进行初始化,然后打开mmu和cache。这些都准备好后,判断一下解压内核 是否会覆盖未解压的内核映像。如果会,则进行一些调整,然后开始解压内核;如果不会,则直接解压。最后是刷新cache,关闭mmu和dcache,使 cache和tlb内容无效,跳到解压后的内核入口执行arm相关的内核代码。

海思SD3403/SS928开发(二)环境搭建:从零到一构建高效开发环境的实战指南 本文详细介绍了海思SD3403/SS928开发板环境搭建的全流程,包括Ubuntu系统选择、基础配置、开发环境依赖安装、SDK工具链配置等关键步骤。通过实战经验分享,帮助开发者避免常见问题,快速构建高效开发环境,提升开发效率。 阅读详情

相关推荐

DLPFC.h5ad格式数据简便获取方式+STAGATE论文复现问题

简便获取DLPFC.h5ad格式的数据方式

apple_74057836的博客 2257

arm-linux head.S 源代码分析

<br />arm-linux head.S 源代码分析<br />这是ARM-Linux运行的第一个文件,这些代码是一个比较独立的代码包裹器。其作用就是解压Linux内核,并将PC指针跳到内核(vmlinux)的第一条指令。<br />Bootloader中传入到Linux中的参数总共有三个,Linux中用到的是第二个和第三个。第二个参数是architecture id,第三个是taglist的地址。Architecture id的arm芯片在Linux中一定要唯一。Taglist是bootloa

Embeded system Development 2万+

数据分析项目:直播电商用户流失分析

其中,'Tenure'(在网时长)可能是影响用户流失的关键因素,意味着用户在平台上的停留时间越长,流失的可能性可能越低,这提示平台应注重提高用户粘性,增加用户在平台上的活跃度和停留时间。)的分析,挖掘用户流失的原因和模式,分析发现用户投诉处理效率(Complain)、距上次消费时长(DaySinceLastOrder)、会员生命周期(Tenure)等8项核心指标对流失预测贡献度达87%,模型准确率94.58%,并提出相应的建议。重视用户的满意度,及时处理用户的投诉和反馈,不断改进平台的服务和功能。

Nervousr的博客 2459

compress/head.S解析

清除BBS xorl %eax,%eax //eax=0 movl $_edata,%edi //edi=0x262a2d movl $_end,%ecx //ecx=26eea4 subl %edi,%ecx cld rep stosb 解压缩内核,就是从压缩的vmlinux,解压出vmlinux.bin /* * Do the decompression, and jump...

chengbeng1745的博客 475

CS5212芯片实战:手把手教你设计DP转VGA转换电路(附完整原理图)

本文详细介绍了CS5212芯片在DP转VGA转换电路设计中的实战应用,包括芯片架构解析、核心电路设计要点、PCB布局与电磁兼容设计、调试技巧及进阶应用开发。通过完整的原理图和实用设计指南,帮助工程师快速掌握高可靠转换器的设计方法,特别适合需要国产化替代方案的场景。

weixin_30576827的博客 390

ADS实战指南:负载牵引(Loadpull)在射频功放设计中的关键应用

本文深入解析了负载牵引(Loadpull)技术在射频功率放大器设计中的核心作用与实战方法。通过生动的比喻和详细的ADS软件操作指南,阐述了如何通过系统扫描负载阻抗,在史密斯圆图上绘制等功率线与等效率线,从而精准找到最优阻抗点,以平衡输出功率与效率,是应对5G等现代通信系统严苛要求的必备技能。

weixin_29198045的博客 601

代码执行流程

arch/arm/boot/compressed/head.s ----->调用arch/arm/boot/compressed/misc.c的decompress_kernel()函数解压内核----->arch/arm/kernel/head.S---->arch/arm/kernel/head-common.S初始化 ---->init/main.c的asmlinkage void __in

548

修复后找不到ip 黑群晖_黑群晖引导U盘损坏了怎么办?

前言虽然一般来说引导U盘损坏的可能性很小,但是避免不了万一出现问题导致黑群晖没有办法引导进入系统也是一件相当麻烦的事情。这里我就用两块U盘做个小试验,看看如果黑群晖上的引导U盘损坏以后,另一块U盘需要怎样写引导文件才能让黑群晖正常引导进入系统。试验开始我这里黑群晖使用的是DSM6.1.7的系统,U盘一块使用的是闪迪的酷豆,另一块就是闪迪的酷铄。目前系统引导U盘使用的是酷豆,VID:0781 PID...

weixin_36239323的博客 8269

Linux手机DIY.内核初探.系统后台启动简单介绍

Linux手机DIY.内核初探.系统后台启动简单介绍草木瓜  于 2006-11-25一、序  Linux系统是如何启动,这对将来应用开发是十分重要的,本文整理自Linux论坛,结合Moto E680,夏新E600和飞利浦968进行简单介绍二、重要提示    为了方便更好的理解本文,提供下面链结。    全系列的文章地址,手机应用开发专栏:http://blog.csdn.ne

草木瓜 9278

Python 爬虫实战:雪球用户数据爬取与投资行为分析(含 API 签名算法解析)

雪球作为国内领先的投资者社区,拥有丰富的用户数据和投资信息。通过爬取雪球用户数据并进行分析,我们可以深入了解用户的持仓偏好、交易行为和投资策略,为企业和投资者提供有价值的市场洞察。

yansideyucsdn的博客 2245

【100%通过率】华为od 机试真题 C语言 实现【补种未成活胡杨】

近些年来,我国防沙治沙取得显著成果。某沙漠新种植N棵胡杨(编号1-N),排成一排。一个月后,有M棵胡杨未能成活。现可补种胡杨K棵,请问如何补种(只能补种,不能新种),可以得到最多的连续胡杨树?

MISAYAONE的博客 1653

ARM+Linux 的启动分析

基于ARM 的Linux 的启动分析报告 摘要:本文主要分析基于ARM 的Linux-2.2.26 内核启动过程。将首先从/ arch/arm/Makefile着手,介绍三种不同的启动方案,再剖析典型的压缩内核 zImage启动方案的代码结构,最后将详细分析这种方案的启动过程,直到调用 start_kernel()为止。 1、Linux 内核的启动方案: 由/arch/arm/Make

thinkpadlove的专栏 2778

YOLOv12改进策略【Neck】| arXiv 2024 MAFPN:多分支辅助特征金字塔网络 | 通过双向辅助融合与异构卷积机制,解决传统PAFPN多尺度特征融合不充分与小目标检测不足问题

本文记录的是利用MAFPN颈部结构改进YOLOv12的目标检测网络模型。通过引入双向辅助融合机制优化特征处理流程,在自底向上路径利用 SAF 模块融合主干浅层特征与颈部输出,并保留浅层空间信息,为小目标检测提供更丰富细节;自顶向下路径通过 AAF 模块建立密集连接,聚合浅层高 / 低分辨率层、同级层及前层特征,均衡各层通道数后实现多向梯度信息交互,增强中 / 大目标特征表达。将其应用到中,解决传统特征金字塔在多尺度信息整合中的不足,提升多尺度目标检测的精度与特征表达的丰富性。Multi-Branch Aux

Limiiiing的博客 1254

arm-linux源码分析之解压内核映像

linux-2.6.20.6/arch/arm/boot/compressed/head.S   开头有一段宏定义,我们只看其中一段,分析一下gnu arm汇编的宏定义   #elif defined(CONFIG_ARCH_S3C2410)               .macro loadsp, rb               mov /rb, #0x50000000            

2622

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Linux内核源码分析--内核启动之(1)zImage自解压过程(Linux-3.0 ARMv7) 【转】

转自:http://blog.chinaunix.net/uid-25909619-id-4938388.html   研究内核源码和内核运行原理的时候,很总要的一点是要了解内核的初始情况,也就是要了解内核启动过程。我在研究内核的内存管理的时候,想知道内核启动后的页表的放置,页表的初始化等信息,这促使我这次仔细地研究内核的启动代码。       CPU在bootloader的帮助下将内核载入...

weixin_33915554的博客 125

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

嵌入式 arm平台kernel启动第一阶段汇编head.s分析

转载地址:https://blog.csdn.net/skdkjzz/article/details/24498495 arm_linux内核生成过程: 1. 依据arch/arm/kernel/vmlinux.lds 生成linux内核源码根目录下的vmlinux,这个vmlinux属于未压缩,带调试信息、符号表的最初的内核,大小约23MB;  命令:arm-linux-gnu-ld -o ...

kunkliu的博客 376

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

linux 2.6源代码情景分析笔记之系统启动2

linux/arch/i386/boot/compressed/head.S在setup()结束后,此函数就被移动到物理地址0x00100000处或者0x00001000处,这取决于内核映像是被高装载到ram中还是低装载到ram中。解读函数:startup_32:        cld        cli        movl $(__BOOT_DS),%eax

superkiss2的专栏 696

ARM linux的启动部分源代码简略分析

ARM linux的启动部分源代码简略分析  以友善之臂的mini2440开发板为平台,以较新的内核linux-2.6.32.7版本为例,仅作说明之用。   当内核映像被加载到RAM之后,Bootloader的控制权被释放。内核映像并不是可直接运行的目标代码,而是一个压缩过的zImage(小内核)。但是,也并非是zImage映像中的一切均被压缩了,映像中包含未被压缩的部分,这部分中包含解压缩

嵌入精灵 1505

?【内核修炼】Linux启动过程流程简析

Linux启动流程分析 ,这里仅简要分析Linux的大致启动流程,更多细节请阅读源码分析。   Linux内核启动过程分为解压和启动vmlinux。启动过程从内核映像入口开始执行,解压 vmlinux (内核自引导)并且转换到虚拟地址空间,再调用统一的内核启动函数start_

Android-Linux系统开发 1342

linux0.01源代码分析笔记

linux0.01(原始版)源代码分析笔记 1. 整体结构: 第一个文件夹boot ,包含boot.s 和head.s 。boot.s 实现计算机加电自检引导扇区,第一次加载扇区和第二次加载操作系统的功能,head.s 主要包括初始设置的代码、时钟中断int 0x08的过程代码、系统调用中断int 0x80的过程代码以及任务A 和任务B 等的代码和数据。 (其中.S为扩展名的文件为汇编文件...

JD san的博客 8150

linux内核启动地址的确定

 内核编译链接过程是依靠vmlinux.lds文件,以arm为例vmlinux.lds文件位于kernel/arch/arm/vmlinux.lds,vmlinux-armv.lds的生成过程在kernel/arch/arm/Makefile中ifeq ($(CONFIG_CPU_32),y)PROCESSOR = armvTEXTADDR = 0xC0008000LD

Xianfeng Design 1377

Linux 内核 C 语言深度解析

课程简介 在看一些 GNU 开源软件,或者阅读 Linux 内核、驱动源码时,相信大家和我一样,经常会遇到一些“看似熟悉”,但一仔细分析又不是很懂的 C 语言“稀奇古怪”的语法。其实这些都是 GNU C 对 C 语言语法的扩展。一般的教材中基本不会讲到这些,所以看不懂这些特殊的 C 语法应用也很正常。 此达人课将带领大家,一起学习 Linux 内核或 GNU 开源软件中对 C 语言各种扩展语法的使...

GitChat 4615

基于ARM处理器的反汇编器软件简单设计及实现

写在前面 2012年写的毕业设计,仅供参考 反汇编的目的 缺乏某些必要的说明资料的情况下, 想获得某些软件系统的源代码、设计思想及理念, 以便复制, 改造、移植和发展; 从源码上对软件的可靠性和安全性进行验证,对那些直接与CPU 相关的目标代码进行安全性分析; 涉及的主要内容 分析ARM处理器指令的特点,以及编译以后可执行的二进制文件代码的特征; 将二进制机器代码经过指令和数...

weixin_34413357的博客 341

任务0的内核堆栈,用户堆栈

     描述任务0的内核堆栈和用户堆栈是如何产生的:     linux0.11系统共使用了4种堆栈:系统初始化时临时使用的堆栈;供内核程序自己使用的堆栈(内核堆栈),只有一个,位于系统     地址空间固定的位置,也就是后来任务0的用户态堆栈;每个任务通过系统调用,执行内核程序时使用的堆栈,也即任务的内核态     堆栈,每个任务都有自己独立的内核态堆栈;任务在用户态执行的堆栈,位于任务(进程)地址空间的末端,即任务的用户态堆栈。     从head.s程序起,系统正式在保护模式下运行,此时堆栈段被设置

roma823的专栏 2131
上一篇: 牛蹄印章 阿牛是个好青年/内核移植,android
下一篇: uboot到linux启动内核需要注意的经验之谈
#Page#
博客等级 码龄23年 1691粉丝 360原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值