FriendlyARM Suppervivi + linux-2.6.29.1 + rootfs_busybox-1.13.3

最近一直在琢磨mini2440开发板上移植bootloader、linux kernel以及rootfs这三者的“内幕”。其过程可谓艰辛啊,万事开头难吗,但我们不可知难而退。也就是这个学习的过程,会使你自己受益非浅,进而鼓励自己朝着感兴趣的方向一直前进。。。

下面针对移植工作一些简单的总结,当然这期间也参考了网上大量的BLOG信息,以及相关书籍。不多说了,进入主题。

PS:以下的移植过程均是基于Nor 模式,即以FriendlyARM的supervivi作为bootloader。当然mini2440板子上的启动模式要设置为Nor 模式。

Part 1 关于bootloader

对于mini2440来说,系统的引导方式可以分为Nor 与Nand 两种模式。个人理解两种模式的主要功能与作用基本上一样:配置内核启动前的硬件环境(例如System Clock、MMU、Uart等模块的初始化),然后启动内核。但Nor Flash模式的交互性会更强引起。

  • Nor Mode:FriendlyARM在出厂时就将supervivi被写在了nor flash中,如果不借助ICE(JTAG仿真器)是无法更新supervivi的。supervivi也是基于vivi bootloader修改过来的。其内部工作流程以及相关detail可以参考vivi之源码。
  • Nand Mode:nand 模式下的bootloader可以被更新(即在Nor 模式通过相关命令更新之),从而达到引导不同OS Kernel的要求。具体细节可参考相关资料。

下面是摘自网络上fafen在其BLOG的一段介绍(感谢fafen):

1 Bootloader的概念和作用

Bootloader是嵌入式系统的引导加载程序,它是系统上电后运行的第一段程序,其作用类似于 PC 机上的 BIOS。在完成对系统的初始化任务之后,它会将非易失性存储器(通常是FlashDOC等)中的Linux 内核拷贝到 RAM 中去,然后跳转到内核的第一条指令处继续执行,从而启动 Linux 内核。由此可见,Bootloader Linux 内核有着密不可分的联系,要想清楚的了解 Linux内核的启动过程,我们必须先得认识 Bootloader的执行过程,这样才能对嵌入式系统的整个启动过程有清晰的掌握。

2 Bootloader的执行过程

不同的处理器上电或复位后执行的第一条指令地址并不相同,对于 ARM 处理器来说,该地址为 0x00000000。对于一般的嵌入式系统,通常把 Flash 等非易失性存储器映射到这个地址处,而 Bootloader就位于该存储器的最前端,所以系统上电或复位后执行的第一段程序便是Bootloader。而因为存储 Bootloader的存储器不同,Bootloader的执行过程也并不相同,下面将具体分析。

嵌入式系统中广泛采用的非易失性存储器通常是 Flash,而 Flash 又分为 Nor Flash Nand Flash 两种。 它们之间的不同在于:Nor Flash 支持芯片内执行(XIP eXecute In Place),这样代码可以在Flash上直接执行而不必拷贝到RAM中去执行。而Nand Flash并不支持XIP,所以要想执行 Nand Flash 上的代码,必须先将其拷贝到 RAM中去,然后跳到 RAM 中去执行。

3Bootloader的功能

实际应用中的 Bootloader根据所需功能的不同可以设计得很复杂,除完成基本的初始化系统和调用 Linux 内核等基本任务外,还可以执行很多用户输入的命令,比如设置 Linux 启动参数,给 Flash 分区等;也可以设计得很简单,只完成最基本的功能。但为了能达到启动Linux 内核的目的,所有的 Bootloader都必须具备以下功能:

(1)、初始化 RAM

因为 Linux 内核一般都会在 RAM 中运行,所以在调用 Linux 内核之前 bootloader 必须设置和初始化 RAM,为调用 Linux内核做好准备。初始化 RAM 的任务包括设置CPU 的控制寄存器参数,以便能正常使用 RAM 以及检测RAM 大小等。

 (2)、初始化串口

串口在 Linux 的启动过程中有着非常重要的作用,它是 Linux内核和用户交互的方式之一。Linux 在启动过程中可以将信息通过串口输出,这样便可清楚的了解 Linux 的启动过程。虽然它并不是 Bootloader 必须要完成的工作,但是通过串口输出信息是调试Bootloader Linux 内核的强有力的工具,所以一般的 Bootloader 都会在执行过程中初始化一个串口做为调试端口。

(3)、检测处理器类型

Bootloader在调用 Linux内核前必须检测系统的处理器类型,并将其保存到某个常量中提供给 Linux 内核。Linux 内核在启动过程中会根据该处理器类型调用相应的初始化程序。

