移植Linux3.4.2到jz2440开发板(长文详细记录)

一、下载、配置、编译、烧写jffs2

1.下载、解压

    注意有时解压不了是文件权限的问题!

2.修改Makefile

3.配置

选用一种默认的配置:s3c2410_defconfig,创建配置文件:.config

默认配置在arch/arm/configs/下。。。。

4.make uImage生成内核映像文件

Q1:

sudo apt-get install uboot-mkimage

sudo apt-get install u-boot-tools

OK!

5.在windows下建立SIP

    18:10开始

    除了arch中有对arm的选择,其余文件看起来都是通用的,全部添加。若遇到问题就在Linux下grep 源码!

6.测试uImage

成功启动内核:

    

从以上可以看出:

    编译的是我carl;CPU是arm920T;Mahine是SMDK2410;S3C2410A的ID;系统初始化时钟(400M,100M,50M);command line就是启动参数bootaegs;内存64M;

    以下的MTD分区有问题:那么rootfs肯定不能正常挂载

    

    果然:

    

7.uboot启动Linux参数之machid

可以从两个地方获取:uboot环境变量(优先)  和   uboot源码:jz2440.c中board_init中设置

uboot命令:set machid xxx

如:set machid 16a  // smdk2440                       7cf    //mini2440

    不同的machid会让Linux使用不同的初始化函数,其中涉及单板的相关初始化,比较重要的是波特率,如果启动内核显示乱码则查看波特率的设置。(参数设置为单板使用的晶振大小)在该初始化结构体的smdk_map_io中。bootargs中的115200设置可以避免这个问题。

在Linux源码:arch/arm/mach-s3c24xx/Mach-xxxx.c中:

如:

其中的machid在Linux:include/asm-arm/mach-types.h中可以查看Linux支持的所有machid。

例如:

当uboot环境变量没有设置machid,那么machid一定会采用在uboot代码中的初始化:

果然,此时的machid = id of smdk2410      (当然,此时的整个系统完全能正常启动)

若:

 

    

    在Linux源码:mach-smdk2440.c中查看:

    

    

    可见在smdk2440的board初始化函数中使用了16934400这个镜子,改为实际板载的12000000

    make uImage后再测试:

    

    解决!

8.uboot传递的bootargs

    bootargs = console=ttySAC0,115200 root=/dev/mtdblock3 rootfstype=yaffs2

小结:

    以下几个步骤可以编译出可以从串口输出的uImage:

    a.uboot里设置machid(可以在代码中设置直接给内核,也可以在uboot shell中set machid xxx)

    b.根据machid在Linux源码中找到对应的初始化结构体,并在map_io中找到对应的函数,查看波特率设置,并设置爱为单板的晶振(也可以在uboot  的  bootargs中设置为:console=ttySAC0,115200)

    c.配置、编译Linux:make s3c2410_defconfig      make uImage(前提要修改Makefile  的ARCH和CROSS_COMPLIE)

    d.在uboot中设置bootargs = console=ttyASC0,115200  root=/dev/mtdblock3   rootfstype=jffs2   等

*****************************************************************************************************************************************

二、修改Linux分区和制作根文件系统

9.修改Linux分区

    在前面已经看出一个大问题,我们设置了参数root=/dev/mtdblock3   即我们想要挂载mtdblock3这个分区的文件系统作为rootfs,但是,现在我们的Linux分区和预先设置的不一样,导致了最后不能挂接,那么我们就来为Linux修改分区:

    根据打印分区的信息:                                        

    

    在Linux源码中查到:

    

    很明显应该在comm-smdk.c中:

    

    找到mtd分区的结构体。(arch/arm/mach-s3c24xx/common_smdk.c  注意不同的machid对应的分区结构体不一样)这个结构体每个成员就四个字段:分区name、偏移offset  和 大小size。(注意offset和size使用宏,在结构体定义处)

    修改为预先设想的四个分区:

    

    将arch/arm/mach-s3c24xx/common-smdk.c 拖过去编译

    编译测试:

    

    内核成功打印出分区信息。

    Q:不断输出这两类信息

    

    Q2:CRC错误,读不到内核

    

    这是由于boot command中对内核分区的设置小于内核实际的大小,导致内核没有被完全读出来,同时也会引发连锁反应:在烧写内核时会擦除fs的区域,导致fs不能被读取,同时内核打印出Q1信息。

    解决办法:

        法一:裁剪内核!(暂时不用这个方法)          法二:不烧写kernel,只是下载到SDRAM,用bootm addr运行它

    尝试挂载yaffs2文件系统:

    由info:

    

    知,目前系统不支持yaffs2文件系统。(在配置文件.config中可以查看支持哪些)

    尝试jffs2:VFS能找到fs挂载,但是init进程出错,也不行!

