精华内容
下载资源
问答
  • /showinfo/linux-108543.html Arm linux 内核移植及系统初始化过程分析 本文主要介绍内核移植过程中涉及文件分布及其用途以及简单介绍系统初始化过程整个arm linux内核启动可分为三个阶段 第一阶段主要是进行...
  • linux 内核移植

    2019-10-04 10:48:40
    移植的主要工作是增减内核模块,重新编译定制好得内核,使之符合具体要求。 本文简要的说明2.6.35内核在s3c2410平台的移植。主要写编译过程。步骤如下: 1. 下载并解压源码包 2. 进入源码根目录,修改Makefile ...

    在有了合适的交叉编译工具链和移植了bootloader的基础上,就要进行期待的内核移植。

    移植的主要工作是增减内核模块,重新编译定制好得内核,使之符合具体要求。

    本文简要的说明2.6.35内核在s3c2410平台的移植。主要写编译过程。步骤如下:

    1.

    下载并解压源码包

    2.

    进入源码根目录,修改Makefile

    ARCH = arm

    CROSS_COMPILE = arm-linux-

    3.

    添加网卡驱动

    一般嵌入式开发环境都是用网络文件系统,所以需要网卡驱动的支持。

    linux-2.6.35内核中有cs8900的驱动,但是我们需要修改。另外,也可以增加一个可以使用的cs8900网卡到我们的内核。

    这里使用第二种方法

    添加分区信息,添加nand flash驱动

    4.

    配置生成.config文件

    配置内核

    make menuconfig

    这里的需要选择的选项比较多,如Kernel Features, File Systems, Device Drivers等。

    可以通过修改kconfig的内容来增减menuconfig的选项,根据自己板子和文件系统的类型来选配。

    5.

    make zImage

    转载于:https://www.cnblogs.com/liujiahi/archive/2011/10/13/2210351.html

    展开全文
  • 这里记一下移植过程的一些经验。 一、管脚复用 之前我只移植过S4C2440平台的内核,该芯片datasheet不多,一个文档搞定,不像我现在用芯片,有N个文档要看,而且有N个核。像GPIO配置,也比2440复杂得多。在管脚...

    最近一段时间主要是搞内核,当然,也要和其它部门扯皮。
    这还是我第一次在实际工作是移植内核,以前那些,只能算是学习、积累。这里记一下移植过程的一些经验。

    一、管脚复用
    之前我只移植过S4C2440平台的内核,该芯片datasheet不多,一个文档搞定,不像我现在用的芯片,有N个文档要看,而且有N个核。像GPIO的配置,也比2440复杂得多。在管脚复用上,我掉进过2个坑里。
    我们统一在u-boot里做管脚复用,在仔细对着硬件平台的原理图的GPIO后,确认没搞错。不过还是出问题。
    一个是RS485,都知道它是半双工的,在收、发之前要做方向的配置,这个配置就是一个GPIO的电平的配置。不过测试时发现,只能发,不能收,拿示波器来看,发现那个GPIO无论设置为0还是1(已确认能设置0或1),一直是高电平,——感觉上,这个GPIO无法操作。幸亏学习了sysfs,将寄存器的读写操作做成/sys/下的一个文件(前面写有sysfs的驱动内核的文章),使用echo来查看该GPIO管脚复用寄存器,发现值不是我们期望设置的,它改变了,复用成其它的功能了,不管三七二十一,先强制设置成是GPIO功能,结果可以接收数据,能肯定是复用的问题。u-boot没错,就是内核有问题,于是便去查,发现内核在某个地方也做了管脚复用的配置,对着手册看,配置的值果然是我们看的值。继续探索,发现是内核配置选项中开了管脚复用的宏,去掉该配置就可以了。
    另一个坑隐藏得比较深,因为涉及到另外的一个核上的代码。当我测试与linux有关的外设时,一切都正常,比如,led能点亮。但我运行其它核的程序时,led灭了。使用上述方法查找,发现是管脚复寄存器值改变了。这个问题在项目例会上提出,然后时时被领导问进展(也不是那么频繁,比如一天问两次这样吧)。在不到48小时内(周三10时开会,周四晚上19点多解决),终于找到出现问题的函数并解决,这还是一个同事帮忙找到的。花费时间最多的是定位在哪个地方将寄存器值改变了。一来,涉及了三个核的程序运行,二来,代码太庞大,我又不熟悉(熟悉代码的人在做其它紧急的事)。
    开始怀疑的时候,我在整个代码树搜索寄存值的地址,包括基地址。使用管脚复用基地址找不到是因为那个函数的管脚复用基地址是用另一个基地址做偏移算出来的,所以,不能直接找出来。我没有考虑到搜索改变后的寄存器的那个数值,因为给寄存器赋值不会直接把所有的位都写成一个数,这是我的经验,——但那个函数是这样做,它赋的值就是我看到的值。我也没考虑搜索各个寄存器地址的偏移量,这是我的疏忽之处。之所以说那么多,是为自己找借口:不是我没有从这个方向查找,是查找方法不当。
    再说回那个函数的用途,它是EVM板的一种用法,在人家的EVM板上,那些管脚就是这么配置的,没问题,问题是我们参考人家时,把那些管脚做别的用途。从这个角度看,有时候,直接拿人家的东西未必是好的。不过现在的开发,一般不会自己从头设计,比如我之前的ARM开发板,就是那个公司从三星的参考设计上开发的。如果有个强大的技术支持,应该会好很多。在与同事聊天时,聊到某家很牛的公司买某平台芯片提供商很多芯片,芯片提供商直接派人到该公司做支持。

    二、rtc晶振
    某天无意中发现设备上的时间会慢慢变慢。一天有半个小时的误差。于是上网找资料,找到内核的HZ配置,时钟中断,rtc同步等等很多资料,但好像没什么联系。只好求助硬件部门帮查找。首先硬件电路设计与我们参考的那个板子相同,芯片也相同,内核也相同,按理应该是没有问题的。在人家板子上也未发现问题,时间很准。最后硬件部门出动了频率计数器,发现rtc的晶振不是外部的32.768KHz,而是32KHz左右,查看芯片手册,里面说可以配置使用内部的振荡器,也可以配置使用外部的,我们是使用外部的晶振,但为何不生效?继续看手册,发现有一个寄存器的位是做这个配置的:
    Internal 32-kHz clock source control bit (EEPROM bit):
    when 0, the internal 32-kHz clock source is the crystal oscillator or an
    external 32-kHz clock in case the crystal oscillator is used in bypass
    mode
    when 1, the internal 32-kHz clock source is the RC oscillator.

    在u-boot读取这个位的值,结果是1,确实是使用了内部的振荡器,将它改成0,时间果然准确了。既然找到问题,就好办了。每次u-boot启动时都是读这个位的值,如果是1,就将它改成0。但是,有一个疑问,为何人家参考设计的板子没设置这个值,也没问题?
    cu上也有篇类似的文章,我也是从里面找到一些线索的。
    http://blog.chinaunix.net/uid-21501855-id-3320042.html

    注1:用人家的东西,坑太多了。
    注2:公司对于在用品bug处理比较严格,要求xx天给出临时解决方案,yy天给出永久的解决方案。于是在文中也顺便提了一下我解决小bug所花的小时间。

     

    展开全文
  • 本课程为linux kernel移植的第3部分,主要内容是linux内核启动流程...本部分的学习目标是对linux内核的启动和构建过程有个清晰理解,对内核启动后的状态有清晰认识,从而能够帮助我们分析和解决内核移植过程中的问题。
  • 本文主要是对下面文章摘要修改,以此解释为何移植过程中使用工具以及操作含义与作用 文章:http://baike.so.com/doc/353340-374271.html "什么是文件系统",文件系统是对一个存储设备上数据和元数据进行...

    本文主要是对下面文章的摘要修改,以此解释为何移植过程中使用的工具以及操作的含义与作用,主要是黑体部分

    文章:http://baike.so.com/doc/353340-374271.html

    "什么是文件系统",文件系统是对一个存储设备上的数据和元数据进行组织的机制。这种机制有利于用户和操作系统的交互。

          尽管内核是 Linux 的核心,但文件却是用户与操作系统交互所采用的主要工具。这对 Linux 来说尤其如此,这是因为在 UNIX 传统中,它使用文件 I/O 机制管理硬件设备和数据文件————所以既然将设备看成文件,那对设备的操作就需要支持文件操作的工具,这就对应移植时候的Busybox,它集成了各种类似linux中shell命令(执行脚本).

          根文件系统之所以在前面加一个"根",说明它是加载其它文件系统的"根",既然是根的话,那么如果没有这个根,其它的文件系统也就没有办法进行加载的。它包含系统引导和使其他文件系统得以挂载(mount)所必要的文件。根文件系统包括Linux启动时所必须的目录和关键性的文件,例如Linux启动时都需要有init目录下的相关文件,在 Linux挂载分区时Linux一定会找/etc/fstab这个挂载文件等,根文件系统中还包括了许多的应用程序bin目录等,任何包括这些Linux 系统启动所必须的文件都可以成为根文件系统。————我们在内核配置中先设置内核支持yaffs文件系统,而后手动建立文件夹root.2.6.30.4以及内部各种目录及其相应内部文件(此时只是一个文件夹而已),其中就有包括init目录,etc目录及其下的fstab文件等,最后利用制作工具将文件夹(如mkyaffs2image工具等)制作成yaffs格式的文件系统映像.

         那么根文件系统在系统启动中到底是什么时候挂载的呢?先将/dev/ram0挂载,而后执行/linuxrc.等其执行完后。切换根目录,再挂载具体的根文件系统.根文件系统执行完之后,也就是到了Start_kernel()函数的最后,执行init的进程,也就第一个用户进程。对系统进行各种初始化的操作。如果要弄明白这里的过程的话,可要好好的看看Linux内核源码了。

         在 Linux 中将一个文件系统与一个存储设备关联起来的过程称为挂载(mount)。根文件系统首先是一种文件系统,该文件系统不仅具有普通文件系统的存储数据文件的功能,但是相对于普通的文件系统,它的特殊之处在于,它是内核启动时所挂载(mount)的第一个文件系统内核代码的映像文件保存在根文件系统中,系统引导启动程序会在根文件系统挂载之后从中把一些初始化脚本(如rcS,inittab)和服务加载到内存中去运行。我们要明白文件系统和内核是完全独立的两个部分。嵌入式中移植的内核下载到开发板上,是没有办法真正的启动Linux操作系统的,会出现无法加载文件系统的错误。Linux启动时,第一个必须挂载的是根文件系统;若系统不能从指定设备上挂载根文件系统,则系统会出错而退出启动。成功之后可以自动或手动挂载其他的文件系统。因此,一个系统中可以同时存在不同的文件系统。

        在内核源码目录结构中:

    Fs目录:存放Linux支持的文件系统代码和各种类型的文件操作代码。每一个子目录支持一个文件系统,包括我们常用的文件系统类型:Ext2、Ext3、Ext4、 Proc、Fat和Nfs。其中Ext3文件系统对应的就是/fs/ext3目录。

    展开全文
  • 本文主要介绍内核移植过程中涉及文件分布及其用途,以及简单介绍系统初始化过程。整个arm linux内核启动可分为三个阶段:第一阶段主要是进行cpu和体系结构检查、cpu本身初始化以及页表建立等;第二阶段...
     
    

    本文主要介绍内核移植过程中涉及文件的分布及其用途,以及简单介绍系统的初始化过程。整个arm linux内核的启动可分为三个阶段:第一阶段主要是进行cpu和体系结构的检查、cpu本身的初始化以及页表的建立等;第二阶段主要是对系统中的一些基础设施进行初始化;最后则是更高层次的初始化,如根设备和外部设备的初始化。了解系统的初始化过程,有益于更好地移植内核。

     

    1.  内核移植涉及文件分布介绍

    1.1.   内核移植涉及的头文件

    /linux-2.6.18.8/include

    [root@localhost include]# tree -L 1

    .

    |-- Kbuild

    |-- acpi

    |-- asm -> asm-arm

    |-- asm-alpha

    |-- asm-arm   ------------------------------->(1)

    |-- asm-sparc

    |-- asm-sparc64

    |-- config

    |-- keys

    |-- linux          ------------------------------->(2)

    |-- math-emu

    |-- media

    |-- mtd

    |-- net

    |-- pcmcia

    |-- rdma

    |-- rxrpc

    |-- scsi

    |-- sound

    `-- video

     

    内核移植过程中涉及到的头文件包括处理器相关的头文件(1)和处理器无关的头文件(2)。

     

    1.2.   内核移植涉及的源文件

    /linux-2.6.18.8/arch/arm

    [root@localhost arm]# tree -L 1

    .

    |-- Kconfig

    |-- Kconfig-nommu

    |-- Kconfig.debug

    |-- Makefile

    |-- boot  ------------------------------->(2)

    |-- common

    |-- configs

    |-- kernel  ------------------------------->(3)

    |-- lib

    |-- mach-at91rm9200

    ……

    |-- mach-omap1

    |-- mach-omap2

    |-- mach-realview

    |-- mach-rpc

    |-- mach-s3c2410   ------------------------------->(4)

    |-- mach-sa1100

    |-- mach-versatile

    |-- mm    ------------------------------->(5)

    |-- nwfpe

    |-- oprofile

    |-- plat-omap

    |-- tools    ------------------------------->(1)

    `-- vfp

     

    (1)

    /linux-2.6.18.8/arch/arm/tools

    [root@localhost tools]# tree -L 1

    .

    |-- Makefile

    |-- gen-mach-types

    `-- mach-types

     

    Mach-types 文件定义了不同系统平台的系统平台号。移植linux内核到新的平台上需要对新的平台登记系统平台号。

     

    Mach-types文件格式如下:

    # machine_is_xxx   CONFIG_xxxx             MACH_TYPE_xxx        number

    s3c2410             ARCH_S3C2410           S3C2410                        182

    smdk2410          ARCH_SMDK2410 SMDK2410                   193

     

    之所以需要这些信息,是因为脚本文件linux/arch/arm/tools/gen-mach-types需要linux/arch/tools/mach-types来产生linux/include/asm-arm/mach-types.h文件,该文件中设置了一些宏定义,需要这些宏定义来为目标系统选择合适的代码。

     

    (2)

    linux-2.6.18.8/arch/arm/boot/compressed

    [root@localhost compressed]# tree -L 1

    .

    |-- Makefile

    |-- Makefile.debug

    |-- big-endian.S

    |-- head-at91rm9200.S

    |-- head.S

    |-- ll_char_wr.S

    |-- misc.c

    |-- ofw-shark.c

    |-- piggy.S

    `-- vmlinux.lds.in

     

    Head.s 是内核映像的入口代码,是自引导程序。自引导程序包含一些初始化程序,这些程序都是体系结构相关的。在对系统作完初始化设置工作后,调用misc.c文件中的decompress_kernel()函数解压缩内核映像到指定的位置,然后跳转到kernel的入口地址。

     

    Vmlinux.lds.in用来生成内核映像的内存配置文件。

     

    (3)

    linux-2.6.18.8/arch/arm/kernel

    [root@localhost kernel]# tree -L 1

    .

    |-- Makefile

    |-- apm.c

    |-- armksyms.c

    |-- arthur.c

    |-- asm-offsets.c

    |-- bios32.c

    |-- calls.S

    |-- dma.c

    |-- ecard.c

    |-- entry-armv.S

    |-- entry-common.S

    |-- entry-header.S

    |-- fiq.c

    |-- head-common.S

    |-- head-nommu.S

    |-- head.S

    |-- init_task.c

    |-- io.c

    |-- irq.c

    |-- isa.c

    |-- module.c

    |-- process.c

    |-- ptrace.c

    |-- ptrace.h

    |-- semaphore.c

    |-- setup.c

    |-- smp.c

    |-- sys_arm.c

    |-- time.c

    |-- traps.c

    `-- vmlinux.lds.S

     

    内核入口处也是由一段汇编语言实现的,由head.s和head-common.s两个文件组成。

    Head.s 是内核的入口文件, 在head.s的末尾处 #include "head-common.S"。 经过一系列的初始化后,跳转到linux-2.6.18.8/init/main.c中的start_kernel()函数中,开始内核的基本初始化过程。

     

     

    /linux-2.6.18.8/init

    [root@localhost init]# tree

    .

    |-- Kconfig

    |-- Makefile

    |-- calibrate.c

    |-- do_mounts.c

    |-- do_mounts.h

    |-- do_mounts_initrd.c

    |-- do_mounts_md.c

    |-- do_mounts_rd.c

    |-- initramfs.c

    |-- main.c

    `-- version.c

     

    (4)

    /linux-2.6.18.8/arch/arm/mach-s3c2410

    [root@localhost mach-s3c2410]# tree -L 1

    .

    |-- Kconfig

    |-- Makefile

    |-- Makefile.boot

    |-- bast-irq.c

    |-- bast.h

    |-- clock.c

    |-- clock.h

    |-- common-smdk.c

    |-- common-smdk.h

    |-- cpu.c

    |-- cpu.h

    |-- devs.c

    |-- devs.h

    |-- dma.c

    |-- gpio.c

    |-- irq.c

    |-- irq.h

    |-- mach-anubis.c

    |-- mach-smdk2410.c

    |-- pm-simtec.c

    |-- pm.c

    |-- pm.h

    |-- s3c2400-gpio.c

    |-- s3c2400.h

    |-- s3c2410-clock.c

    |-- s3c2410-gpio.c

    |-- s3c2410.c

    |-- s3c2410.h

    |-- sleep.S

    |-- time.c

    |-- usb-simtec.c

    `-- usb-simtec.h

     

    这个目录中的文件都是板级相关的,其中比较重要是如下几个:

    linux/arch/arm/mach-s3c2410/cpu.c

    linux/arch/arm/mach-s3c2410/common-smdk.c

    linux/arch/arm/mach-s3c2410/devs.c

    linux/arch/arm/mach-s3c2410/mach-smdk2410.c

    linux/arch/arm/mach-s3c2410/Makefile.boot

    linux/arch/arm/mach-s3c2410/s3c2410.c

     

    2.  处理器和设备

    这里主要介绍处理器和设备的描述和操作过程。设备描述在linux/arch/arm/mach-s3c2410/devs.c和linux/arch/arm/mach-s3c2410/common-smdk.c中实现。最后以nand flash为例具体介绍。

    2.1.   处理器、设备描述

    设备描述主要两个结构体完成:struct resource和struct platform_device。

    先来看看着两个结构体的定义:

    struct resource {

           resource_size_t start;

           resource_size_t end;

           const char *name;

           unsigned long flags;

           struct resource *parent, *sibling, *child;

    };

     

    Resource结构体主要是描述了设备在系统中的起止地址、名称、标志以及为了链式描述方便指向本结构体类型的指针。Resource定义的实例将被添加到platform_device结构体对象中去。

     

    struct platform_device {

           const char      * name;

           u32         id;

           struct device   dev;

           u32         num_resources;

           struct resource       * resource;

    };

     

    Platform_device结构体包括结构体的名称、ID号、平台相关的信息、设备的数目以及上面定义的resource信息。Platform_device结构对象将被直接通过设备操作函数注册导系统中去。具体注册和注销过程在下一节介绍。

     

    2.2.   处理器、设备操作

    (1) int platform_device_register(struct platform_device * pdev);    注册设备

    (2) void platform_device_unregister(struct platform_device * pdev); 注销设备

    (3) int platform_add_devices(struct platform_device **devs, int num);添加设备,通过调用上面两个函数实现。

    2.3.   添加Nand flash设备

    下面以nand flash 设备的描述为例,具体介绍下设备的描述和注册过程。

     

    // resource结构体实例s3c_nand_resource 对nand flash 控制器描述,包括控制器的起止地址和标志。

    static struct resource s3c_nand_resource[] = {

           [0] = {

                  .start = S3C2410_PA_NAND,

                  .end   = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,

                  .flags = IORESOURCE_MEM,

           }

    };

     

    //platform_device结构体实例s3c_device_nand定义了设备的名称、ID号并把resource对象作为其成员之一。

    struct platform_device s3c_device_nand = {

           .name               = "s3c2410-nand",

           .id             = -1,

           .num_resources       = ARRAY_SIZE(s3c_nand_resource),

           .resource   = s3c_nand_resource,

    };

     

    // nand flash 的分区情况,由mtd_partition结构体定义。

    static struct mtd_partition smdk_default_nand_part[] = {

           [0] = {

                  .name      = "Boot Agent",

                  .size = SZ_16K,

                  .offset     = 0,

           },

           [1] = {

                  .name      = "S3C2410 flash partition 1",

                  .offset = 0,

                  .size = SZ_2M,

           },

           [2] = {

                  .name      = "S3C2410 flash partition 2",

                  .offset = SZ_4M,

                  .size = SZ_4M,

           },

           [3] = {

                  .name      = "S3C2410 flash partition 3",

                  .offset     = SZ_8M,

                  .size = SZ_2M,

           },

           [4] = {

                  .name      = "S3C2410 flash partition 4",

                  .offset = SZ_1M * 10,

                  .size = SZ_4M,

           },

           [5] = {

                  .name      = "S3C2410 flash partition 5",

                  .offset     = SZ_1M * 14,

                  .size = SZ_1M * 10,

           },

           [6] = {

                  .name      = "S3C2410 flash partition 6",

                  .offset     = SZ_1M * 24,

                  .size = SZ_1M * 24,

           },

           [7] = {

                  .name      = "S3C2410 flash partition 7",

                  .offset = SZ_1M * 48,

                  .size = SZ_16M,

           }

    };

     

    static struct s3c2410_nand_set smdk_nand_sets[] = {

           [0] = {

                  .name             = "NAND",

                  .nr_chips = 1,

                  .nr_partitions  = ARRAY_SIZE(smdk_default_nand_part),

                  .partitions       = smdk_default_nand_part,

           },

    };

     

    /* choose a set of timings which should suit most 512Mbit

     * chips and beyond.

    */

     

    static struct s3c2410_platform_nand smdk_nand_info = {

           .tacls              = 20,

           .twrph0          = 60,

           .twrph1          = 20,

           .nr_sets   = ARRAY_SIZE(smdk_nand_sets),

           .sets        = smdk_nand_sets,

    };

     

    /* devices we initialise */

    // 最后将nand flash 设备加入到系统即将注册的设备集合中。

    static struct platform_device __initdata *smdk_devs[] = {

           &s3c_device_nand,

           &smdk_led4,

           &smdk_led5,

           &smdk_led6,

           &smdk_led7,

    };

     

    然后通过smdk_machine_init()函数,调用设备添加函数platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs)) 完成设备的注册。具体过程参见系统初始化的相关部分。

    3.  系统初始化

    3.1.   系统初始化的主干线

    Start_kernel() èsetup_arch() èreset_init() è kernel_thread(init …) è init() è do_basic_setup() èdriver_init() è do_initcall()

     

    Start_kernel()函数负责初始化内核各个子系统,最后调用reset_init(),启动一个叫做init的内核线程,继续初始化。Start_kernel()函数在init/main.c中实现。

     

    asmlinkage void __init start_kernel(void)

    {

           char * command_line;

           extern struct kernel_param __start___param[], __stop___param[];

     

           smp_setup_processor_id();

     

           /*

            * Need to run as early as possible, to initialize the

            * lockdep hash:

            */

           lockdep_init();

     

           local_irq_disable();

           early_boot_irqs_off();

           early_init_irq_lock_class();

     

    /*

     * Interrupts are still disabled. Do necessary setups, then

     * enable them

     */

           lock_kernel();

           boot_cpu_init();

           page_address_init();

           printk(KERN_NOTICE);

           printk(linux_banner);

           setup_arch(&command_line);

     //setup processor and machine and destinate some pointers for do_initcalls() functions

    // for example init_machine pointer is initialized with smdk_machine_init() function , and //init_machine() function is called by customize_machine(), and the function is processed by //arch_initcall(fn). Therefore  smdk_machine_init() is issured.    by edwin

           setup_per_cpu_areas();

           smp_prepare_boot_cpu();     /* arch-specific boot-cpu hooks */

     

           /*

            * Set up the scheduler prior starting any interrupts (such as the

            * timer interrupt). Full topology setup happens at smp_init()

            * time - but meanwhile we still have a functioning scheduler.

            */

           sched_init();

           /*

            * Disable preemption - early bootup scheduling is extremely

            * fragile until we cpu_idle() for the first time.

            */

           preempt_disable();

           build_all_zonelists();

           page_alloc_init();

           printk(KERN_NOTICE "Kernel command line: %s/n", saved_command_line);

           parse_early_param();

           parse_args("Booting kernel", command_line, __start___param,

                     __stop___param - __start___param,

                     &unknown_bootoption);

           sort_main_extable();

           unwind_init();

           trap_init();

           rcu_init();

           init_IRQ();

           pidhash_init();

           init_timers();

           hrtimers_init();

           softirq_init();

           timekeeping_init();

           time_init();

           profile_init();

           if (!irqs_disabled())

                  printk("start_kernel(): bug: interrupts were enabled early/n");

           early_boot_irqs_on();

           local_irq_enable();

     

           /*

            * HACK ALERT! This is early. We're enabling the console before

            * we've done PCI setups etc, and console_init() must be aware of

            * this. But we do want output early, in case something goes wrong.

            */

           console_init();

           if (panic_later)

                  panic(panic_later, panic_param);

     

           lockdep_info();

     

           /*

            * Need to run this when irqs are enabled, because it wants

            * to self-test [hard/soft]-irqs on/off lock inversion bugs

            * too:

            */

           locking_selftest();

     

    #ifdef CONFIG_BLK_DEV_INITRD

           if (initrd_start && !initrd_below_start_ok &&

                         initrd_start < min_low_pfn << PAGE_SHIFT) {

                  printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "

                      "disabling it./n",initrd_start,min_low_pfn << PAGE_SHIFT);

                  initrd_start = 0;

           }

    #endif

           vfs_caches_init_early();

           cpuset_init_early();

           mem_init();

           kmem_cache_init();

           setup_per_cpu_pageset();

           numa_policy_init();

           if (late_time_init)

                  late_time_init();

           calibrate_delay();

           pidmap_init();

           pgtable_cache_init();

           prio_tree_init();

           anon_vma_init();

    #ifdef CONFIG_X86

           if (efi_enabled)

                  efi_enter_virtual_mode();

    #endif

           fork_init(num_physpages);

           proc_caches_init();

           buffer_init();

           unnamed_dev_init();

           key_init();

           security_init();

           vfs_caches_init(num_physpages);

           radix_tree_init();

           signals_init();

           /* rootfs populating might need page-writeback */

           page_writeback_init();

    #ifdef CONFIG_PROC_FS

           proc_root_init();

    #endif

           cpuset_init();

           taskstats_init_early();

           delayacct_init();

     

           check_bugs();

     

           acpi_early_init(); /* before LAPIC and SMP init */

     

           /* Do the rest non-__init'ed, we're now alive */

           rest_init();

    }

     

    分析start_kernel()源码, 其中setup_arch() 和 reset_init()是两个比较关键的函数。下面将具体分析这两个函数。

    3.2.   setup_arch()函数分析

    首先我们来分析下setup_arch()函数。

    Setup_arch()函数主要工作是安装cpu和machine,并为start_kernel()后面的初始化函数指针指定值。

    其中setup_processor()函数调用linux/arch/arm/kernel/head_common.S 中的lookup_processor_type函数查询处理器的型号并安装。

     

    Setup_machine()函数调用inux/arch/arm/kernel/head_common.S 中的lookup_machine_type(__machine_arch_type)函数根据体系结构号__machine_arch_type,在__arch_info_begin和__arch_info_end段空间查询体系结构。问题是__machine_arch_type是在什么时候赋的初值?__arch_info_begin和__arch_info_end段空间到底放的是什么内容?

    __machine_arch_type是一个全局变量,在linux/boot/decompress/misc.c的解压缩函数中得以赋值。

    decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p, int arch_id)

    {

           __machine_arch_type    = arch_id;

    }

     

    __arch_info_begin和__arch_info_end段空间到底放的内容由链接器决定,存放是.arch.info.init段的内容。这个段是通过段属性__attribute__指定的。Grep一下.arch.info.init 得到./include/asm/mach/arch.h:53: __attribute__((__section__(".arch.info.init"))) = {       / 在linux/include/asm-arm/mach/arch.h 中发现MACHINE_START宏定义。

     

    #define MACHINE_START(_type,_name)                  /

    static const struct machine_desc __mach_desc_##_type    /

     __attribute_used__                                   /

     __attribute__((__section__(".arch.info.init"))) = {     /

           .nr          = MACH_TYPE_##_type,            /

           .name             = _name,

     

    #define MACHINE_END                            /

    };

     

    inux/arch/arm/mach-s3c2410/mach-smdk2410.c中对.arch.info.init段的初始化如下。

    MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch

                                    * to SMDK2410 */

           /* Maintainer: Jonas Dietsche */

           .phys_io  = S3C2410_PA_UART,

           .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

           .boot_params  = S3C2410_SDRAM_PA + 0x100,

           .map_io          = smdk2410_map_io,

           .init_irq   = s3c24xx_init_irq,

           .init_machine  = smdk_machine_init,

           .timer             = &s3c24xx_timer,

    MACHINE_END

     

    由此可见在.arch.info.init段内存放了__desc_mach_desc_SMDK2410结构体。初始化了相应的初始化函数指针。问题又来了, 这些初始化指针函数是什么时候被调用的呢?

    分析发现,不一而同。

    如s3c24xx_init_irq()函数是通过start_kernel()里的init_IRQ()函数调用init_arch_irq()实现的。因为在MACHINE_START结构体中  .init_irq       = s3c24xx_init_irq,而在setup_arch()函数中init_arch_irq = mdesc->init_irq, 所以调用init_arch_irq()就相当于调用了s3c24xx_init_irq()。

    又如smdk_machine_init()函数的初始化。在MACHINE_START结构体中,函数指针赋值,.init_machine      = smdk_machine_init。而init_machine()函数被linux/arch/arm/kernel/setup.c文件中的customize_machine()函数调用并被arch_initcall(Fn)宏处理,arch_initcall(customize_machine)。 被arch_initcall(Fn)宏处理过函数将linux/init/main.c

    do_initcalls()函数调用。 具体参看下边的部分。

     

    void __init setup_arch(char **cmdline_p)

    {

           struct tag *tags = (struct tag *)&init_tags;

           struct machine_desc *mdesc;

           char *from = default_command_line;

     

           setup_processor();

           mdesc = setup_machine(machine_arch_type);//machine_arch_type =SMDK2410  by edwin

           machine_name = mdesc->name;

     

           if (mdesc->soft_reboot)

                  reboot_setup("s");

     

           if (mdesc->boot_params)

                  tags = phys_to_virt(mdesc->boot_params);

     

           /*

            * If we have the old style parameters, convert them to

            * a tag list.

            */

           if (tags->hdr.tag != ATAG_CORE)

                  convert_to_tag_list(tags);

           if (tags->hdr.tag != ATAG_CORE)

                  tags = (struct tag *)&init_tags;

     

           if (mdesc->fixup)

                  mdesc->fixup(mdesc, tags, &from, &meminfo);

     

           if (tags->hdr.tag == ATAG_CORE) {

                  if (meminfo.nr_banks != 0)

                         squash_mem_tags(tags);

                  parse_tags(tags);

           }

     

           init_mm.start_code = (unsigned long) &_text;

           init_mm.end_code   = (unsigned long) &_etext;

           init_mm.end_data   = (unsigned long) &_edata;

           init_mm.brk       = (unsigned long) &_end;

     

           memcpy(saved_command_line, from, COMMAND_LINE_SIZE);

           saved_command_line[COMMAND_LINE_SIZE-1] = '/0';

           parse_cmdline(cmdline_p, from);

           paging_init(&meminfo, mdesc);

           request_standard_resources(&meminfo, mdesc);

     

    #ifdef CONFIG_SMP

           smp_init_cpus();

    #endif

     

           cpu_init();

     

           /*

            * Set up various architecture-specific pointers

            */

           init_arch_irq = mdesc->init_irq;

           system_timer = mdesc->timer;

           init_machine = mdesc->init_machine;

     

    #ifdef CONFIG_VT

    #if defined(CONFIG_VGA_CONSOLE)

           conswitchp = &vga_con;

    #elif defined(CONFIG_DUMMY_CONSOLE)

           conswitchp = &dummy_con;

    #endif

    #endif

    }

    3.3.   rest_init()函数分析

    下面我们来分析下rest_init()函数。

    Start_kernel()函数负责初始化内核各子系统,最后调用reset_init(),启动一个叫做init的内核线程,继续初始化。在init内核线程中,将执行下列init()函数的程序。Init()函数负责完成根文件系统的挂接、初始化设备驱动程序和启动用户空间的init进程等重要工作。

     

    static void noinline rest_init(void)

           __releases(kernel_lock)

    {

           kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);

           numa_default_policy();

           unlock_kernel();

     

           /*

            * The boot idle thread must execute schedule()

            * at least one to get things moving:

            */

           preempt_enable_no_resched();

           schedule();

           preempt_disable();

     

           /* Call into cpu_idle with preempt disabled */

           cpu_idle();

    }

     

     

    static int init(void * unused)

    {

           lock_kernel();

           /*

            * init can run on any cpu.

            */

           set_cpus_allowed(current, CPU_MASK_ALL);

           /*

            * Tell the world that we're going to be the grim

            * reaper of innocent orphaned children.

            *

            * We don't want people to have to make incorrect

            * assumptions about where in the task array this

            * can be found.

            */

           child_reaper = current;

     

           smp_prepare_cpus(max_cpus);

     

           do_pre_smp_initcalls();

     

           smp_init();

           sched_init_smp();

     

           cpuset_init_smp();

     

           /*

            * Do this before initcalls, because some drivers want to access

            * firmware files.

            */

           populate_rootfs();   //挂接根文件系统

     

           do_basic_setup();   //初始化设备驱动程序

     

           /*

            * check if there is an early userspace init.  If yes, let it do all

            * the work        //启动用户空间的init进程

            */

     

           if (!ramdisk_execute_command)

                  ramdisk_execute_command = "/init";

     

           if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {

                  ramdisk_execute_command = NULL;

                  prepare_namespace();

           }

     

           /*

            * Ok, we have completed the initial bootup, and

            * we're essentially up and running. Get rid of the

            * initmem segments and start the user-mode stuff..

            */

           free_initmem();

           unlock_kernel();

           mark_rodata_ro();

           system_state = SYSTEM_RUNNING;

           numa_default_policy();

     

           if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)

                  printk(KERN_WARNING "Warning: unable to open an initial console./n");

     

           (void) sys_dup(0);

           (void) sys_dup(0);

     

           if (ramdisk_execute_command) {

                  run_init_process(ramdisk_execute_command);

                  printk(KERN_WARNING "Failed to execute %s/n",

                                ramdisk_execute_command);

           }

     

           /*

            * We try each of these until one succeeds.

            *

            * The Bourne shell can be used instead of init if we are

            * trying to recover a really broken machine.

            */

           if (execute_command) {

                  run_init_process(execute_command);

                  printk(KERN_WARNING "Failed to execute %s.  Attempting "

                                       "defaults.../n", execute_command);

           }

           run_init_process("/sbin/init");

           run_init_process("/etc/init");

           run_init_process("/bin/init");

           run_init_process("/bin/sh");

     

           panic("No init found.  Try passing init= option to kernel.");

    }

     

    3.3.1. 挂接根文件系统

    Linux/init/ramfs.c

    void __init populate_rootfs(void)

    {

           char *err = unpack_to_rootfs(__initramfs_start,

                          __initramfs_end - __initramfs_start, 0);

           if (err)

                  panic(err);

    #ifdef CONFIG_BLK_DEV_INITRD

           if (initrd_start) {

    #ifdef CONFIG_BLK_DEV_RAM

                  int fd;

                  printk(KERN_INFO "checking if image is initramfs...");

                  err = unpack_to_rootfs((char *)initrd_start,

                         initrd_end - initrd_start, 1);

                  if (!err) {

                         printk(" it is/n");

                         unpack_to_rootfs((char *)initrd_start,

                                initrd_end - initrd_start, 0);

                         free_initrd();

                         return;

                  }

                  printk("it isn't (%s); looks like an initrd/n", err);

                  fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);

                  if (fd >= 0) {

                         sys_write(fd, (char *)initrd_start,

                                       initrd_end - initrd_start);

                         sys_close(fd);

                         free_initrd();

                  }

    #else

                  printk(KERN_INFO "Unpacking initramfs...");

                  err = unpack_to_rootfs((char *)initrd_start,

                         initrd_end - initrd_start, 0);

                  if (err)

                         panic(err);

                  printk(" done/n");

                  free_initrd();

    #endif

           }

    #endif

    }

     

    3.3.2. 初始化设备驱动程序

    linux/init/main.c

    static void __init do_basic_setup(void)

    {

           /* drivers will send hotplug events */

           init_workqueues();

           usermodehelper_init();

           driver_init();   /* 初始化驱动程序模型。调用驱动初始化函数初始化子系统。 */

     

    #ifdef CONFIG_SYSCTL

           sysctl_init();

    #endif

     

           do_initcalls();

    }

     

     

    linux/init/main.c

    extern initcall_t __initcall_start[], __initcall_end[];

     

    static void __init do_initcalls(void)

    {

           initcall_t *call;

           int count = preempt_count();

     

           for (call = __initcall_start; call < __initcall_end; call++) {

                  char *msg = NULL;

                  char msgbuf[40];

                  int result;

     

                  if (initcall_debug) {

                         printk("Calling initcall 0x%p", *call);

                         print_fn_descriptor_symbol(": %s()",

                                       (unsigned long) *call);

                         printk("/n");

                  }

     

                  result = (*call)();

     

                  ……

    ……

    ……

           }

     

           /* Make sure there is no pending stuff from the initcall sequence */

           flush_scheduled_work();

    }

    分析上面一段代码可以看出,设备的初始化是通过do_basic_setup()函数调用do_initcalls()函数,实现__initcall_start, __initcall_end段之间的指针函数执行的。而到底是那些驱动函数怎么会被集中到这个段内的呢?我们知道系统内存空间的分配是由链接器ld读取链接脚本文件决定。链接器将同样属性的文件组织到相同的段里面去,如所有的.text段都被放在一起。在链接脚本里面可以获得某块内存空间的具体地址。我们来看下linux-2.6.18.8/arch/arm/kernel/vmlinux.lds.S文件。由于文件过长,只贴出和__initcall_start, __initcall_end相关的部分。

    __initcall_start = .;

                         *(.initcall1.init)

                         *(.initcall2.init)

                         *(.initcall3.init)

                         *(.initcall4.init)

                         *(.initcall5.init)

                         *(.initcall6.init)

                         *(.initcall7.init)

                  __initcall_end = .;

    从脚本文件中我们可以看出, 在__initcall_start, __initcall_end之间放置的是属行为(.initcall*.init)的函数数据 。在linux/include/linux/init.h文件中可以知道,(.initcall*.init)属性是由__define_initcall(level, fn)宏设定的。

     

    #define __define_initcall(level,fn) /

           static initcall_t __initcall_##fn __attribute_used__ /

           __attribute__((__section__(".initcall" level ".init"))) = fn

     

    #define core_initcall(fn)        __define_initcall("1",fn)

    #define postcore_initcall(fn)         __define_initcall("2",fn)

    #define arch_initcall(fn)        __define_initcall("3",fn)

    #define subsys_initcall(fn)            __define_initcall("4",fn)

    #define fs_initcall(fn)                   __define_initcall("5",fn)

    #define device_initcall(fn)             __define_initcall("6",fn)

    #define late_initcall(fn)          __define_initcall("7",fn)

    #define __initcall(fn)      device_initcall(fn)

     

    由此可以判断,所有的设备驱动函数都必然通过*_initcall(fn)宏的处理。以此为入口,可以查询所有的设备驱动。

    core_initcall(fn)

    static int __init consistent_init(void)        linux/arch/arm/mm/consistent.c

    static int __init v6_userpage_init(void)      linux/arch/arm/mm/copypage-v6.c

    static int __init init_dma(void)             linux/arch/arm/kernel/dma.c

    static int __init s3c2410_core_init(void)     linux/arch/arm/mach-s3c2410/s3c2410.c

     

    postcore_initcall(fn)

    static int ecard_bus_init(void)                            linux/arch/arm/kernel/ecard.c

     

    arch_initcall(fn)

    static __init int bast_irq_init(void)                linux/arch/arm/mach-s3c2410/bast-irq.c

    static int __init s3c_arch_init(void)              linux/arch/arm/mach-s3c2410/cpu.c

    static __init int pm_simtec_init(void)         linux/arch/arm/mach-s3c2410/pm-simtec.c

    static int __init customize_machine(void)     linux/arch/arm/kernel/setup.c

     

    subsys_initcall(fn)

    static int __init ecard_init(void)                   linux/arch/arm/kernel/ecard.c

    int __init scoop_init(void)                           linux/arch/arm/common/scoop.c

    static int __init topology_init(void)        linux/arch/arm/kernel/setup.c

     

    fs_initcall(fn)

    static int __init alignment_init(void)             linux/arch/arm/mm/alignment.c

     

    device_initcall(fn)

    static int __init leds_init(void)                     linux/arch/arm/kernel/time.c

    static int __init timer_init_sysfs(void)          linux/arch/arm/kernel/time.c

     

    late_initcall(fn)

    static int __init crunch_init(void)                 arch/arm/kernel/crunch.c

    static int __init arm_mrc_hook_init(void)     linux/arch/arm/kernel/traps.c

     

    3.3.3. 启动用户空间的程序

    展开全文
  • 本文主要描述再EVB335x-II以Device Tree方式移植新TI官网AM335xxilie最新linux3.14.43版本的内核以及移植Debin文件系统的过程及遇到一些问题
  • linux内核移植演示

    2020-01-14 09:59:02
    本文档主要记录了linux内核移植的方法和步骤,对linux内核启动过程不做解释和说明。本文档基于linux3.4.39内核版本移植,ARM开发板为s5p6818板。 2、内核编译步骤 1、编译u-boot,生成配置规则,为内核编译做准备 u-...
  • 浅谈分析Arm linux 内核移植及系统初始化的过程(一)   学习嵌入式ARM linux,主要想必三个方向发展: 1、嵌入式linux应用软件开发 2、linux内核剪裁和移植 3、嵌入式linux底层...
  • ================================================================ 浅谈分析Arm linux 内核移植及系统初始化的过程(一) ================================================================学习嵌入式ARM ...
  • Rong edwinrong@mxic.com.cn 本文主要介绍内核移植过程中涉及文件分布及其用途,以及简单介绍系统初始化过程。整个arm linux内核启动可分为三个阶段:第一阶段主要是进行cpu和体系结构检查
  • 要介绍内核移植过程中涉及文件分布及其用途,以及简单介绍系统初始化过程。整个arm linux内核启动可分为三个阶段: 第一阶段主要是进行 cpu和体系结构检查、cpu本身初始化以及页表建立等; ...
  • 本课程为linux kernel移植的第3部分,主要内容是linux内核启动...本部分的学习目标是对linux内核的启动和构建过程有个清晰理解,对内核启动后的状态有清晰认识,从而能够帮助我们分析和解决内核移植过程中的问题。...
  • 文章主要参考《嵌入式Linux之我行——u-boot-2009.08在2440上的移植详解(五)》(http://blog.chinaunix.net/space.php?uid=22174347&do=blog&id=1786937) 虽然作者已经写得很详细了,但我在移植过程中还是遇到了...
  • 本篇文章,主要记录在4.1.15内核移植的过程中,对于TSC2007设备的添加,需要如何添加设备树信息。 1、TSC2007硬件原理图分析 先看相关原理图: tsc2007控制器芯片的原理图。 由tsc2007芯片原图与核心板相关连接的...
  • 本文介绍arm核cpu裸机启动过程。在cpu reset之后,pc会指向reset vector (地址位于0x00000000 or 0xFFFF0000),此时代码需要做以下几件事情: 在多核系统中,让非主要核睡眠。 初始化异常向量 初始化memory,包括...
  • 本课程为linux kernel移植的第2部分,主要内容是内核的配置和编译过程实践、配置和编译原理解析,其中重点是menuconfig的实现和Kconfig、.config文件的关系。
  • 昨天中午下载,用了半天时间完成了针对ARM CPU基本配置,第一次做Linux移植就出乎意料顺利,其实这主要得益于Linux内核优良设计和该版本对ARM处理器近乎完美支持!关键词: Linux 2.6.27 内核 移植 ARM ...
  • S3C2440ARM芯片+linux2.6.30.4内核平台学习过程 原文地址链接请点击 最近这段时间一直在持续学习linux,主要是基于S3C2440ARM芯片,linux的内核是2.6.30.4;目前总体上来说进展比较顺利,经过NandFlash驱动...
  • 基于gec2410开发板,主要讲解移植的过程和步骤。 步骤如下:建立工作目录:mkdir /home/build_kernel1 下载linux内核源码从http://www.kernel.org/pub/linux/kernel/v2.6/linux2.6.14.1.tar.bz2下载linux2.6.14.1...
  • linux内核数据库sqlite3的移植和简单操作 基于嵌入式linux数据库主要有SQLite, Firebird, Berkeley DB, eXtremeDB 这几种数据库特点: 1.Firebird是关系型数据库,功能强大,支持存储过程、SQL兼容等  2....
  •  本文主要是记录下我在这整个过程中所遇到问题及解决方法,主要的过程网上很多文章都写过了,我只是补充些我自己看法及问题。这可以说是一个痛苦的过程,因为各种问题不断,困扰了我很,不过黄天不负有心人,...

空空如也

空空如也

1 2 3 4 5 ... 11
收藏数 217
精华内容 86
关键字:

内核移植的主要过程