(4)、设置 Linux启动参数

Bootloader在执行过程中必须设置和初始化 Linux 的内核启动参数。目前传递启动参数主要采用两种方式:即通过 struct param_struct struct tag(标记列表,tagged list)两种结构传递。struct param_struct 是一种比较老的参数传递方式,在 2.4 版本以前的内核中使用较多。从 2.4 版本以后 Linux 内核基本上采用标记列表的方式。但为了保持和以前版本的兼容性,它仍支持 struct param_struct 参数传递方式,只不过在内核启动过程中它将被转换成标记列表方式。标记列表方式是种比较新的参数传递方式,它必须以 ATAG_CORE 开始,并以ATAG_NONE 结尾。中间可以根据需要加入其他列表。Linux内核在启动过程中会根据该启动参数进行相应的初始化工作。

(5)、调用 Linux内核映像

Bootloader完成的最后一项工作便是调用 Linux内核。如果 Linux 内核存放在 Flash 中,并且可直接在上面运行(这里的 Flash Nor Flash),那么可直接跳转到内核中去执行。但由于在 Flash 中执行代码会有种种限制,而且速度也远不及 RAM 快,所以一般的嵌入式系统都是将 Linux内核拷贝到 RAM 中,然后跳转到 RAM 中去执行。

不论哪种情况,在跳到 Linux 内核执行之前 CPU的寄存器必须满足以下条件:r00r1=处理器类型,r2=标记列表在 RAM中的地址。

Part 2  kernel移植

1:  Linux启动过程

Linux的启动过程可以概括为:查找处理器内核类型和处理器类型调用相应的初始化函数,再建立页表,最后跳转到 start_kernel()函数开始内核的初始化工作。

当然其内部细节现在还没必要去深究,只要我们知道是这么回事即可。下面同样为fafen在BLOG中语录(再次感谢fafen):

Bootloader Linux 内核映像拷贝到 RAM 以后,可以通过下例代码启动 Linux 内核:

call_linux(0, machine_type, kernel_params_base)

其中,machine_tpye Bootloader检测出来的处理器类型, kernel_params_base 是启动参数在 RAM 的地址。通过这种方式将 Linux 启动需要的参数从 bootloader传递到内核。

Linux 内核有两种映像:一种是非压缩内核,叫 Image,另一种是它的压缩版本,叫 zImage。根据内核映像的不同,Linux 内核的启动在开始阶段也有所不同。zImage Image经过压缩形成的,所以它的大小比 Image 小。但为了能使用 zImage,必须在它的开头加上解压缩的代码,将 zImage 解压缩之后才能执行,因此它的执行速度比 Image 要慢。但考虑到嵌入式系统的存储空容量一般比较小,采用 zImage 可以占用较少的存储空间,因此牺牲一点性能上的代价也是值得的。所以一般的嵌入式系统均采用压缩内核的方式。

对于ARM 系列处理器来说,zImage 的入口程序即为 arch/arm/boot/compressed/head.S。它依次完成以下工作:开启 MMU Cache,调用 decompress_kernel()解压内核,最后通过调用 call_kernel()进入非压缩内核 Image 的启动。下面将具体分析在此之后 Linux 内核的启动过程。

1)、 Linux内核入口

Linux 非压缩内核的入口位于文件/arch/arm/kernel/head-armv.S 中的stext 段。该段的基地址就是压缩内核解压后的跳转地址。如果系统中加载的内核是非压缩的 Image,那么bootloader将内核从 Flash中拷贝到 RAM 后将直接跳到该地址处,从而启动 Linux 内核。不同体系结构的 Linux 系统的入口文件是不同的,而且因为该文件与具体体系结构有关,所以一般均用汇编语言编写。对基于 ARM 处理的 Linux 系统来说,该文件就是head-armv.S。该程序通过查找处理器内核类型和处理器类型调用相应的初始化函数,再建立页表,最后跳转到 start_kernel()函数开始内核的初始化工作。检测处理器内核类型是在汇编子函数__lookup_processor_type中完成的。通过以下代码可实现对它的调用:

bl __lookup_processor_type

__lookup_processor_type调用结束返回原程序时,会将返回结果保存到寄存器中。其中r8 保存了页表的标志位,r9 保存了处理器的 ID 号,r10 保存了与处理器相关的 stru proc_info_list 结构地址。