10.使用busybox制作根文件系统    

    首先,uboot启动内核,内核到bootargs设置的分区去读取文件系统,并执行文件系统中的一个引导程序,这个引导程序就好像一个配置文件,让内核去执行更多的应用程序,如打开串口,运行某些服务等等。

    busybox就是一系列命令的集合,如ls、chmod等。

    a.下载busybox源码,解压

        注意版本不要太高,否则可能对编译器版本有要求。

        

    b.配置、编译

        先vi Makefile设置ARCH 和  CROSS_COMPLILE(这里的交叉编译工具要和编译内核使用的版本一样)

        再make  menuconfig进入配置界面,再次设置CROSS_COMPLILE,里面有很多的选择,比如对命令的选择。采用默认配置吧,成功配置文件(make menuconfig  依赖于库:  libncurses5-dev         要先安装库sudo apt-get install libncurses5-dev)

        再make ,有问题就解决。  

        

    c.新建一个空的fs_dir,并安装busybox

    mkdir rootfs

    vi  INSTALL  可以看到如何安装:

    

    make  CONFIG_PREFIX=。。。。/rotfs/    install

    

    

    

    d.安装交叉编译工具的C库到fs_dir

    find -name “lib”

    

    安装armv4t的静态库:

    

    注意安装库的时候:加-d选项(把链接文件cp后也作为链接文件,否则会cp链接后的实际文件,导致fs异常庞大)

    p是保留源文件属性

    e.创建/etc

    

    内核启动fs的第一个进程是init进程,这个init进程可以是/sbin/init 也可以是linuxrc  在bootargs中设置init=linuxrc

    

    创建inittab

    https://www.xuebuyuan.com/3209620.html

    

    sysinit指定init首先五执行/etc/init.d/rcS  ,这是一个脚本文件

    askfirst指定执行shell

    ctrlaltdel指定组合键按下时执行reboot的程序

    shutdown指定关机时取消所有挂载的fs

    restart指定指定init进程

    创建/etc/init.d/rcS        https://www.cnblogs.com/PengfeiSong/p/6443149.html

    

    指定PATH并export 导出

    umask指定创建文件的默认权限(umask是022的时候,默认touch创建一个文件的权限是644)

    mount -a指定挂载所有的虚拟文件系统,要依赖于/etc/fstab中固定格式指定的fs

    mdev就是在/dev下自动创建设备驱动文件

    创建fstab

    

    这些挂载点的文件也是要创建的,不然没法挂载。

    f.创建/dev

    

    

    

    g.创建其他目录

    

    h.制作jffs2映像文件

    

    

    用mkfs.jffs2 制作映像文件,先安装mtd-utils

                            

    

烧写测试:

Q:成功挂载,但init被kill,exitcode = 4   (SIGILL  非法指令)

由于arm-linux-gcc是采用eabi接口的编译方式,那么内核一定要启用 ARM EABI接口,重新make menuconfig ,使能ARM EABI后

make  uImage

注意在make menuconfig中使用/查找很方便。

