ARM linux解析之压缩内核zImage的启动过程 一

首先,我们要知道在zImage的生成过程中,是把arch/arm/boot/compressed/head.s 和解压代码misc.cdecompress.c加在压缩内核的最前面最终生成zImage的,那么它的启动过程就是从这个head.s开始的,并且如果代码从RAM运行的话,是与位置无关的,可以加载到内存的任何地方。

下面以arch/arm/boot/compressed/head.s为主线进行启动过程解析。

1.   head.sdebug宏定义部分

最开始的一段都是head.sdebug宏定义部分,这部分可以方便我们调试时使用。

如下:

#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

#elif defined(CONFIG_CPU_XSCALE)

           .macro loadsp, rb, tmp

           .endm

           .macro writeb, ch, rb

           mcr      p14, 0, \ch, c8, c0, 0

           .endm

#else

           .macro loadsp, rb, tmp

           .endm

           .macro writeb, ch, rb

           mcr      p14, 0, \ch, c1, c0, 0

           .endm

#endif

#else

#include <mach/debug-macro.S>

 

           .macro writeb, ch, rb

           senduart \ch, \rb

           .endm

#if defined(CONFIG_ARCH_SA1100)

           .macro loadsp, rb, tmp

           mov     \rb, #0x80000000  @ physical base address

#ifdef CONFIG_DEBUG_LL_SER3

           add \rb, \rb, #0x00050000 @ Ser3

#else

           add \rb, \rb, #0x00010000 @ Ser1

#endif

           .endm

#elif defined(CONFIG_ARCH_S3C2410)

           .macro loadsp, rb, tmp

           mov     \rb, #0x50000000

           add \rb, \rb, #0x4000 * CONFIG_S3C_LOWLEVEL_UART_PORT

           .endm

#else

           .macro loadsp, rb, tmp

           addruart \rb, \tmp

           .endm

#endif

#endif

#endif

如果开启DEBUGging宏的话,这部分代码分两段CONFIG_DEBUG_ICEDCC是用ARMv6以上的加构支持的ICEDCC技术进行调试,DCCDebug Communications Channel)是ARM的一个调试通信通道,在串口无法使用的时候可以使用这个通道进行数据的通信,具体的技术参前ARM公司文档《ARM Architecture Reference Manual》。

第二部分首先#include <mach/debug-macro.S>,这个文件定义位于arch/arm/mach-xxxx/include/mach/debug-macro.S里面,所以这个是和平台相关的,里面定义了每个平台的相关的串口操作,因这个时候系统还没有起来,所以它所用的串口配置参数是依赖于前一级bootloader所设置好的,如我们使用的u-boot设置好所有的参数。如我们的EVBARM的实现如下:

#include <mach/hardware.h>