检测处理器类型是在汇编子函数 __lookup_architecture_type 中完成的。与 __lookup_processor_type类似,它通过代码:“bl __lookup_processor_type”来实现对它的调用。该函数返回时,会将返回结构保存在 r5r6 r7 三个寄存器中。其中 r5 保存了 RAM 的起始基地址,r6 保存了 I/O基地址,r7 保存了 I/O的页表偏移地址。当检测处理器内核和处理器类型结束后,将调用__create_page_tables 子函数来建立页表,它所要做的工作就是将 RAM 基地址开始的 4M 空间的物理地址映射到 0xC0000000 开始的虚拟地址处。对笔者的 S3C2410 开发板而言,RAM 连接到物理地址 0x30000000 处,当调用 __create_page_tables 结束后 0x30000000 0x30400000 物理地址将映射到 0xC00000000xC0400000 虚拟地址处。当所有的初始化结束之后,使用如下代码来跳到 C 程序的入口函数 start_kernel()处,开始之后的内核初始化工作:b SYMBOL_NAME(start_kernel)

2)start_kernel函数

start_kernel是所有 Linux 平台进入系统内核初始化后的入口函数,它主要完成剩余的与硬件平台相关的初始化工作,在进行一系列与内核相关的初始化后,调用第一个用户进程-init 进程并等待用户进程的执行,这样整个 Linux 内核便启动完毕。该函数所做的具体工作有:调用 setup_arch()函数进行与体系结构相关的第一个初始化工作;对不同的体系结构来说该函数有不同的定义。对于 ARM 平台而言,该函数定义在arch/arm/kernel/Setup.c。它首先通过检测出来的处理器类型进行处理器内核的初始化,然后通过 bootmem_init()函数根据系统定义的 meminfo 结构进行内存结构的初始化,最后调用paging_init()开启 MMU,创建内核页表,映射所有的物理内存和 IO空间。创建异常向量表和初始化中断处理函数;初始化系统核心进程调度器和时钟中断处理机制;初始化串口控制台(serial-console);ARM-Linux 在初始化过程中一般都会初始化一个串口做为内核的控制台,这样内核在启动过程中就可以通过串口输出信息以便开发者或用户了解系统的启动进程。创建和初始化系统 cache,为各种内存调用机制提供缓存,包括;动态内存分配,虚拟文件系统(VirtualFile System)及页缓存。初始化内存管理,检测内存大小及被内核占用的内存情况;初始化系统的进程间通信机制(IPC);当以上所有的初始化工作结束后,start_kernel()函数会调用 rest_init()函数来进行最后的初始化,包括创建系统的第一个进程-init 进程来结束内核的启动。init 进程首先进行一系列的硬件初始化,然后通过命令行传递过来的参数挂载根文件系统。最后 init 进程会执行用户传递过来的“init=”启动参数执行用户指定的命令,或者执行以下几个进程之一:

execve("/sbin/init",argv_init,envp_init)

execve("/etc/init",argv_init,envp_init)

execve("/bin/init",argv_init,envp_init)

execve("/bin/sh",argv_init,envp_init)

当所有的初始化工作结束后,cpu_idle()函数会被调用来使系统处于闲置(idle)状态并等待用户程序的执行。至此,整个 Linux 内核启动完毕。

Linux 内核是一个非常庞大的工程,经过十多年的发展,它已从从最初的几百 KB 大小发展到现在的几百兆。清晰的了解它执行的每一个过程是件非常困难的事。但是在嵌入式开发过程中,我们并不需要十分清楚Linux 的内部工作机制,只要适当修改Linux 内核中那些与硬件相关的部分,就可以将Linux 移植到其它目标平台上。通过对Linux 的启动过程的分析,我们可以看出哪些是和硬件相关的,哪些是Linux 内核内部已实现的功能,这样在移植Linux 的过程中便有所针对。而Linux内核的分层设计将使Linux 的移植变得更加容易。

2.开工前的准备工作:

主机:VMWare6.0 + RedFlag (内核版本2.6.22.6)

编译器:友善之臂提供的arm-linux-4.3.2

Linux内核www.kernel.org/ 中的FTP链接 ftp://ftp.kernel.org/pub/,在里面进入文件夹“linux->kernel->v2.6,你会看到很多版本的内核压缩包和补丁,选中Linux-2.6.29.1.tar.bz2下载。

PS:FTP传输不够稳定,也可以在网上查找相关资源进行下载。

yaffs2源码进入http://www.aleph1.co.uk/cgi-bin/viewcvs.cgi/,点击“Download GNU tarball”,下载后出现cvs-root.tar.gz压缩包。

3:Linux内核相关配置及源码修改

1)、 进入内核目录,修改makefile,并对内核进行默认配置进行修改

       193行,修改

       ARCH                        ?=arm

       CROSS_COMPILE   ?=arm-linux-