再次测试:成功挂载jffs2文件系统

    11.使Linux支持yaffs2文件系统

    yaffs2源码并没有合并到Linux中,总结的说就是将yaffs2源码合并到Linux源码,然后在Linux配置中启用yaffs2。

    a.下载、解压

    

    

    b.合并到Linux

    vi README-linux

    

    可知:在yaffs的目录下执行patch-ker.sh这个脚本文件,将yaffs源码打补丁到Linux源码中

    

    c.配置

    

    

    d.编译和debug,再编译,直到编译成功

    可能有两类错误:

        a.mtd结构体成员提示没有,实际上是有的,被定义为_xxxx,在使用的时候全用成xxxx。(在较新的yaffs2中没这个bug)

        b.r_root 的获取函数没定义,参考其他文件系统使用的d_make_root函数,直接替换。(在较新的yaffs2中没这个bug)

    

    e.制作yaffs2映像文件

    

    

    e.下载测试:

        如果出现问题,可能是uboot在烧写的时候对yaffs的支持不够友好,修改uboot源码。

        采用替换法,将uboot替换成可以用的,若还是同样的错误uboot肯定问题。

        以下是对uboot中yaffs部分的源码分析:

    /common/cmd_nand.c      do_nand函数中:

#ifdef CONFIG_CMD_NAND_YAFFS    //针对write.yaffs  命令  如nand  write.yaffs  30000000 offset    size

        } else if (!strcmp(s, ".yaffs")) {

            if (read) {//如果是read就输出错误
                printf("Unknown nand command suffix '%s'.\n", s);

                return 1;
            }

            ret = nand_write_skip_bad(nand, off, &rwsize,            //主要的烧写函数     off 和 rwsize对应上面的offset    size
                        (u_char *)addr, WITH_YAFFS_OOB);        

#endif



进入nand_write_skip_bad函数:drivers/mtd/nand/nand_until.c

/*
* nand_write_skip_bad:
* Write image to NAND flash.
* Blocks that are marked bad are skipped and the is written to the next
* block instead as long as the image is short enough to fit even after
* skipping the bad blocks.
*
* @param nand      NAND device
* @param offset    offset in flash
* @param length    buffer length
* @param buffer        buffer to read from
* @param flags        flags modifying the behaviour of the write to NAND
* @return        0 in case of success
*/
int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
            u_char *buffer, int flags)
{
    int rval = 0, blocksize;
    size_t left_to_write = *length;
    u_char *p_buffer = buffer;
    int need_skip;

#ifdef CONFIG_CMD_NAND_YAFFS

    //WITH_YAFFS_OOB是作为参数传进来的
    if (flags & WITH_YAFFS_OOB) {
        if (flags & ~WITH_YAFFS_OOB)//肯定是不行的。。。
            return -EINVAL;

        int pages; //erasesize  writesize  在nand_get_flash_type中设定

        //128K / 2K = 64页
        pages = nand->erasesize / nand->writesize;

        //blocksize 就是一块总的大小 = 128K + (oobsize*64页)
        blocksize = (pages * nand->oobsize) + nand->erasesize;

        if (*length % (nand->writesize + nand->oobsize)) {
            printf ("Attempt to write incomplete page"
                " in yaffs mode\n");

            return -EINVAL;
        }
    } else

#endif
    {
        blocksize = nand->erasesize;//不执行
    }

    /*
     * nand_write() handles unaligned, partial page writes.
     *
     * We allow length to be unaligned, for convenience in
     * using the $filesize variable.
     *
     * However, starting at an unaligned offset makes the
     * semantics of bad block skipping ambiguous (really,
     * you should only start a block skipping access at a
     * partition boundary).  So don't try to handle that.
     */
    if ((offset & (nand->writesize - 1)) != 0) {//对齐检查
        printf ("Attempt to write non page aligned data\n");

        *length = 0;

        return -EINVAL;

    }

    /*check_skip_len返回值:
        当offset在nand flash中无效时,返回-1(程序员应该不会范这种低级错误。。。)
        当offset在nand flash中有效时,若在offset和length之间有坏块则返回1,否则返回0
    */
    need_skip = check_skip_len(nand, offset, *length);

    if (need_skip < 0) {
        printf ("Attempt to write outside the flash area\n");
        *length = 0;

        return -EINVAL;
    }

    /*当need_skip = 0(无坏块) 且 flag中不存在WITH_DROP_FFS
        目前flag之没有WITH_DROP_FFS的,所以当没有坏块时就会在这里面执行nand_write,
        都不会到下面的while中取写了,所以下面的修改也就没意义了!!!
            重点:下面的while是针对有坏块的情况,涉及OOB的操作,所以可以在可以添加一个条件:
        !(WITH_YAFFS_OOB)即当没有定义WITH_YAFFS_OOB这个宏时。

    */

    //if (!need_skip && !(flags & WITH_DROP_FFS)) {
    if (!need_skip && !(flags & WITH_DROP_FFS) && !(WITH_YAFFS_OOB)) {
        rval = nand_write (nand, offset, length, buffer);
        if (rval == 0)
            return 0;

        *length = 0;

        printf ("NAND write to offset %llx failed %d\n",offset, rval);

        return rval;
    }

    while (left_to_write > 0) {//循环写
        size_t block_offset = offset & (nand->erasesize - 1);
        size_t write_size, truncated_write_size;

        WATCHDOG_RESET ();

        if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
            printf ("Skip bad block 0x%08llx\n",
                offset & ~(nand->erasesize - 1));
            offset += nand->erasesize - block_offset;
            continue;
        }

        if (left_to_write < (blocksize - block_offset))
            write_size = left_to_write;
        else
            write_size = blocksize - block_offset;

#ifdef CONFIG_CMD_NAND_YAFFS

        if (flags & WITH_YAFFS_OOB) {
            int page, pages;
            size_t pagesize = nand->writesize;
            size_t pagesize_oob = pagesize + nand->oobsize;
            struct mtd_oob_ops ops;
            
            ops.len = pagesize;
            ops.ooblen = nand->oobsize;
            ops.mode = MTD_OOB_RAW;//原来是MTD_OOB_AUTO;  
            ops.ooboffs = 0;

            pages = write_size / pagesize_oob;

            for (page = 0; page < pages; page++) {
                WATCHDOG_RESET();
                ops.datbuf = p_buffer;
                ops.oobbuf = ops.datbuf + pagesize;
                rval = nand->write_oob(nand, offset, &ops);
                //write_oob写第一页时,返回0,成功,但是下面的!会break写
                if (rval)        //原本为if(!rval)
                    break;
                offset += pagesize;
                p_buffer += pagesize_oob;
            }
        }
        else

#endif

        {
            truncated_write_size = write_size;

#ifdef CONFIG_CMD_NAND_TRIMFFS

            if (flags & WITH_DROP_FFS)
                truncated_write_size = drop_ffs(nand, p_buffer,
                        &write_size);
#endif

            rval = nand_write(nand, offset, &truncated_write_size,
                    p_buffer);
            offset += write_size;
            p_buffer += write_size;
        }

        if (rval != 0) {
            printf ("NAND write to offset %llx failed %d\n",
                offset, rval);
            *length -= left_to_write;

            return rval;
        }
        left_to_write -= write_size;
    }

    return 0;

}

 经以上改正的三个bug,再次测验就OK!

 测验:

