linux内核启动地址 解压缩 启动参数

ARM 106浏览

  一般情况下都在生成vmlinux后,再对内核进行压缩成为zImage,压缩的目录是kernel/arch/arm/boot。

  下载到flash中的是压缩后的zImage文件,zImage是由压缩后的vmlinux和解压缩程式组成,如下图所示:

zImage是由压缩后的vmlinux和解压缩程式组成

  zImage链接脚本也叫做vmlinux.lds,位于kernel/arch/arm/boot/compressed。

  是由同一目录下的vmlinux.lds.in文件生成的

  在kernel/arch/arm/boot/Makefile文件中定义了:

  ifeq ($(CONFIG_ARCH_S3C2410),y)

  ZTEXTADDR     = 0x30008000

  ZRELADDR     = 0x30008000

  endif

  ZTEXTADDR就是解压缩代码的ram偏移地址,ZRELADDR是内核ram启动的偏移地址,这里看到指定ZTEXTADDR的地址为 30008000,

  以上就是我对内核启动地址的分析,总结一下内核启动地址的设置:

  设置kernel/arch/arm/boot/Makefile文件中的

  ifeq ($(CONFIG_ARCH_S3C2410),y)

  ZTEXTADDR     = 0x30008000

  ZRELADDR     = 0x30008000

  endif

  # We now have a PIC decompressor implementation.  Decompressors running

  # from RAM should not define ZTEXTADDR.  Decompressors running directly

  # from ROM or Flash must define ZTEXTADDR (preferably via the config)

  #

  查看2410的datasheet ,发现内存映射的基址是0x3000 0000 ,那么  0x30008000又是怎么来的呢?

  在内核文件kernel/Document/arm/Booting 文件中有:

  Calling the kernel image

  Existing boot loaders: MANDATORY

  New boot loaders: MANDATORY

  There are two options for calling the kernel zImage. If the zImage is stored in flash, and is linked correctly to be run from flash, then it is legal for the boot loader to call the zImage in flash directly.

  The zImage may also be placed in system RAM (at any location) and called there. Note that the kernel uses 16K of RAM below the image to store page tables. The recommended placement is 32KiB into RAM.

  看来在image下面用了32K(0x8000)的空间存放内核页表,

  0x30008000就是2410的内核在RAM中的启动地址,这个地址就是这么来的。

  关于内核解压缩的过程分析

  内核压缩和解压缩代码都在目录kernel/arch/arm/boot/compressed,

  编译完成后将产生vmlinux、head.o、misc.o、head-s3c2410.o、piggy.o这几个文件,

  head.o是内核的头部文件,负责初始设置;

  misc.o将主要负责内核的解压工作,他在head.o之后;

  head-s3c2410.o文件主要针对的初始化,将在链接时和head.o合并;

  piggy.o是个中间文件,其实是个压缩的内核(kernel/vmlinux),只不过没有和初始化文件及解压文件链接而已;

  vmlinux是没有(zImage是压缩过的内核)压缩过的内核,就是由piggy.o、head.o、misc.o、head-s3c2410.o组成的。

  在BootLoader完成系统的引导以后并将Linux内核调入内存之后,调用bootLinux(),

  这个函数将跳转到kernel的起始位置。

  如果kernel没有压缩,就能启动了。

  如果kernel压缩过,则要进行解压,在压缩过的kernel头部有解压程式。

  压缩过得kernel入口第一个文件源码位置在arch/arm/boot/compressed/head.S。

  他将调用函数decompress_kernel(),这个函数在文件arch/arm/boot/compressed/misc.c中,

  decompress_kernel()又调用proc_decomp_setup(),arch_decomp_setup()进行设置,

  然后使用在打印出信息“Uncompressing Linux...”后,调用gunzip()。将内核放于指定的位置。

  以下分析head.S文件:

  (1)对于各种arm CPU的DEBUG输出设定,通过定义宏来统一操作。

  (2)设置kernel开始和结束地址,保存architecture ID。

  (3)如果在arm2以上的CPU中,用的是普通用户模式,则升到终极用户模式,然后关中断。

  (4)分析LC0结构delta offset,判断是否需要重载内核地址(r0存入偏移量,判断r0是否为零)。

  这里是否需要重载内核地址,我以为主要分析arch/arm/boot/Makefile、arch/arm/boot/compressed /Makefile

  和arch/arm/boot/compressed/vmlinux.lds.in三个文件,主要看vmlinux.lds.in链接文件的主要段的位置,

  LOAD_ADDR(_load_addr)=0x30008000,而对于TEXT_START(_text、_start)的位置只设为 0,BSS_START(__bss_start)=ALIGN(4)。

  对于这样的结果依赖于,对内核解压的运行方式,也就是说,内核解压前是在内存(RAM)中还是在FLASH上,

  因为这里,我们的BOOTLOADER将压缩内核(zImage)移到了RAM的0x30008000位置,我们的压缩内核是在内存(RAM)从 0x30008000地址开始顺序排列,

  因此我们的r0获得的偏移量是载入地址(0x30008000)。

  接下来的工作是要把内核映像的相对地址转化为内存的物理地址,即重载内核地址。

  (5)需要重载内核地址,将r0的偏移量加到BSS region和GOT table中。

  (6)清空bss堆栈空间r2-r3。

  (7)建立C程式运行需要的缓存,并赋于64K的栈空间。

  (8)这时r2是缓存的结束地址,r4是kernel的最后执行地址,r5是kernel境象文件的开始地址。检查是否地址有冲突。

  将r5等于r2,使decompress后的kernel地址就在64K的栈之后。

  (9)调用文件misc.c的函数decompress_kernel(),解压内核于缓存结束的地方(r2地址之后)。此时各寄存器值有如下变化:

  r0为解压后kernel的大小

  r4为kernel执行时的地址

  r5为解压后kernel的起始地址

  r6为CPU类型值(processor ID)

  r7为系统类型值(architecture ID)

  (10)将reloc_start代码拷贝之kernel之后(r5+r0之后),首先清除缓存,而后执行reloc_start。

  (11)reloc_start将r5开始的kernel重载于r4地址处。

  (12)清除cache内容,关闭cache,将r7中architecture ID赋于r1,执行r4开始的kernel代码。