#include <mach/platform.h>

 

      .macro addruart, rp, rv

      ldr \rp, =ARM_EVB_UART0_BASE                       @ System peripherals (phys address)

      ldr \rv, =(IO_BASE+ ARM_EVB _UART0_BASE)           @ System peripherals (virt address)

      .endm

 

      .macro senduart,rd,rx

      strb      \rd, [\rx, #(0x00)]  @ Write to Transmitter Holding Register

      .endm

 

      .macro waituart,rd,rx

1001:   ldr \rd, [\rx, #(0x18)]                         @ Read Status Register

      tst \rd, #0x20                                         @when TX FIFO  Full, then wait

      bne 1001b

      .endm

 

      .macro busyuart,rd,rx

1001:   ldr \rd, [\rx, #(0x18)]        @ Read Status Register

      tst \rd, #0x08                         @ when uart is busy then wait

      bne 1001b

      .endm

主要实现 addruartsenduartwaituartbusyuart这四个函数的具体实施。这个是调试函数打印的基础。

下面是调试打印用到的kputckphex

           .macro kputc,val

           mov     r0, \val

           bl   putc

           .endm

 

           .macro kphex,val,len

           mov     r0, \val

           mov     r1, #\len

           bl   phex

           .endm

它所调用的putc phex是在head.s最后的一段定义的,如下

 

#ifdef DEBUG

           .align   2

           .type    phexbuf,#object

phexbuf:   .space  12

           .size     phexbuf, . - phexbuf

上面是分配打印hexbuffer,下面是具体的实现:

@ phex corrupts {r0, r1, r2, r3}

phex:        adr r3, phexbuf

           mov     r2, #0

           strb      r2, [r3, r1]

1:        subs     r1, r1, #1

           movmi r0, r3

           bmi      puts

           and r2, r0, #15

           mov     r0, r0, lsr #4

           cmp     r2, #10

           addge   r2, r2, #7

           add r2, r2, #'0'

           strb      r2, [r3, r1]

           b    1b

 

@ puts corrupts {r0, r1, r2, r3}

puts:         loadsp  r3, r1

1:        ldrb      r2, [r0], #1

           teq r2, #0

           moveq  pc, lr

2:        writeb  r2, r3

           mov     r1, #0x00020000

3:        subs     r1, r1, #1

           bne 3b

           teq r2, #'\n'

           moveq  r2, #'\r'

           beq 2b

           teq r0, #0

           bne 1b

           mov     pc, lr

@ putc corrupts {r0, r1, r2, r3}

putc:

           mov     r2, r0

           mov     r0, #0

           loadsp  r3, r1

           b    2b

 

@ memdump corrupts {r0, r1, r2, r3, r10, r11, r12, lr}

memdump:    mov     r12, r0

           mov     r10, lr

           mov     r11, #0

2:        mov     r0, r11, lsl #2

           add r0, r0, r12

           mov     r1, #8

           bl   phex

           mov     r0, #':'

           bl   putc

1:        mov     r0, #' '

           bl   putc

           ldr  r0, [r12, r11, lsl #2]

           mov     r1, #8

           bl   phex

           and r0, r11, #7

           teq r0, #3

           moveq  r0, #' '

           bleq     putc

           and r0, r11, #7

           add r11, r11, #1

           teq r0, #7

           bne 1b

           mov     r0, #'\n'

           bl   putc

           cmp     r11, #64

           blt  2b

           mov     pc, r10

#endif

嘿嘿,还有memdump 这个函数可以用,不错。

好了,言归正传,再往下看,代码如下:

           .macro debug_reloc_start

#ifdef DEBUG

           kputc   #'\n'

           kphex   r6, 8         

           kputc   #':'

           kphex   r7, 8         

#ifdef CONFIG_CPU_CP15

           kputc   #':'

           mrc      p15, 0, r0, c1, c0

           kphex   r0, 8         

#endif

           kputc   #'\n'

           kphex   r5, 8         

           kputc   #'-'

           kphex   r9, 8         

           kputc   #'>'

           kphex   r4, 8         

           kputc   #'\n'

#endif

           .endm

 

           .macro debug_reloc_end

#ifdef DEBUG

           kphex   r5, 8         

           kputc   #'\n'

           mov     r0, r4

           bl   memdump      

#endif

           .endm

debug_reloc_start

用来打印出一些代码重定位后的信息,关于重定位,后面会说,      debug_reloc_end

用来把解压后的内核的256字节的数据dump出来,查看是否正确。很不幸的是,这个不是必须调用的,调试的时候,这些都是要自己把这些调试函数加上去的。好debug部分到这里就完了。

2.   head.s.start部分,进入或保持在svc模式,并关中断

继续向下分析,下面是定义.start段,这段在链接时被链接到代码的最开头,那么zImage启动时,最先执行的代码也就是下面这段代码start开始的,如下:

           .section ".start", #alloc, #execinstr

 

           .align

           .arm                     @ Always enter in ARM state

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                @ absolute load/run zImage address

           .word   _edata             @ zImage end address

 THUMB(         .thumb            )

1:        mov     r7, r1              @ save architecture ID

           mov     r8, r2              @ save atags pointer

 

#ifndef __ARM_ARCH_2__

          

           mrs      r2, cpsr      @ get current mode

           tst  r2, #3             @ not user?

           bne not_angel

           mov     r0, #0x17        @ angel_SWIreason_EnterSVC

 ARM(        swi 0x123456   )    @ angel_SWI_ARM

 THUMB(         svc 0xab          )    @ angel_SWI_THUMB

not_angel:

           mrs      r2, cpsr      @ turn off interrupts to

           orr r2, r2, #0xc0         @ prevent angel from running

           msr      cpsr_c, r2

#else

           teqp     pc, #0x0c000003         @ turn off interrupts

#endif

为何这个会先执行呢?问的好。那么来个中断吧:这个是由arch/arm/boot/compressed/vmlinux.lds的链接脚本决定的,如下:

  .text : {

    _start = .;

    *(.start)

    *(.text)

    *(.text.*)

    *(.fixup)

    *(.gnu.warning)

    *(.rodata)

    *(.rodata.*)

    *(.glue_7)

    *(.glue_7t)

    *(.piggydata)

    . = ALIGN(4);

  }

怎么样,看到没,.text段最开始的一部分就是.start段,所以这就注定了它就是最先执行的代码。

好了,中断结束,再回到先前面的代码,这段代码的最开始是会被编译器编译成8nop 这个是为了留给ARM的中断向量表的,但是整个head.s都没有用到中断啊,谁知道告诉我一下,谢了。

然后呢,把u-boot 传过来的放在r1,r2的值,存在r7,r8中,r1存是的evb板的ID号,而r2存的是内核要用的参数地址,这两个参数在解压内核的时候不要用到,所以暂时保存一下,解压内枋完了,再传给linux内核。

再然后是几个宏定义的解释,ARM()BSYM()THUMB(),再加上 W()吧,这几个个宏定义都是在arch/arm/include/asm/unified.h里面定义的,好了,这里也算个中断吧,如下:

#ifdef CONFIG_THUMB2_KERNEL

......

#define ARM(x...)

#define THUMB(x...)   x

#ifdef __ASSEMBLY__

#define W(instr)    instr.w

#endif

#define BSYM(sym)     sym + 1

#else  

......

#define ARM(x...)  x

#define THUMB(x...)

#ifdef __ASSEMBLY__

#define W(instr)    instr

#endif

#define BSYM(sym)     sym

#endif

好的看到上面的定义你就会明白了,这里是为了兼容THUMB2指令的内核。

关于#define ARM(x...) 里面的“...”,没有见过吧,这个是C语言的C99的新标准,变参宏,就是在x里,你可以随便你输入多少个参数。别急还没有完,因为没有看见文件里有什么方包含这个头文件。是的文件中确实没有包含,它的定义是在:arch/arm/makefile中加上的:

KBUILD_AFLAGS    += -include asm/unified.h

行,这些宏解释到此,下面再出现,我就无视它了。

好了,再回来,读取cpsr并判断是否处理器处于supervisor模式——从u-boot进入kernel,系统已经处于SVC32模式;而利用angel进入则处于user模式,还需要额外两条指令。之后是再次确认中断关闭,并完成cpsr写入。

注:AngelARM公司的一种调试方法,它本身就是一个调试监控程序,是一组运行在目标机上的程序,可以接收主机上调试器发送的命令,执行诸如设置断点、单步执行目标程序、观察或修改寄存器、存储器内容之类的操作。与基于jtag的调试代理不同,Angel调试监控程序需要占用一定的系统资源,如内存、串行端口等。使用angel调试监控程序可以调试在目标系统运行的arm程序或thumb程序。

好了,里面有一句:teqp  pc, #0x0c000003         @ turn off interrupts

是否很奇怪,不过大家千万不要纠结它,因为它是ARMv2架构以前的汇编方法,用于模式变换,和中断关闭的,看不明白也没关系,因为我们以后也用不到。这里知道一下有这个事就行了。

      行,到这里.start段就完了,代码那么多,其实就是做一件事,保证运行下面的代码时已经进入了SVC模式,并保证中断是关的,完了.start部分结束。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数字乡村和智慧农业的数字化转型是当前农业发展的新趋势,旨在通过应用数字技术,实现农业全流程的再造和全生命周期的管理服务。中国政府高度重视这一领域的发展,提出“数字中国”和“乡村振兴”战略,以提升国家治理能力,推动城乡融合发展。 数字乡村的建设面临乡村治理、基础设施、产业链条和公共服务等方面的问题,需要分阶段实施《数字乡村发展战略纲要》来解决。农业数字化转型的需求包括满足市民对优质农产品的需求、解决产销对接问题、形成优质优价机制、提高农业劳动力素质、打破信息孤岛、提高农业政策服务的精准度和有效性,以及解决农业融资难的问题。 数字乡村建设的关键在于构建“1+3+4+1”工程,即以新技术、新要素、新商业、新农民、新文化、新农村为核心,推进数据融合,强化农业大数据的汇集功能。数字农业大数据解决方案以农业数字底图和数据资源为基础,通过可视化监管,实现区域农业的全面数字化管理。 数字农业大数据架构基于大数据、区块链、GIS和物联网技术,构建农业大数据中心、农业物联网平台和农村综合服务指挥决策平台三大基础平台。农业大数据中心汇聚各类涉农信息资源和业务数据,支持大数据应用。信息采集系统覆盖市、县、乡、村多级,形成高效的农业大数据信息采集体系。 农业物联网平台包括环境监测系统、视频监控系统、预警预报系统和智能控制系统,通过收集和监测数据,实现对农业环境和生产过程的智能化管理。综合服务指挥决策平台利用数据分析和GIS技术,为农业决策提供支持。 数字乡村建设包括三大服务平台:治理服务平台、民生服务平台和产业服务平台。治理服务平台通过大数据和AI技术,实现乡村治理的数字化;民生服务平台利用互联网技术,提供各类民生服务;产业服务平台融合政企关系,支持农业产业发展。 数字乡村的应用场景广泛,包括农业生产过程、农产品流通、农业管理和农村社会服务。农业生产管理系统利用AIoT技术,实现农业生产的标准化和智能化。农产品智慧流通管理系统和溯源管理系统提高流通效率和产品追溯能力。智慧农业管理通过互联网+农业,提升农业管理的科学性和效率。农村社会服务则通过数字化手段,提高农村地区的公共服务水平。 总体而言,数字乡村和智慧农业的建设,不仅能够提升农业生产效率和管理水平,还能够促进农村地区的社会经济发展,实现城乡融合发展,是推动中国农业现代化的重要途径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值