2)、 修改平台输入时钟 (Fin)

        找到内核源码arch/arm/mach-s3c2440/mach-smdk2440.c文件,在函数 static void __init smdk2440_map_io(void)中,修改成s3c24xx_init_clocks(12000000)。//12MHz

3)、 修改machine名称(可以跳过此步骤)

修改文件arch/arm/mach-s3c2440/mach-smdk2440.c,在文件中找到MACHINE_START( ),修改为MACHINE_START(S3C2440, “Board S3C2440”)

4)、 修改Nand flash分区信息

修改文件kernel.git/arch/arm/plat-s3c24xx/common-smdk.c

第一,修改分区信息:

static struct mtd_partition smdk_default_nand_part[] = {

       [0] = {

       .name = "bootloader",

       .offset = 0x00000000, 

       .size = 0x00060000, //此处网上很多高手说是0x00030000。这上值具体要与supervivi启动时的值对应起来,本人的板上的值为0x00060000。以下同样如此。

Part 3  rootfs移植

关于此部分的移植,可参考另外一文:http://blog.csdn.net/itismine/archive/2009/11/05/4772284.aspx

PS:

总结

以上的工作也只是完成kernel、rootfs的移植,对于一个新的board来说,这些远远不够,因为还有外围的设备驱动需要编写;以及bootloader的detail有待研究。总之,如果需要构建一个完整的、成功的系统,还有大量的工作有待完成。

学习中,前进中……

       },

       [1] = {

       .name = "kernel",

       .offset = 0x00060000, //网络上为0x00050000

       .size = 0x00200000,

       },

       [2] = {

       .name = "root",

       .offset = 0x00260000, //网络上为0x00250000

       .size = 0x03dac000,

       }

};

第二,再修改s3c2410_platform_nand_smdk_nand_info smdk_nand_info = {

.tacls = 0,

.twrph0 = 30,

.twrph1=0,

};

5)、 修改LCD背光 (可跳过此步骤)

修改文件/arch/arm/mach-s3c2440/mach-smdk2440.c,由于FriendlyARM的3.5寸液晶的背光控制是由S3C2440GPG4引脚来控制的,故下面的改动将开启背光。

static void __init smdk2440_machine_init(void)

{

       s3c24xx_fb_set_platdata(&smdk2440_fb_info);

       platform_add_devices();

       s3c2410_gpio_cfgpin(S3C2410_GPG4,S3C2410_GPG4_OUTP);

       s3c2410_gpio_setpin(S3C2410_GPG4,1);                                     smdk_machine_init();

}

6)、 LCD参数修改

        这里用的是NEC3.5英寸屏液晶屏,分辨率为320x240,需要修改修改文件arch/arm/mach-s3c2440/mach-smdk2440.c

static struct s3c2410fb_display smdk2440_lcd_cfg __initdata =

{

      

       .right_margin = 37,

       .hsync_len = 6, 

       .upper_margin =2,

       .lower_margin = 6,

       .vsync_len =2,

};

static struct s3c2410fb_mach_info smdk2440_fb_info   __initdata      ={

.default_display =0

       .gpccon         = 0xaa955699,

       .gpccon_mask      = 0xffc003cc,

       .gpcup           = 0x0000ffff,

       .gpcup_mask = 0xffffffff,

       .gpdcon         = 0xaa95aaa1,

       .gpdcon_mask      = 0xffc0fff0,

       .gpdup           = 0x0000faff,

       .gpdup_mask = 0xffffffff,

.lpcsel = 0xf82,

};

7)、 给内核安装yaffs2文件系统的补丁

   #tar -zxvf cvs-root.tar.gz // 解压cvs-root.tar.gz,具体位置不限。

   #cd ~/cvs-root/yaffs2/

   #./patch-ker.sh c ~/linux-2.6.29.1/ 

patch-ker.sh自动完成以下3件事情:

(1) 修改内核fs/Kconfig

    增加一行:source "fs/yaffs2/Kconfig"

(2) 修改内核fs/Kconfig

    增加一行:ojb-$(CONFIG_YAFFS_FS) +=yaffs2/

(3) 在内核fs/目录下创建yaffs2目录

    yaffs2源码目录下面的Makefile.kernel文件复制为内核fs/yaffs2/Makefie;

    yaffs2 源码目录的Kconfig文件复制到内核fs/yaffs2目录下;

    yaffs2源码目录下的*.c *.h文件复制到内核fs/yaffs2目录下.