12.在默认配置上裁剪Linux

    首先要清除make clean   make  mrproper   make  distclean的区别:

    make clean:清除一些编译生成的文件。

    make mrproper:在clean的基础上,清除配置文件(.config),和各种备份文件

    make distclean:在mrproper的基础上,清除一些编辑备份文件和补丁文件

    在下载源码后,本身就是干净了,就算执行mrproper也费不了时间,无所谓。在第一次配置生成配置文件后,若只修改源文件最好不要mrproper,会拖长编译时间,因为make本身就会检查该文件是否需要重新编译。当需要全部重新从源码配置才需要mrproper。至于make distclean的清除范围更大了,应该会回到源码的初始状态,不如重新tar。

    现在重0开始配置、编译、裁剪:

    a.修改顶层Makefile之ARCH = arm     CROSSXXX = arm-linux-

    b.初始配置:make  s3c2410_defconfig   会生成.config文件(采用Linux中的一种默认soc配置,可以在arch/arm/configs中找到各种默认配置文件)

    c.二次配置:make  menuconfig  进入图形化配置界面,可视化的菜单配置(比如CROSS_XXX、ABI、EABI、board support、serial等)

    

    第一大类:General setup是一些通用设置,比如编译器前缀设置、版本设置(选auto即可)、页交换swap支持、IPC、POSIX、BSD、IRQ、SLAB(内存管理机制)等。可以只修改编译器前缀,其他的采用默认配置即可。

 

 第二大类:Enable loadable module support    这是支持可加载模块,必须的,不然后续驱动都从源码添加再编译太麻烦。

 第三大类:Enabe the block layer   这是块设备的一些支持,使用硬盘/USB/SCSI设备者必选默认即可。

 

    2TB+  是支持2TB以上的大块设备支持,在嵌入式系统中一般不会用这么大,不选。

    SG v4 为块设备启用第四版SG(SCSI generic)支持.v4相比v3能够支持更复杂的SCSI指令(可变长度的命令描述块,双向数据传输,通用请求/应答协议),而且UDEV也要用它来获取设备的序列号.对于使用systemd的系统来说,必须选"Y".

    SG v4 lib  不需要手动开启此选项,如果有其他模块需要使用,会被自动开启。

  data integrity support 如果设备支持 T10/SCSI Data Integrity Field 或者 T13/ATA External Path Protection 特性,那么可以开启此选项,否则建议关闭.

  I/O调度默认即可。