8)、修改S3C2440的机器号

    关于mach_type,我们有两种方式来修改。

    (1)修改bootloader启动时传给内核的mach_type.

    (2)修改arch/arm/tools/mach-types文件中S3C2440的mach_type值 。

    本人使用的是方式(1),以下为fafen的做法。

由于Bootloader传递给Linux内核的机器号为782,为与Bootloader传递参数一致,修改 arch/arm/tools/math-types文件。

s3c2440                     ARCH_S3C2440              S3C2440                    362

修改为:

s3c2440                     ARCH_S3C2440              S3C2440                    782

另外,还可以不修改内核中的S3C2440机器号,只需修改修改Bootloader传递给内核的参数中的机器号就可以了。在VIVI中菜单中,按s,再按s,输入mach_type,回车,输入362w,保存。

4.配置Linux Kernel

1) 进入Linux-2.6.29.1内核主目录:/

    #cp arch/arm/config/s3c2410_defconfig ./ -f            

2) 配置内核模块的功能,有几种方式可以进行界面选择:

make menuconfig(文本选单的配置方式,在有字符终端下才能使用)

make xconfig(图形窗口模式的配置方式,图形窗口的配置比较直观,必须支持Xwindow下才能使用)

make oldconfig(文本配置方式,在原内核配置的基础修改时使用)

这里使用make menuconfig命令

3)[*]Enable loadable module support--->

              [*]Forced module loading

              [*]Module unloading

4)System Type--->

           S3C2410 Machines--->

                     [*]SMDK2410/A9M2410  /*选上,其余不选*/

           S3C2440 Machines--->

                     [*]SMDK2440

                     [*]SMDK2440 with S3C2440 CPU module /* 其余不选 */

其余的Machines下选项全部不选(如2400241224422443

5)Kernel Features--->

[*]Use the ARM EABI to compile the kernel

       注:由于所使用的的交叉编译arm-linux-gcc-4.3.2是符合EABI标准交叉编译器,对于浮点运行会预设硬浮点运算FPA(Float Point Architecture),而没有FPACPU,比如SAMSUNG S3C2410/S3C2440,会使用FPE(Float Point Emulation 即软浮点),这样在速度上就会遇到极大的限制,使用EABI(Embedded Application Binary Interface)则可以对此改善处理,ARM EABI有许多革新之处,其中最突出的改进就是Float Point Performance,它使用Vector Float Point(矢量浮点),因此可以极大提高涉及到浮点运算的程序。

参考:http://www.hotchn.cn/bbs/viewthread.php?tid=130&extra=page%3D1

6)Boot options-?

noinitrd root="/dev/mtdblock2" init="/linuxrc"  console=ttySAC0

7)Userspace binary formats--->

              [*]Kernel support for ELF binaries

其它的可以全部不选。

8) 选择支持yaffs2文件系统

Filesystem--->

              Miscellaneous filesystems--->

                     <*>YAFFS2 file system support

                     [ ]   Lets Yaffs do its own ECC

              Native language support

<*> Codepage 437 (United States,Canada)

<*>Simplified Chinese charset(GB2312)

<*>Traditional Chinese charset(Big5)

<*>NLS ISO 8859-1(Latin1:Western European Languages)

<*>NLS UTF-8

9)Device Drivers--->

              Graphics support--->

                     <*>Support for frame buffer devices--->

                            [*]Enable firmware EDID

                            [*]Enable Video Mode Handling Helpers

                            <*>S3C2410 LCD framebuffer support

                     Console display driver support--->

                            <*>Framebuffer Console support

                            [*]Select compiled-in fonts

                            [*] VGA8x8 font

                            [*]VGA8x16 font

              [*] Bootup logo--->

                            [*]Standard black and white Linux logo

                            [*]Standard 16-color Linux logo

                            [*]Standard 224-color Linux logo

Bootup logo--->选择的那几项,将会在系统启动时在液晶上显示开机logo

5. build内核

#make clean

#make zImage

build完成之后,会在arch/arm/boot/下生成zImage。

至此可将zImage下载至板上运行。

Part 3  rootfs移植

关于此部分的移植,可参考另外一文:http://blog.csdn.net/itismine/archive/2009/11/05/4772284.aspx

PS:由于nand flash为128M,在生成rootfs镜像文件image时,需要用FriendlyARM的mkyaffs2image-128M ,而不能使用mkyaffs2image。原因有待考证。

 

总结

以上的工作也只是完成kernelrootfs的移植,对于一个新的board来说,这些远远不够,因为还有外围的设备驱动需要编写;以及bootloaderdetail有待研究。总之,如果需要构建一个完整的、成功的系统,还有大量的工作有待完成。

学习中,前进中……

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值