第四大类:system Type  系统类型:选择CPU架构,开发板类型以及与board相关的配置选项。

 

在ARM system type中选择架构

第五大类:Bus support  PC卡的支持,不用就不选。

第六大类:Kernel feature 设置内核的一些特性,如EABI、ARM ABI(EABI取决于编译器,否则启动内核会发生指令异常  exitcode = 4)以及Linux的内存特性机制,嵌入式中一般用不到那么复杂的内存管理机制。

    其中的EXPERIMENTAL是实验阶段,可选可不选。

    menory的3G/1G是说:内核空间1G,用户空间3G。对ARM来说是2G/2G。

    high menory是物理内存<896M时,没有high memory,因为所有的内存都被kernel直接映射了。

    详情见:https://blog.csdn.net/acs713/article/details/8575235

    KSM是一种共享内存机制,嵌入式中一般不用。

第七大类:Boot options  内核启动参数的设置,一般默认即可,再说了,uboot不都设置了启动参数了嘛。

    可选:use bootlader 的参数。

第八大类:CPU Power mangement  CPU电源管理,默认即可。

    允许软件控制的空闲进程电源管理,arm linux一般不用选。

第九大类:Floating point emulation  浮点运算管理。

    若cpu支持硬浮点,选VFP浮点体系,否则选NWFPE。(这是浮点模拟器,当CPU不支持硬浮点处理时,必选NWFPE浮点模拟器,采用未定义指令异常来处理浮点)

第十大类:Userspace binary formats  内核支持什么样的可执行文件,一般选a.out elf

MISC是杂项设备的意思,不管。

第十一大类:Power management options 电源管理配置

    第一个是说系统休眠后仅保持内存供电,系统启动时直接从内存开始运行。

    最后一个是高级电源管理,一般都用不到。

第十二大类:Networking support 对arm-linux来说这是必须的吧

    Networking options是unix socket 和TCP/IP的一些设置,另外wireless比较重要,其他的看是否需要。

第十三大类:Device Derivers 设备驱动配置,对应各种字符设备、块设备和网卡设备、杂项设备。以及各种总线接口支持,如IIC、SPI、并口、STAT、SISC等。

第十四大类:File systems  选择Linux支持的各种文件系统

    其中里面有JFFS2 YAFFS2的选项。

第十五大类:kernel hacking 调试内核时使用,不用管。

第十六大类:Security options  安全选项,默认即可。

第十七大类:Cryptographic API  加密处理的API

第十八大类:Library routines  库:包含CRC32校验函数,zlib压缩函数等。不包含在内核源码中的第三方内核模块可能需要这些库,可以全不选,内核中若有其他部分依赖会自动选上。

第十九大类:Load an Alternate Configuration File  就是在make menuconfig时打开的配置文件

           Save an Alternate Configuration File  退出mank menuconfig时将配置保存到某个文件中。

    以上展示的二次配置是s3c2410_defconfig的配置,在这个过程中可以动态配置不要的东西。

    现在的uImage:

    2.5M左右

    

d.在二次配置后若大小还不满足要求,继续裁剪内核。

主要考虑裁剪:支持的多余单板、支持多以的文件系统、支持不必要的输入设备接口、支持不必要的驱动接口

配合.config文件     可以直接make menuconfig了:

(1)先去掉不必要的单板吧,在system type中:

原本支持了很多不必要的单板,修改为以下:

(2)再去掉不必要的文件系统,在File system中:

去掉了ext2/3,Reiserfs,CD-ROMfs,tmpfs,ROMfs。

保留了ext4,JFFS2,YAFFS2,DOS,Networking fs等。

(3)输入设备无非就是鼠标键盘,不要就去了呗,还有他们的接口,PS/2已经不用了!输出设备,主要是触摸屏,不用触摸屏就去了。

在驱动中:

(4)还有一些SISC、IDE/ATA 、STAT等硬盘接口,这些接口一般是通用计算机用的。去掉吧,还是在驱动中:SISC保留吧!

(5)声卡和显卡,以太网驱动等,没有就不要,还在驱动中。

剪裁完成,OK!来make  uImage:(若最后大小还是大了就只有修改uboot、内核mtd分区)

尴尬!2.08M,再挤挤吧,哈哈哈

在.config中看看有什么可以去掉:

   //PM就是电源管理

在menuconfig中/搜索:

这是其他选择后被动选择的,以下两个也不管了。

在menuconfig中/搜索:

最后:

这是串口驱动,暂时保留吧!

声卡,没用!去掉:

这个和上面相连,不用管了。

加密算法,暂时保留吧!

再编译试试:

运气!!!小于2048!!!

测试:

启动错误:

Q1:

设置 rootfstype = yaffs2

Q2:uncorrectable error :     //连续输出

    可能是nand 中保存的数据出错,重新erase   nand,再write

Q3:

https://blog.csdn.net/Blazar/article/details/79247464   //对应解决

http://blog.chinaunix.net/uid-27159438-id-3280213.html  //同类解决

最终结果:uboot2012 + Linux 3.4 + yaffs2    都是自己移植的

uboot2012 + Linux-3.4.2 + rootfs.yaffs2(均为自己移植)

效果:

前面的bug:

没有 + x:

那就chmod 755 rcS加个可行性权限,再重启测试:

使uImage支持虚拟文件系统

如上解决后make uImage,再重启测试:

OK!

13.ECC

14.总结

    对jffs2的支持一贯比较好,但是在使用yaffs2的文件系统时,会遇到很多的问题!

    第一,uboot中  nand   write.yaffs 命令的支持问题:nand_write_skip_bad函数

    第二,Linux中,添加yaffs2后,yaffs中的bug:yaffs_getxattr选择问题!

附:以下调试的命令

tftp 30000000 uImage;bootm 30000000

tftp 30000000 uImage;nand erase.part kernel;nand write 30000000 kernel  

tftp 30000000 uImage_4.3;nand erase.part kernel;nand write 30000000 kernel

tftp 30000000 uImage_new;nand erase.part kernel;nand write 30000000 kernel

tftp 30000000 fs_mini_mdev_new.jffs2;nand erase.part fs;nand write.jffs2 30000000 260000 $filesize

tftp 30000000 fs_mini.jffs2;nand erase.part fs;nand write.jffs2 30000000 260000 $filesize

tftp 30000000 fs_mini_mdev_new.yaffs2;nand erase.part fs;nand write.yaffs 30000000 260000 $filesize

tftp 30000000 fs_mini.yaffs2;nand erase.part fs;nand write.yaffs 30000000 260000 $filesize

tftp 30000000 u-boot_new;nand erase.part u-boot;nand write 30000000 u-boot

tftp 30000000 u-boot.bin;protect off all;erase 0 3FFFF;cp.b 30000000 0 40000

set bootargs console=ttySAC0,115200 root=/dev/mtdblock3

猜你喜欢

转载自blog.csdn.net/Carl_0/article/details/87856439